From dcf26e1302063bbdb7a5d20be0ec8fbc6241dff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 11 Nov 2024 10:10:29 +0100 Subject: [PATCH 1/3] SPE-2501: Add an option to specify which extruder controls the bed temperature. --- src/libslic3r/GCode.cpp | 37 ++++++++++++++++++++++++++++++----- src/libslic3r/GCode.hpp | 1 + src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 20 +++++++++++++++++++ src/libslic3r/PrintConfig.hpp | 1 + src/slic3r/GUI/Tab.cpp | 1 + 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6c27390ea4..3906a05304 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1215,7 +1215,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Enable ooze prevention if configured so. DoExport::init_ooze_prevention(print, m_ooze_prevention); - std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); + const std::string start_gcode = this->_process_start_gcode(print, initial_extruder_id); this->_print_first_layer_chamber_temperature(file, print, start_gcode, config().chamber_temperature.get_at(initial_extruder_id), false, false); this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -1888,15 +1888,34 @@ void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, const Print } } +std::string GCodeGenerator::_process_start_gcode(const Print& print, unsigned int current_extruder_id) +{ + const int num_extruders = print.config().nozzle_diameter.values.size(); + const int bed_temperature_extruder = print.config().bed_temperature_extruder; + if (0 < bed_temperature_extruder && bed_temperature_extruder <= num_extruders) { + const int first_layer_bed_temperature = print.config().first_layer_bed_temperature.get_at(bed_temperature_extruder - 1); + DynamicConfig config; + config.set_key_value("first_layer_bed_temperature", new ConfigOptionInts(num_extruders, first_layer_bed_temperature)); + return this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, current_extruder_id, &config); + } else { + return this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, current_extruder_id); + } +} + // Write 1st layer bed temperatures into the G-code. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature // M190 - Set Extruder Temperature and Wait void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { - bool autoemit = print.config().autoemit_temperature_commands; - // Initial bed temperature based on the first extruder. - int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); + const bool autoemit = print.config().autoemit_temperature_commands; + const int num_extruders = print.config().nozzle_diameter.values.size(); + const int bed_temperature_extruder = print.config().bed_temperature_extruder; + const bool use_first_printing_extruder = bed_temperature_extruder <= 0 || bed_temperature_extruder > num_extruders; + + // Initial bed temperature based on the first printing extruder or based on the extruded in bed_temperature_extruder. + int temp = print.config().first_layer_bed_temperature.get_at(use_first_printing_extruder ? first_printing_extruder_id : bed_temperature_extruder - 1); + // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, false, temp_by_gcode); @@ -2646,7 +2665,15 @@ LayerResult GCodeGenerator::process_layer( if (temperature > 0 && (temperature != print.config().first_layer_temperature.get_at(extruder.id()))) gcode += m_writer.set_temperature(temperature, false, extruder.id()); } - gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id)); + + // Bed temperature for layers from the 2nd layer is based on the first printing + // extruder on the layer or on the extruded in bed_temperature_extruder. + const int num_extruders = print.config().nozzle_diameter.values.size(); + const int bed_temperature_extruder = print.config().bed_temperature_extruder; + const bool use_first_extruder = bed_temperature_extruder <= 0 || bed_temperature_extruder > num_extruders; + const int bed_temperature = print.config().bed_temperature.get_at(use_first_extruder ? first_extruder_id : bed_temperature_extruder - 1); + gcode += m_writer.set_bed_temperature(bed_temperature); + // Mark the temperature transition from 1st to 2nd layer to be finished. m_second_layer_things_done = true; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index eabbdc41b9..6b35337e76 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -494,6 +494,7 @@ private: std::string _extrude(const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, std::string_view description, double speed, const EmitModifiers &emit_modifiers = EmitModifiers()); void print_machine_envelope(GCodeOutputStream &file, const Print &print); + std::string _process_start_gcode(const Print &print, unsigned int current_extruder_id); void _print_first_layer_chamber_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, int temp, bool wait, bool accurate); void _print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 5ff7364e5c..8e226e2239 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -511,6 +511,7 @@ static std::vector s_Preset_print_options { "wall_distribution_count", "min_feature_size", "min_bead_width", "top_one_perimeter_type", "only_one_perimeter_first_layer", "automatic_extrusion_widths", "automatic_infill_combination", "automatic_infill_combination_max_layer_height", + "bed_temperature_extruder" }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1bce82c2fd..c35d6ae652 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -618,6 +618,16 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionInts{ 0 }); + def = this->add("bed_temperature_extruder", coInt); + def->label = L("Bed temperature by extruder"); + def->category = L("Extruders"); + def->tooltip = L("The extruder which determines bed temperatures. " + "Set to 0 to determine temperatures based on the first printing extruder " + "of the first and the second layers."); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionInt(0)); + def = this->add("before_layer_gcode", coString); def->label = L("Before layer change G-code"); def->tooltip = L("This custom code is inserted at every layer change, right before the Z move. " @@ -5162,6 +5172,16 @@ void DynamicPrintConfig::normalize_fdm() if (!this->has("solid_infill_extruder") && this->has("infill_extruder")) this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt()); + if (this->has("bed_temperature_extruder")) { + const size_t num_extruders = this->opt("nozzle_diameter")->size(); + const int extruder = this->opt("bed_temperature_extruder")->value; + + // Replace invalid values with 0. + if (extruder < 0 || extruder > num_extruders) { + this->option("bed_temperature_extruder")->setInt(0); + } + } + if (this->has("spiral_vase") && this->opt("spiral_vase", true)->value) { { // this should be actually done only on the spiral layers instead of all diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5f21c786c6..f860caf4c8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -798,6 +798,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, arc_fitting)) ((ConfigOptionBool, autoemit_temperature_commands)) + ((ConfigOptionInt, bed_temperature_extruder)) ((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, between_objects_gcode)) ((ConfigOptionBool, binary_gcode)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 623b4853c0..ba75abb3b1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1635,6 +1635,7 @@ void TabPrint::build() optgroup->append_single_option_line("support_material_extruder"); optgroup->append_single_option_line("support_material_interface_extruder"); optgroup->append_single_option_line("wipe_tower_extruder"); + optgroup->append_single_option_line("bed_temperature_extruder"); optgroup = page->new_optgroup(L("Ooze prevention")); optgroup->append_single_option_line("ooze_prevention"); From d7a0dfd8e4e6dfc1230b02495ea6398722fc257f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 11 Nov 2024 10:12:54 +0100 Subject: [PATCH 2/3] SPE-2501: Improve the notification about the bed temperature difference. --- src/libslic3r/Config.hpp | 1 + src/libslic3r/Print.cpp | 19 ++++++---- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintApply.cpp | 21 ++++++++++- src/libslic3r/PrintBase.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 2 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 4 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 41 ++++++++++++--------- 10 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index cc7eabe336..93b90ead57 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -974,6 +974,7 @@ public: // A scalar is nil, or all values of a vector are nil. bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } bool is_nil(size_t idx) const override { return values[idx < this->values.size() ? idx : 0] == nil_value(); } + std::vector getInts() const override { return this->values; } std::string serialize() const override { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 529fd604a8..ba6dbef7b6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -443,14 +443,19 @@ std::string Print::validate(std::vector* warnings) const std::vector extruders = this->extruders(); if (warnings) { - for (size_t a=0; a 15 - || std::abs(m_config.first_layer_bed_temperature.get_at(extruders[a]) - m_config.first_layer_bed_temperature.get_at(extruders[b])) > 15) { - warnings->emplace_back("_BED_TEMPS_DIFFER"); - goto DONE; + if (m_config.bed_temperature_extruder == 0) { + for (size_t a = 0; a < extruders.size(); ++a) { + for (size_t b = a + 1; b < extruders.size(); ++b) { + if (std::abs(m_config.bed_temperature.get_at(extruders[a]) - m_config.bed_temperature.get_at(extruders[b])) > 15 + || std::abs(m_config.first_layer_bed_temperature.get_at(extruders[a]) - m_config.first_layer_bed_temperature.get_at(extruders[b])) > 15) { + warnings->emplace_back("_BED_TEMPS_DIFFER"); + goto DONE; + } } - DONE:; + } + + DONE:; + } if (!this->has_same_shrinkage_compensations()) warnings->emplace_back("_FILAMENT_SHRINKAGE_DIFFER"); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e6728cf69e..1ea05498a1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -608,7 +608,7 @@ public: // List of existing PrintObject IDs, to remove notifications for non-existent IDs. std::vector print_object_ids() const override; - ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; + ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector *warnings = nullptr) override; void set_task(const TaskParams ¶ms) override { PrintBaseWithState::set_task_impl(params, m_objects); } void process() override; void finalize() override { PrintBaseWithState::finalize_impl(m_objects); } diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index eac7d8df5d..74c1056def 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1050,7 +1050,23 @@ static PrintObjectRegions* generate_print_object_regions( return out.release(); } -Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config) +static void validate_print_config_change(const PrintConfig &old_config, const DynamicPrintConfig &new_config, std::vector *warnings) +{ + if (warnings == nullptr) { + return; + } + + if (old_config.bed_temperature_extruder > 0 && old_config.bed_temperature_extruder == new_config.option("bed_temperature_extruder")->getInt()) { + // Bed temperature extruder is set, and it didn't change with the new config. + if (old_config.bed_temperature.values != new_config.option("bed_temperature")->getInts() + || old_config.first_layer_bed_temperature.values != new_config.option("first_layer_bed_temperature")->getInts()) { + // When any bed temperature changes, we warn the user that the bed temperature extruder may need to be changed. + warnings->emplace_back("_BED_TEMPS_CHANGED"); + } + } +} + +Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config, std::vector *warnings) { #ifdef _DEBUG check_model_ids_validity(model); @@ -1076,6 +1092,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); t_config_option_keys region_diff = m_default_region_config.diff(new_full_config); + // Check if the print config change will produce any warnings. + validate_print_config_change(m_config, new_full_config, warnings); + // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; auto update_apply_status = [&apply_status](bool invalidated) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 2c113f1ee2..e8832c00e4 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -433,7 +433,7 @@ public: // Some data was changed, which in turn invalidated already calculated steps. APPLY_STATUS_INVALIDATED, }; - virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config) = 0; + virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector *warnings = nullptr) = 0; const Model& model() const { return m_model; } struct TaskParams { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 9182cb7932..954aed7a2a 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -260,7 +260,7 @@ static t_config_option_keys print_config_diffs(const StaticPrintConfig &curr } -SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config) +SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config, std::vector *warnings) { #ifdef _DEBUG check_model_ids_validity(model); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index d02b4af725..abb76b6b7c 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -490,7 +490,7 @@ public: bool empty() const override { return m_objects.empty(); } // List of existing PrintObject IDs, to remove notifications for non-existent IDs. std::vector print_object_ids() const override; - ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; + ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector *warnings = nullptr) override; void set_task(const TaskParams ¶ms) override { PrintBaseWithState::set_task_impl(params, m_objects); } void process() override; void finalize() override { PrintBaseWithState::finalize_impl(m_objects); } diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 0388ea2e99..cf39de01a8 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -590,11 +590,11 @@ std::string BackgroundSlicingProcess::validate(std::vector* warning // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. -Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config) +Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const DynamicPrintConfig &config, std::vector *warnings) { assert(m_print != nullptr); assert(config.opt_enum("printer_technology") == m_print->technology()); - Print::ApplyStatus invalidated = m_print->apply(model, config); + Print::ApplyStatus invalidated = m_print->apply(model, config, warnings); if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && !m_fff_print->is_step_done(psGCodeExport)) { // Some FFF status was invalidated, and the G-code was not exported yet. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index d11d1d1c33..22779611a1 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -133,7 +133,7 @@ public: // Apply config over the print. Returns false, if the new config values caused any of the already // processed steps to be invalidated, therefore the task will need to be restarted. - PrintBase::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config); + PrintBase::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config, std::vector *warnings = nullptr); // After calling the apply() function, set_task() may be called to limit the task to be processed by process(). // This is useful for calculating SLA supports for a single object only. void set_task(const PrintBase::TaskParams ¶ms); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5bf75209bc..b40b8dd47c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -968,7 +968,7 @@ void Plater::priv::init() this->notification_manager->close_notification_of_type(NotificationType::UserAccountID); // show connect tab this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text); - + this->main_frame->on_account_login(user_account->get_access_token()); } else { // refresh do different operations than on_account_login @@ -1060,11 +1060,11 @@ void Plater::priv::init() this->q->Bind(EVT_UA_REFRESH_TIME, [this](UserAccountTimeEvent& evt) { this->user_account->set_refresh_time(evt.data); - }); + }); this->q->Bind(EVT_UA_ENQUEUED_REFRESH, [this](SimpleEvent& evt) { this->main_frame->on_account_will_refresh(); - }); - + }); + this->q->Bind(EVT_PRINTABLES_CONNECT_PRINT, [this](wxCommandEvent& evt) { if (!this->user_account->is_logged()) { // show login dialog instead of print dialog @@ -1287,7 +1287,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ #ifdef __linux__ // On Linux Constructor of the ProgressDialog calls DisableOtherWindows() function which causes a disabling of all children of the find_toplevel_parent(q) // And a destructor of the ProgressDialog calls ReenableOtherWindows() function which revert previously disabled children. - // But if printer technology will be changes during project loading, + // But if printer technology will be changes during project loading, // then related SLA Print and Materials Settings or FFF Print and Filaments Settings will be unparent from the wxNoteBook // and that is why they will never be enabled after destruction of the ProgressDialog. // So, distroy progress_gialog if we are loading project file @@ -1365,8 +1365,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ // For exporting from the 3mf we shouldn't check printer_presets for the containing information about "Print Host upload" wxGetApp().load_current_presets(false); - // Update filament colors for the MM-printer profile in the full config - // to avoid black (default) colors for Extruders in the ObjectList, + // Update filament colors for the MM-printer profile in the full config + // to avoid black (default) colors for Extruders in the ObjectList, // when for extruder colors are used filament colors q->update_filament_colors_in_full_config(); is_project_file = true; @@ -2131,9 +2131,16 @@ void Plater::priv::process_validation_warning(const std::vector& wa print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto")); return true; }; - } else if (text == "_BED_TEMPS_DIFFER") { - text = _u8L("Bed temperatures for the used filaments differ significantly."); + } else if (text == "_BED_TEMPS_DIFFER" || text == "_BED_TEMPS_CHANGED") { + text = _u8L("Bed temperatures for the used filaments differ significantly.\n" + "For multi-material prints it is recommended to set the "); + hypertext = _u8L("'Bed temperature by extruder' and 'Wipe tower extruder'"); + multiline = true; notification_type = NotificationType::BedTemperaturesDiffer; + action_fn = [](wxEvtHandler*) { + GUI::wxGetApp().jump_to_option("bed_temperature_extruder", Preset::Type::TYPE_PRINT, boost::nowide::widen("Multiple Extruders")); + return true; + }; } else if (text == "_FILAMENT_SHRINKAGE_DIFFER") { text = _u8L("Filament shrinkage will not be used because filament shrinkage " "for the used filaments differs significantly."); @@ -2290,15 +2297,16 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool std::vector(1) }; - // Apply new config to the possibly running background task. + std::vector warnings; + // Apply new config to the possibly running background task and give the user feedback on warnings. if (printer_technology == ptFFF) { with_single_bed_model_fff(q->model(), s_multiple_beds.get_active_bed(), [&](){ - invalidated = background_process.apply(q->model(), full_config); + invalidated = background_process.apply(q->model(), full_config, &warnings); apply_statuses[s_multiple_beds.get_active_bed()] = invalidated; }); } else if (printer_technology == ptSLA) { with_single_bed_model_sla(q->model(), s_multiple_beds.get_active_bed(), [&](){ - invalidated = background_process.apply(q->model(), full_config); + invalidated = background_process.apply(q->model(), full_config, &warnings); apply_statuses[0] = invalidated; }); } else { @@ -2378,7 +2386,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // The delayed error message is no more valid. delayed_error_message.clear(); // The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors. - std::vector warnings; std::string err = background_process.validate(&warnings); if (err.empty()) { notification_manager->set_all_slicing_errors_gray(true); @@ -3170,16 +3177,16 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) std::vector object_ids = { evt.status.warning_object_id }; std::vector warning_steps = { evt.status.warning_step }; std::vector flagss = { int(evt.status.flags) }; - + if (warning_steps.front() == -1) { flagss = { PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS, PrintBase::SlicingStatus::UPDATE_PRINT_OBJECT_STEP_WARNINGS }; notification_manager->close_slicing_errors_and_warnings(); } - + for (int flags : flagss ) { if (warning_steps.front() == -1) { warning_steps.clear(); - if (flags == PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) { + if (flags == PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) { int i = 0; while (i < int(printer_technology == ptFFF ? psCount : slapsCount)) { warning_steps.push_back(i); ++i; } } else { @@ -5242,7 +5249,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/* return true; } else if (boost::algorithm::iends_with(filename, ".zip")) { if (!load_just_one_file) { - WarningDialog dlg(static_cast(this), + WarningDialog dlg(static_cast(this), format_wxstr(_L("You have several files for loading and \"%1%\" is one of them.\n" "Please note that only one .zip file can be loaded at a time.\n" "In this case we can load just \"%1%\".\n\n" From 6b28de6634688dde6550a51ba688c5a9773fae58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 12 Feb 2025 10:41:41 +0100 Subject: [PATCH 3/3] SPE-2501: Add support for blinking arrow for more items on the same tab. --- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 9 +++-- src/slic3r/GUI/Tab.hpp | 2 +- src/slic3r/GUI/wxExtensions.cpp | 61 ++++++++++++++++++++++++--------- src/slic3r/GUI/wxExtensions.hpp | 19 ++++++++-- 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b40b8dd47c..0d16e1ea6d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2138,7 +2138,7 @@ void Plater::priv::process_validation_warning(const std::vector& wa multiline = true; notification_type = NotificationType::BedTemperaturesDiffer; action_fn = [](wxEvtHandler*) { - GUI::wxGetApp().jump_to_option("bed_temperature_extruder", Preset::Type::TYPE_PRINT, boost::nowide::widen("Multiple Extruders")); + GUI::wxGetApp().get_tab(Preset::Type::TYPE_PRINT)->activate_option("bed_temperature_extruder", boost::nowide::widen("Multiple Extruders"), { "wipe_tower_extruder" }); return true; }; } else if (text == "_FILAMENT_SHRINKAGE_DIFFER") { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ba75abb3b1..f8f3751759 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1149,7 +1149,7 @@ void Tab::update_wiping_button_visibility() { } } -void Tab::activate_option(const std::string& opt_key, const wxString& category) +void Tab::activate_option(const std::string &opt_key, const wxString &category, const std::vector &another_blinking_opt_keys) { wxString page_title = translate_category(category, m_type); @@ -1197,7 +1197,12 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category) set_focus(field->getWindow()); } - m_highlighter.init(get_custom_ctrl_with_blinking_ptr(opt_key)); + std::vector> custom_blinking_ctrls = { get_custom_ctrl_with_blinking_ptr(opt_key) }; + for (const std::string &another_blinking_opt_key : another_blinking_opt_keys) { + custom_blinking_ctrls.emplace_back(get_custom_ctrl_with_blinking_ptr(another_blinking_opt_key)); + } + + m_highlighter.init(custom_blinking_ctrls); } void Tab::cache_config_diff(const std::vector& selected_options, const DynamicPrintConfig* config/* = nullptr*/) diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 71c80d7ad6..1d0dd6b414 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -398,7 +398,7 @@ public: void on_value_change(const std::string& opt_key, const boost::any& value); void update_wiping_button_visibility(); - void activate_option(const std::string& opt_key, const wxString& category); + void activate_option(const std::string &opt_key, const wxString &category, const std::vector &another_blinking_opt_keys = {}); void cache_config_diff(const std::vector& selected_options, const DynamicPrintConfig* config = nullptr); void apply_config_from_cache(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index aa29db9ae2..602bf4c601 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -731,11 +731,33 @@ void HighlighterForWx::init(std::pair params) if (!Highlighter::init(!params.first && !params.second)) return; - m_custom_ctrl = params.first; - m_show_blink_ptr = params.second; + assert(m_blinking_custom_ctrls.empty()); + m_blinking_custom_ctrls.push_back({params.first, params.second}); - *m_show_blink_ptr = true; - m_custom_ctrl->Refresh(); + BlinkingCustomCtrl &blinking_custom_ctrl = m_blinking_custom_ctrls.back(); + *blinking_custom_ctrl.show_blink_ptr = true; + blinking_custom_ctrl.custom_ctrl_ptr->Refresh(); +} + +void HighlighterForWx::init(const std::vector> &blinking_custom_ctrls_params) +{ + this->invalidate(); + + const bool input_failed = blinking_custom_ctrls_params.empty() || + std::any_of(blinking_custom_ctrls_params.cbegin(), blinking_custom_ctrls_params.cend(), + [](auto ¶ms) { return params.first == nullptr || params.second == nullptr; }); + + if (!Highlighter::init(input_failed)) + return; + + assert(m_blinking_custom_ctrls.empty()); + for (const std::pair &blinking_custom_ctrl_params : blinking_custom_ctrls_params) { + m_blinking_custom_ctrls.push_back({blinking_custom_ctrl_params.first, blinking_custom_ctrl_params.second}); + + BlinkingCustomCtrl &blinking_custom_ctrl = m_blinking_custom_ctrls.back(); + *blinking_custom_ctrl.show_blink_ptr = true; + blinking_custom_ctrl.custom_ctrl_ptr->Refresh(); + } } // - using a BlinkingBitmap. Change state of this bitmap @@ -753,13 +775,15 @@ void HighlighterForWx::invalidate() { Highlighter::invalidate(); - if (m_custom_ctrl && m_show_blink_ptr) { - *m_show_blink_ptr = false; - m_custom_ctrl->Refresh(); - m_show_blink_ptr = nullptr; - m_custom_ctrl = nullptr; - } - else if (m_blinking_bitmap) { + if (!m_blinking_custom_ctrls.empty()) { + for (BlinkingCustomCtrl &blinking_custom_ctrl : m_blinking_custom_ctrls) { + assert(blinking_custom_ctrl.is_valid()); + *blinking_custom_ctrl.show_blink_ptr = false; + blinking_custom_ctrl.custom_ctrl_ptr->Refresh(); + } + + m_blinking_custom_ctrls.clear(); + } else if (m_blinking_bitmap) { m_blinking_bitmap->invalidate(); m_blinking_bitmap = nullptr; } @@ -767,14 +791,17 @@ void HighlighterForWx::invalidate() void HighlighterForWx::blink() { - if (m_custom_ctrl && m_show_blink_ptr) { - *m_show_blink_ptr = !*m_show_blink_ptr; - m_custom_ctrl->Refresh(); - } - else if (m_blinking_bitmap) + if (!m_blinking_custom_ctrls.empty()) { + for (BlinkingCustomCtrl &blinking_custom_ctrl : m_blinking_custom_ctrls) { + assert(blinking_custom_ctrl.is_valid()); + *blinking_custom_ctrl.show_blink_ptr = !*blinking_custom_ctrl.show_blink_ptr; + blinking_custom_ctrl.custom_ctrl_ptr->Refresh(); + } + } else if (m_blinking_bitmap) { m_blinking_bitmap->blink(); - else + } else { return; + } Highlighter::blink(); } diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index fc96f48cbd..9ab9ad8855 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -283,12 +283,24 @@ public: class HighlighterForWx : public Highlighter { + struct BlinkingCustomCtrl + { + OG_CustomCtrl *custom_ctrl_ptr; + bool *show_blink_ptr; + + bool is_valid() const + { + return custom_ctrl_ptr != nullptr && show_blink_ptr != nullptr; + } + }; + + using BlinkingCustomCtrls = std::vector; + // There are 2 possible cases to use HighlighterForWx: // - using a BlinkingBitmap. Change state of this bitmap - BlinkingBitmap* m_blinking_bitmap { nullptr }; + BlinkingBitmap *m_blinking_bitmap { nullptr }; // - using OG_CustomCtrl where arrow will be rendered and flag indicated "show/hide" state of this arrow - OG_CustomCtrl* m_custom_ctrl { nullptr }; - bool* m_show_blink_ptr { nullptr }; + BlinkingCustomCtrls m_blinking_custom_ctrls; public: HighlighterForWx() {} @@ -297,6 +309,7 @@ public: void bind_timer(wxWindow* owner) override; void init(BlinkingBitmap* blinking_bitmap); void init(std::pair); + void init(const std::vector> &blinking_custom_ctrls_params); void blink(); void invalidate(); };