mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 19:33:47 +00:00
Autoslice + SliceDelay (#11407)
* Add auto slice after changes option Introduces a new 'Auto slice after changes' setting in Preferences, allowing OrcaSlicer to automatically re-slice when slicing-related settings change. Implements logic in Plater to schedule auto-reslicing if the option is enabled and conditions are met, and sets the default to false in AppConfig. * Improve auto-reslice scheduling after slice cancellation Adds a flag to track when auto-reslice should be triggered after a slicing process is cancelled. Ensures that auto-reslice is scheduled once the current slicing process completes and is not lost if a slice is cancelled mid-operation. * Add configurable delay for auto slice after change Introduces a new 'auto_slice_change_delay_seconds' setting to control the delay before auto slicing starts after a change. Updates AppConfig to set a default value, adds a timer and logic in Plater to handle the delay, and exposes the setting in Preferences. This allows users to group multiple edits before triggering auto slicing. * Combine auto-reslice checkbox and delay input in preferences Replaces separate auto-reslice and delay settings with a unified UI element in Preferences. The new function create_item_auto_reslice adds both the checkbox and delay input in a single row, improving usability and code organization. * Move auto reslice option into Control > Behaviour * Remove 'loop' icon from AutoSlice * Default disable and 1 Sec
This commit is contained in:
@@ -111,6 +111,11 @@ void AppConfig::set_defaults()
|
|||||||
if (get("background_processing").empty())
|
if (get("background_processing").empty())
|
||||||
set_bool("background_processing", false);
|
set_bool("background_processing", false);
|
||||||
#endif
|
#endif
|
||||||
|
if (get("auto_slice_after_change").empty())
|
||||||
|
set_bool("auto_slice_after_change", false);
|
||||||
|
|
||||||
|
if (get("auto_slice_change_delay_seconds").empty())
|
||||||
|
set("auto_slice_change_delay_seconds", "1");
|
||||||
|
|
||||||
if (get("drop_project_action").empty())
|
if (get("drop_project_action").empty())
|
||||||
set_bool("drop_project_action", true);
|
set_bool("drop_project_action", true);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@@ -4104,6 +4105,8 @@ struct Plater::priv
|
|||||||
bool m_ignore_event{false};
|
bool m_ignore_event{false};
|
||||||
bool m_slice_all{false};
|
bool m_slice_all{false};
|
||||||
bool m_is_slicing {false};
|
bool m_is_slicing {false};
|
||||||
|
bool auto_reslice_pending {false};
|
||||||
|
bool auto_reslice_after_cancel {false};
|
||||||
bool m_is_publishing {false};
|
bool m_is_publishing {false};
|
||||||
int m_is_RightClickInLeftUI{-1};
|
int m_is_RightClickInLeftUI{-1};
|
||||||
int m_cur_slice_plate;
|
int m_cur_slice_plate;
|
||||||
@@ -4148,6 +4151,7 @@ struct Plater::priv
|
|||||||
std::string delayed_error_message;
|
std::string delayed_error_message;
|
||||||
|
|
||||||
wxTimer background_process_timer;
|
wxTimer background_process_timer;
|
||||||
|
wxTimer auto_reslice_timer;
|
||||||
|
|
||||||
std::string label_btn_export;
|
std::string label_btn_export;
|
||||||
std::string label_btn_send;
|
std::string label_btn_send;
|
||||||
@@ -4357,6 +4361,9 @@ struct Plater::priv
|
|||||||
std::vector<std::vector<DynamicPrintConfig>> get_extruder_filament_info();
|
std::vector<std::vector<DynamicPrintConfig>> get_extruder_filament_info();
|
||||||
void update_print_volume_state();
|
void update_print_volume_state();
|
||||||
void schedule_background_process();
|
void schedule_background_process();
|
||||||
|
void schedule_auto_reslice_if_needed();
|
||||||
|
void trigger_auto_reslice_now();
|
||||||
|
int auto_slice_delay_seconds() const;
|
||||||
// Update background processing thread from the current config and Model.
|
// Update background processing thread from the current config and Model.
|
||||||
enum UpdateBackgroundProcessReturnState {
|
enum UpdateBackgroundProcessReturnState {
|
||||||
// update_background_process() reports, that the Print / SLAPrint was updated in a way,
|
// update_background_process() reports, that the Print / SLAPrint was updated in a way,
|
||||||
@@ -4742,10 +4749,18 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||||||
panels.push_back(assemble_view);
|
panels.push_back(assemble_view);
|
||||||
|
|
||||||
this->background_process_timer.SetOwner(this->q, 0);
|
this->background_process_timer.SetOwner(this->q, 0);
|
||||||
|
this->auto_reslice_timer.SetOwner(this->q, 0);
|
||||||
this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt)
|
this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt)
|
||||||
{
|
{
|
||||||
if (!this->suppressed_backround_processing_update)
|
if (&evt.GetTimer() == &this->background_process_timer) {
|
||||||
this->update_restart_background_process(false, false);
|
if (!this->suppressed_backround_processing_update)
|
||||||
|
this->update_restart_background_process(false, false);
|
||||||
|
} else if (&evt.GetTimer() == &this->auto_reslice_timer) {
|
||||||
|
this->auto_reslice_timer.Stop();
|
||||||
|
this->trigger_auto_reslice_now();
|
||||||
|
} else {
|
||||||
|
evt.Skip();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
update();
|
update();
|
||||||
@@ -7285,6 +7300,90 @@ void Plater::priv::schedule_background_process()
|
|||||||
this->view3D->get_canvas3d()->set_config(this->config);
|
this->view3D->get_canvas3d()->set_config(this->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Plater::priv::schedule_auto_reslice_if_needed()
|
||||||
|
{
|
||||||
|
AppConfig* cfg = wxGetApp().app_config;
|
||||||
|
if (cfg == nullptr || !cfg->get_bool("auto_slice_after_change"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (model.objects.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
PartPlate* plate = partplate_list.get_curr_plate();
|
||||||
|
if (plate == nullptr || !plate->has_printable_instances())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (background_process.running() || m_is_slicing) {
|
||||||
|
// Remember to restart once the current slice stops and cancel it now.
|
||||||
|
auto_reslice_after_cancel = true;
|
||||||
|
background_process.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int delay_seconds = auto_slice_delay_seconds();
|
||||||
|
if (delay_seconds > 0) {
|
||||||
|
auto_reslice_pending = true;
|
||||||
|
auto_reslice_timer.Stop();
|
||||||
|
auto_reslice_timer.Start(delay_seconds * 1000, wxTIMER_ONE_SHOT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto_reslice_pending)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto_reslice_pending = true;
|
||||||
|
auto_reslice_timer.Stop();
|
||||||
|
wxGetApp().CallAfter([this]() { this->trigger_auto_reslice_now(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plater::priv::trigger_auto_reslice_now()
|
||||||
|
{
|
||||||
|
this->auto_reslice_pending = false;
|
||||||
|
|
||||||
|
AppConfig* cfg = wxGetApp().app_config;
|
||||||
|
if (cfg == nullptr || !cfg->get_bool("auto_slice_after_change"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this->model.objects.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this->background_process.running() || this->m_is_slicing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PartPlate* plate = this->partplate_list.get_curr_plate();
|
||||||
|
if (plate == nullptr || !plate->has_printable_instances())
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->q->reslice();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Plater::priv::auto_slice_delay_seconds() const
|
||||||
|
{
|
||||||
|
AppConfig* cfg = wxGetApp().app_config;
|
||||||
|
if (cfg == nullptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
std::string delay_str = cfg->get("auto_slice_change_delay_seconds");
|
||||||
|
if (delay_str.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
long delay_seconds = 0;
|
||||||
|
try {
|
||||||
|
delay_seconds = std::stol(delay_str);
|
||||||
|
} catch (...) {
|
||||||
|
delay_seconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delay_seconds < 0)
|
||||||
|
delay_seconds = 0;
|
||||||
|
|
||||||
|
const long max_seconds = std::numeric_limits<int>::max() / 1000;
|
||||||
|
if (delay_seconds > max_seconds)
|
||||||
|
delay_seconds = max_seconds;
|
||||||
|
|
||||||
|
return static_cast<int>(delay_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::vector<DynamicPrintConfig>> Plater::priv::get_extruder_filament_info()
|
std::vector<std::vector<DynamicPrintConfig>> Plater::priv::get_extruder_filament_info()
|
||||||
{
|
{
|
||||||
std::vector<std::vector<DynamicPrintConfig>> filament_infos;
|
std::vector<std::vector<DynamicPrintConfig>> filament_infos;
|
||||||
@@ -9446,6 +9545,11 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (auto_reslice_after_cancel) {
|
||||||
|
auto_reslice_after_cancel = false;
|
||||||
|
schedule_auto_reslice_if_needed();
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", exit.");
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", exit.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15942,6 +16046,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||||||
if (p->main_frame->is_loaded()) {
|
if (p->main_frame->is_loaded()) {
|
||||||
this->p->schedule_background_process();
|
this->p->schedule_background_process();
|
||||||
update_title_dirty_status();
|
update_title_dirty_status();
|
||||||
|
p->schedule_auto_reslice_if_needed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -677,6 +677,74 @@ wxBoxSizer *PreferencesDialog::create_item_backup(wxString title, wxString toolt
|
|||||||
return m_sizer_input;
|
return m_sizer_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxBoxSizer *PreferencesDialog::create_item_auto_reslice(wxString title, wxString checkbox_tooltip, wxString delay_tooltip)
|
||||||
|
{
|
||||||
|
wxBoxSizer *sizer_row = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
sizer_row->AddSpacer(FromDIP(DESIGN_LEFT_MARGIN));
|
||||||
|
|
||||||
|
auto checkbox_title = new wxStaticText(m_parent, wxID_ANY, title, wxDefaultPosition, DESIGN_TITLE_SIZE, wxST_NO_AUTORESIZE);
|
||||||
|
checkbox_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
|
||||||
|
checkbox_title->SetFont(::Label::Body_14);
|
||||||
|
checkbox_title->Wrap(DESIGN_TITLE_SIZE.x);
|
||||||
|
checkbox_title->SetToolTip(checkbox_tooltip);
|
||||||
|
|
||||||
|
auto checkbox = new ::CheckBox(m_parent);
|
||||||
|
checkbox->SetValue(app_config->get_bool("auto_slice_after_change"));
|
||||||
|
checkbox->SetToolTip(checkbox_tooltip);
|
||||||
|
|
||||||
|
wxString delay_value = app_config->get("auto_slice_change_delay_seconds");
|
||||||
|
if (delay_value.empty())
|
||||||
|
delay_value = "0";
|
||||||
|
|
||||||
|
auto input = new ::TextInput(m_parent, wxEmptyString, _L("sec"), wxEmptyString, wxDefaultPosition, wxSize(FromDIP(97), -1), wxTE_PROCESS_ENTER);
|
||||||
|
StateColor input_bg(std::pair<wxColour, int>(wxColour("#F0F0F1"), StateColor::Disabled), std::pair<wxColour, int>(*wxWHITE, StateColor::Enabled));
|
||||||
|
input->SetBackgroundColor(input_bg);
|
||||||
|
input->GetTextCtrl()->SetValue(delay_value);
|
||||||
|
wxTextValidator validator(wxFILTER_DIGITS);
|
||||||
|
input->SetToolTip(delay_tooltip);
|
||||||
|
input->GetTextCtrl()->SetValidator(validator);
|
||||||
|
|
||||||
|
sizer_row->Add(checkbox_title, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(3));
|
||||||
|
sizer_row->Add(checkbox, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(5));
|
||||||
|
sizer_row->Add(input, 0, wxALIGN_CENTER_VERTICAL);
|
||||||
|
|
||||||
|
auto commit_delay = [this, input]() {
|
||||||
|
wxString value = input->GetTextCtrl()->GetValue();
|
||||||
|
long seconds = 0;
|
||||||
|
if (!value.ToLong(&seconds) || seconds < 0)
|
||||||
|
seconds = 0;
|
||||||
|
wxString sanitized = wxString::Format("%ld", seconds);
|
||||||
|
input->GetTextCtrl()->SetValue(sanitized);
|
||||||
|
app_config->set("auto_slice_change_delay_seconds", std::string(sanitized.mb_str()));
|
||||||
|
app_config->save();
|
||||||
|
};
|
||||||
|
|
||||||
|
input->GetTextCtrl()->Bind(wxEVT_TEXT_ENTER, [commit_delay](wxCommandEvent &e) {
|
||||||
|
commit_delay();
|
||||||
|
e.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
input->GetTextCtrl()->Bind(wxEVT_KILL_FOCUS, [commit_delay](wxFocusEvent &e) {
|
||||||
|
commit_delay();
|
||||||
|
e.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, checkbox, input](wxCommandEvent &e) {
|
||||||
|
const bool enabled = checkbox->GetValue();
|
||||||
|
app_config->set_bool("auto_slice_after_change", enabled);
|
||||||
|
app_config->save();
|
||||||
|
input->Enable(enabled);
|
||||||
|
input->Refresh();
|
||||||
|
e.Skip();
|
||||||
|
});
|
||||||
|
|
||||||
|
input->Enable(checkbox->GetValue());
|
||||||
|
input->Refresh();
|
||||||
|
|
||||||
|
return sizer_row;
|
||||||
|
}
|
||||||
|
|
||||||
wxBoxSizer* PreferencesDialog::create_item_darkmode(wxString title,wxString tooltip, std::string param)
|
wxBoxSizer* PreferencesDialog::create_item_darkmode(wxString title,wxString tooltip, std::string param)
|
||||||
{
|
{
|
||||||
wxBoxSizer* m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL);
|
wxBoxSizer* m_sizer_checkbox = new wxBoxSizer(wxHORIZONTAL);
|
||||||
@@ -1256,6 +1324,12 @@ void PreferencesDialog::create_items()
|
|||||||
|
|
||||||
auto item_auto_arrange = create_item_checkbox(_L("Auto arrange plate after cloning"), "", "auto_arrange");
|
auto item_auto_arrange = create_item_checkbox(_L("Auto arrange plate after cloning"), "", "auto_arrange");
|
||||||
g_sizer->Add(item_auto_arrange);
|
g_sizer->Add(item_auto_arrange);
|
||||||
|
|
||||||
|
auto item_auto_reslice = create_item_auto_reslice(
|
||||||
|
_L("Auto slice after changes"),
|
||||||
|
_L("If enabled, OrcaSlicer will re-slice automatically whenever slicing-related settings change."),
|
||||||
|
_L("Delay in seconds before auto slicing starts, allowing multiple edits to be grouped. Use 0 to slice immediately."));
|
||||||
|
g_sizer->Add(item_auto_reslice);
|
||||||
|
|
||||||
//// CONTROL > Camera
|
//// CONTROL > Camera
|
||||||
g_sizer->Add(create_item_title(_L("Camera")), 1, wxEXPAND);
|
g_sizer->Add(create_item_title(_L("Camera")), 1, wxEXPAND);
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ public:
|
|||||||
wxBoxSizer *create_item_input(wxString title, wxString title2, wxString tooltip, std::string param, std::function<void(wxString)> onchange = {});
|
wxBoxSizer *create_item_input(wxString title, wxString title2, wxString tooltip, std::string param, std::function<void(wxString)> onchange = {});
|
||||||
wxBoxSizer *create_camera_orbit_mult_input(wxString title, wxString tooltip);
|
wxBoxSizer *create_camera_orbit_mult_input(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_backup(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_backup(wxString title, wxString tooltip);
|
||||||
|
wxBoxSizer *create_item_auto_reslice(wxString title, wxString checkbox_tooltip, wxString delay_tooltip);
|
||||||
wxBoxSizer *create_item_multiple_combobox(wxString title, wxString tooltip, std::string parama, std::vector<wxString> vlista, std::vector<wxString> vlistb);
|
wxBoxSizer *create_item_multiple_combobox(wxString title, wxString tooltip, std::string parama, std::vector<wxString> vlista, std::vector<wxString> vlistb);
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
wxBoxSizer *create_item_link_association(wxString url_prefix, wxString website_name);
|
wxBoxSizer *create_item_link_association(wxString url_prefix, wxString website_name);
|
||||||
|
|||||||
Reference in New Issue
Block a user