Merge branch 'lh_different_materials'

This commit is contained in:
Lukas Matena 2025-02-17 12:33:22 +01:00
commit 0bd3bfb986
19 changed files with 188 additions and 60 deletions

View File

@ -974,6 +974,7 @@ public:
// A scalar is nil, or all values of a vector are nil. // 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() 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(); } bool is_nil(size_t idx) const override { return values[idx < this->values.size() ? idx : 0] == nil_value(); }
std::vector<int> getInts() const override { return this->values; }
std::string serialize() const override std::string serialize() const override
{ {

View File

@ -1215,7 +1215,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// Enable ooze prevention if configured so. // Enable ooze prevention if configured so.
DoExport::init_ooze_prevention(print, m_ooze_prevention); 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_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); 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. // 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. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature // M140 - Set Extruder Temperature
// M190 - Set Extruder Temperature and Wait // 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) 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; const bool autoemit = print.config().autoemit_temperature_commands;
// Initial bed temperature based on the first extruder. const int num_extruders = print.config().nozzle_diameter.values.size();
int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); 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? // Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1; int temp_by_gcode = -1;
bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, false, temp_by_gcode); 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()))) 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_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. // Mark the temperature transition from 1st to 2nd layer to be finished.
m_second_layer_things_done = true; m_second_layer_things_done = true;
} }

View File

@ -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()); 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); 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_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_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); void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);

View File

@ -511,6 +511,7 @@ static std::vector<std::string> s_Preset_print_options {
"wall_distribution_count", "min_feature_size", "min_bead_width", "wall_distribution_count", "min_feature_size", "min_bead_width",
"top_one_perimeter_type", "only_one_perimeter_first_layer", "top_one_perimeter_type", "only_one_perimeter_first_layer",
"automatic_extrusion_widths", "automatic_infill_combination", "automatic_infill_combination_max_layer_height", "automatic_extrusion_widths", "automatic_infill_combination", "automatic_infill_combination_max_layer_height",
"bed_temperature_extruder"
}; };
static std::vector<std::string> s_Preset_filament_options { static std::vector<std::string> s_Preset_filament_options {

View File

@ -443,14 +443,19 @@ std::string Print::validate(std::vector<std::string>* warnings) const
std::vector<unsigned int> extruders = this->extruders(); std::vector<unsigned int> extruders = this->extruders();
if (warnings) { if (warnings) {
for (size_t a=0; a<extruders.size(); ++a) if (m_config.bed_temperature_extruder == 0) {
for (size_t b=a+1; b<extruders.size(); ++b) for (size_t a = 0; a < extruders.size(); ++a) {
if (std::abs(m_config.bed_temperature.get_at(extruders[a]) - m_config.bed_temperature.get_at(extruders[b])) > 15 for (size_t b = a + 1; b < extruders.size(); ++b) {
|| std::abs(m_config.first_layer_bed_temperature.get_at(extruders[a]) - m_config.first_layer_bed_temperature.get_at(extruders[b])) > 15) { if (std::abs(m_config.bed_temperature.get_at(extruders[a]) - m_config.bed_temperature.get_at(extruders[b])) > 15
warnings->emplace_back("_BED_TEMPS_DIFFER"); || std::abs(m_config.first_layer_bed_temperature.get_at(extruders[a]) - m_config.first_layer_bed_temperature.get_at(extruders[b])) > 15) {
goto DONE; warnings->emplace_back("_BED_TEMPS_DIFFER");
goto DONE;
}
} }
DONE:; }
DONE:;
}
if (!this->has_same_shrinkage_compensations()) if (!this->has_same_shrinkage_compensations())
warnings->emplace_back("_FILAMENT_SHRINKAGE_DIFFER"); warnings->emplace_back("_FILAMENT_SHRINKAGE_DIFFER");

View File

@ -608,7 +608,7 @@ public:
// List of existing PrintObject IDs, to remove notifications for non-existent IDs. // List of existing PrintObject IDs, to remove notifications for non-existent IDs.
std::vector<ObjectID> print_object_ids() const override; std::vector<ObjectID> print_object_ids() const override;
ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector<std::string> *warnings = nullptr) override;
void set_task(const TaskParams &params) override { PrintBaseWithState<PrintStep, psCount>::set_task_impl(params, m_objects); } void set_task(const TaskParams &params) override { PrintBaseWithState<PrintStep, psCount>::set_task_impl(params, m_objects); }
void process() override; void process() override;
void finalize() override { PrintBaseWithState<PrintStep, psCount>::finalize_impl(m_objects); } void finalize() override { PrintBaseWithState<PrintStep, psCount>::finalize_impl(m_objects); }

View File

@ -1050,7 +1050,23 @@ static PrintObjectRegions* generate_print_object_regions(
return out.release(); 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<std::string> *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<std::string> *warnings)
{ {
#ifdef _DEBUG #ifdef _DEBUG
check_model_ids_validity(model); 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 object_diff = m_default_object_config.diff(new_full_config);
t_config_option_keys region_diff = m_default_region_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. // Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED; unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated) auto update_apply_status = [&apply_status](bool invalidated)

View File

@ -433,7 +433,7 @@ public:
// Some data was changed, which in turn invalidated already calculated steps. // Some data was changed, which in turn invalidated already calculated steps.
APPLY_STATUS_INVALIDATED, APPLY_STATUS_INVALIDATED,
}; };
virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config) = 0; virtual ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector<std::string> *warnings = nullptr) = 0;
const Model& model() const { return m_model; } const Model& model() const { return m_model; }
struct TaskParams { struct TaskParams {

View File

@ -618,6 +618,16 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionInts{ 0 }); 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 = this->add("before_layer_gcode", coString);
def->label = L("Before layer change G-code"); 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. " 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")) if (!this->has("solid_infill_extruder") && this->has("infill_extruder"))
this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt()); 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<ConfigOptionFloats>("nozzle_diameter")->size();
const int extruder = this->opt<ConfigOptionInt>("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<ConfigOptionBool>("spiral_vase", true)->value) { if (this->has("spiral_vase") && this->opt<ConfigOptionBool>("spiral_vase", true)->value) {
{ {
// this should be actually done only on the spiral layers instead of all // this should be actually done only on the spiral layers instead of all

View File

@ -798,6 +798,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<ArcFittingType>, arc_fitting)) ((ConfigOptionEnum<ArcFittingType>, arc_fitting))
((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionBool, autoemit_temperature_commands))
((ConfigOptionInt, bed_temperature_extruder))
((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, before_layer_gcode))
((ConfigOptionString, between_objects_gcode)) ((ConfigOptionString, between_objects_gcode))
((ConfigOptionBool, binary_gcode)) ((ConfigOptionBool, binary_gcode))

View File

@ -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<std::string> *warnings)
{ {
#ifdef _DEBUG #ifdef _DEBUG
check_model_ids_validity(model); check_model_ids_validity(model);

View File

@ -490,7 +490,7 @@ public:
bool empty() const override { return m_objects.empty(); } bool empty() const override { return m_objects.empty(); }
// List of existing PrintObject IDs, to remove notifications for non-existent IDs. // List of existing PrintObject IDs, to remove notifications for non-existent IDs.
std::vector<ObjectID> print_object_ids() const override; std::vector<ObjectID> print_object_ids() const override;
ApplyStatus apply(const Model &model, DynamicPrintConfig config) override; ApplyStatus apply(const Model &model, DynamicPrintConfig config, std::vector<std::string> *warnings = nullptr) override;
void set_task(const TaskParams &params) override { PrintBaseWithState<SLAPrintStep, slapsCount>::set_task_impl(params, m_objects); } void set_task(const TaskParams &params) override { PrintBaseWithState<SLAPrintStep, slapsCount>::set_task_impl(params, m_objects); }
void process() override; void process() override;
void finalize() override { PrintBaseWithState<SLAPrintStep, slapsCount>::finalize_impl(m_objects); } void finalize() override { PrintBaseWithState<SLAPrintStep, slapsCount>::finalize_impl(m_objects); }

View File

@ -590,11 +590,11 @@ std::string BackgroundSlicingProcess::validate(std::vector<std::string>* warning
// Apply config over the print. Returns false, if the new config values caused any of the already // 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. // 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<std::string> *warnings)
{ {
assert(m_print != nullptr); assert(m_print != nullptr);
assert(config.opt_enum<PrinterTechnology>("printer_technology") == m_print->technology()); assert(config.opt_enum<PrinterTechnology>("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 && if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF &&
!m_fff_print->is_step_done(psGCodeExport)) { !m_fff_print->is_step_done(psGCodeExport)) {
// Some FFF status was invalidated, and the G-code was not exported yet. // Some FFF status was invalidated, and the G-code was not exported yet.

View File

@ -133,7 +133,7 @@ public:
// Apply config over the print. Returns false, if the new config values caused any of the already // 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. // 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<std::string> *warnings = nullptr);
// After calling the apply() function, set_task() may be called to limit the task to be processed by process(). // 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. // This is useful for calculating SLA supports for a single object only.
void set_task(const PrintBase::TaskParams &params); void set_task(const PrintBase::TaskParams &params);

View File

@ -968,7 +968,7 @@ void Plater::priv::init()
this->notification_manager->close_notification_of_type(NotificationType::UserAccountID); this->notification_manager->close_notification_of_type(NotificationType::UserAccountID);
// show connect tab // show connect tab
this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text); this->notification_manager->push_notification(NotificationType::UserAccountID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
this->main_frame->on_account_login(user_account->get_access_token()); this->main_frame->on_account_login(user_account->get_access_token());
} else { } else {
// refresh do different operations than on_account_login // 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->q->Bind(EVT_UA_REFRESH_TIME, [this](UserAccountTimeEvent& evt) {
this->user_account->set_refresh_time(evt.data); this->user_account->set_refresh_time(evt.data);
}); });
this->q->Bind(EVT_UA_ENQUEUED_REFRESH, [this](SimpleEvent& evt) { this->q->Bind(EVT_UA_ENQUEUED_REFRESH, [this](SimpleEvent& evt) {
this->main_frame->on_account_will_refresh(); this->main_frame->on_account_will_refresh();
}); });
this->q->Bind(EVT_PRINTABLES_CONNECT_PRINT, [this](wxCommandEvent& evt) { this->q->Bind(EVT_PRINTABLES_CONNECT_PRINT, [this](wxCommandEvent& evt) {
if (!this->user_account->is_logged()) { if (!this->user_account->is_logged()) {
// show login dialog instead of print dialog // show login dialog instead of print dialog
@ -1287,7 +1287,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
#ifdef __linux__ #ifdef __linux__
// On Linux Constructor of the ProgressDialog calls DisableOtherWindows() function which causes a disabling of all children of the find_toplevel_parent(q) // 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. // 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 // 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. // and that is why they will never be enabled after destruction of the ProgressDialog.
// So, distroy progress_gialog if we are loading project file // So, distroy progress_gialog if we are loading project file
@ -1365,8 +1365,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// For exporting from the 3mf we shouldn't check printer_presets for the containing information about "Print Host upload" // For exporting from the 3mf we shouldn't check printer_presets for the containing information about "Print Host upload"
wxGetApp().load_current_presets(false); wxGetApp().load_current_presets(false);
// Update filament colors for the MM-printer profile in the full config // Update filament colors for the MM-printer profile in the full config
// to avoid black (default) colors for Extruders in the ObjectList, // to avoid black (default) colors for Extruders in the ObjectList,
// when for extruder colors are used filament colors // when for extruder colors are used filament colors
q->update_filament_colors_in_full_config(); q->update_filament_colors_in_full_config();
is_project_file = true; is_project_file = true;
@ -2131,9 +2131,16 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto")); print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto"));
return true; return true;
}; };
} else if (text == "_BED_TEMPS_DIFFER") { } else if (text == "_BED_TEMPS_DIFFER" || text == "_BED_TEMPS_CHANGED") {
text = _u8L("Bed temperatures for the used filaments differ significantly."); 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; notification_type = NotificationType::BedTemperaturesDiffer;
action_fn = [](wxEvtHandler*) {
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") { } else if (text == "_FILAMENT_SHRINKAGE_DIFFER") {
text = _u8L("Filament shrinkage will not be used because filament shrinkage " text = _u8L("Filament shrinkage will not be used because filament shrinkage "
"for the used filaments differs significantly."); "for the used filaments differs significantly.");
@ -2290,15 +2297,16 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
std::vector<Print::ApplyStatus>(1) std::vector<Print::ApplyStatus>(1)
}; };
// Apply new config to the possibly running background task. std::vector<std::string> warnings;
// Apply new config to the possibly running background task and give the user feedback on warnings.
if (printer_technology == ptFFF) { if (printer_technology == ptFFF) {
with_single_bed_model_fff(q->model(), s_multiple_beds.get_active_bed(), [&](){ 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; apply_statuses[s_multiple_beds.get_active_bed()] = invalidated;
}); });
} else if (printer_technology == ptSLA) { } else if (printer_technology == ptSLA) {
with_single_bed_model_sla(q->model(), s_multiple_beds.get_active_bed(), [&](){ 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; apply_statuses[0] = invalidated;
}); });
} else { } else {
@ -2378,7 +2386,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
// The delayed error message is no more valid. // The delayed error message is no more valid.
delayed_error_message.clear(); 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. // The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors.
std::vector<std::string> warnings;
std::string err = background_process.validate(&warnings); std::string err = background_process.validate(&warnings);
if (err.empty()) { if (err.empty()) {
notification_manager->set_all_slicing_errors_gray(true); notification_manager->set_all_slicing_errors_gray(true);
@ -3170,16 +3177,16 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
std::vector<ObjectID> object_ids = { evt.status.warning_object_id }; std::vector<ObjectID> object_ids = { evt.status.warning_object_id };
std::vector<int> warning_steps = { evt.status.warning_step }; std::vector<int> warning_steps = { evt.status.warning_step };
std::vector<int> flagss = { int(evt.status.flags) }; std::vector<int> flagss = { int(evt.status.flags) };
if (warning_steps.front() == -1) { if (warning_steps.front() == -1) {
flagss = { PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS, PrintBase::SlicingStatus::UPDATE_PRINT_OBJECT_STEP_WARNINGS }; flagss = { PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS, PrintBase::SlicingStatus::UPDATE_PRINT_OBJECT_STEP_WARNINGS };
notification_manager->close_slicing_errors_and_warnings(); notification_manager->close_slicing_errors_and_warnings();
} }
for (int flags : flagss ) { for (int flags : flagss ) {
if (warning_steps.front() == -1) { if (warning_steps.front() == -1) {
warning_steps.clear(); warning_steps.clear();
if (flags == PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) { if (flags == PrintBase::SlicingStatus::UPDATE_PRINT_STEP_WARNINGS) {
int i = 0; int i = 0;
while (i < int(printer_technology == ptFFF ? psCount : slapsCount)) { warning_steps.push_back(i); ++i; } while (i < int(printer_technology == ptFFF ? psCount : slapsCount)) { warning_steps.push_back(i); ++i; }
} else { } else {
@ -5242,7 +5249,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*
return true; return true;
} else if (boost::algorithm::iends_with(filename, ".zip")) { } else if (boost::algorithm::iends_with(filename, ".zip")) {
if (!load_just_one_file) { if (!load_just_one_file) {
WarningDialog dlg(static_cast<wxWindow*>(this), WarningDialog dlg(static_cast<wxWindow*>(this),
format_wxstr(_L("You have several files for loading and \"%1%\" is one of them.\n" 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" "Please note that only one .zip file can be loaded at a time.\n"
"In this case we can load just \"%1%\".\n\n" "In this case we can load just \"%1%\".\n\n"

View File

@ -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<std::string> &another_blinking_opt_keys)
{ {
wxString page_title = translate_category(category, m_type); 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()); set_focus(field->getWindow());
} }
m_highlighter.init(get_custom_ctrl_with_blinking_ptr(opt_key)); std::vector<std::pair<OG_CustomCtrl *, bool *>> 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<std::string>& selected_options, const DynamicPrintConfig* config/* = nullptr*/) void Tab::cache_config_diff(const std::vector<std::string>& selected_options, const DynamicPrintConfig* config/* = nullptr*/)
@ -1635,6 +1640,7 @@ void TabPrint::build()
optgroup->append_single_option_line("support_material_extruder"); optgroup->append_single_option_line("support_material_extruder");
optgroup->append_single_option_line("support_material_interface_extruder"); optgroup->append_single_option_line("support_material_interface_extruder");
optgroup->append_single_option_line("wipe_tower_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 = page->new_optgroup(L("Ooze prevention"));
optgroup->append_single_option_line("ooze_prevention"); optgroup->append_single_option_line("ooze_prevention");

View File

@ -398,7 +398,7 @@ public:
void on_value_change(const std::string& opt_key, const boost::any& value); void on_value_change(const std::string& opt_key, const boost::any& value);
void update_wiping_button_visibility(); 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<std::string> &another_blinking_opt_keys = {});
void cache_config_diff(const std::vector<std::string>& selected_options, const DynamicPrintConfig* config = nullptr); void cache_config_diff(const std::vector<std::string>& selected_options, const DynamicPrintConfig* config = nullptr);
void apply_config_from_cache(); void apply_config_from_cache();

View File

@ -731,11 +731,33 @@ void HighlighterForWx::init(std::pair<OG_CustomCtrl*, bool*> params)
if (!Highlighter::init(!params.first && !params.second)) if (!Highlighter::init(!params.first && !params.second))
return; return;
m_custom_ctrl = params.first; assert(m_blinking_custom_ctrls.empty());
m_show_blink_ptr = params.second; m_blinking_custom_ctrls.push_back({params.first, params.second});
*m_show_blink_ptr = true; BlinkingCustomCtrl &blinking_custom_ctrl = m_blinking_custom_ctrls.back();
m_custom_ctrl->Refresh(); *blinking_custom_ctrl.show_blink_ptr = true;
blinking_custom_ctrl.custom_ctrl_ptr->Refresh();
}
void HighlighterForWx::init(const std::vector<std::pair<OG_CustomCtrl *, bool *>> &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 &params) { return params.first == nullptr || params.second == nullptr; });
if (!Highlighter::init(input_failed))
return;
assert(m_blinking_custom_ctrls.empty());
for (const std::pair<OG_CustomCtrl *, bool *> &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 // - using a BlinkingBitmap. Change state of this bitmap
@ -753,13 +775,15 @@ void HighlighterForWx::invalidate()
{ {
Highlighter::invalidate(); Highlighter::invalidate();
if (m_custom_ctrl && m_show_blink_ptr) { if (!m_blinking_custom_ctrls.empty()) {
*m_show_blink_ptr = false; for (BlinkingCustomCtrl &blinking_custom_ctrl : m_blinking_custom_ctrls) {
m_custom_ctrl->Refresh(); assert(blinking_custom_ctrl.is_valid());
m_show_blink_ptr = nullptr; *blinking_custom_ctrl.show_blink_ptr = false;
m_custom_ctrl = nullptr; blinking_custom_ctrl.custom_ctrl_ptr->Refresh();
} }
else if (m_blinking_bitmap) {
m_blinking_custom_ctrls.clear();
} else if (m_blinking_bitmap) {
m_blinking_bitmap->invalidate(); m_blinking_bitmap->invalidate();
m_blinking_bitmap = nullptr; m_blinking_bitmap = nullptr;
} }
@ -767,14 +791,17 @@ void HighlighterForWx::invalidate()
void HighlighterForWx::blink() void HighlighterForWx::blink()
{ {
if (m_custom_ctrl && m_show_blink_ptr) { if (!m_blinking_custom_ctrls.empty()) {
*m_show_blink_ptr = !*m_show_blink_ptr; for (BlinkingCustomCtrl &blinking_custom_ctrl : m_blinking_custom_ctrls) {
m_custom_ctrl->Refresh(); assert(blinking_custom_ctrl.is_valid());
} *blinking_custom_ctrl.show_blink_ptr = !*blinking_custom_ctrl.show_blink_ptr;
else if (m_blinking_bitmap) blinking_custom_ctrl.custom_ctrl_ptr->Refresh();
}
} else if (m_blinking_bitmap) {
m_blinking_bitmap->blink(); m_blinking_bitmap->blink();
else } else {
return; return;
}
Highlighter::blink(); Highlighter::blink();
} }

View File

@ -283,12 +283,24 @@ public:
class HighlighterForWx : public Highlighter 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<BlinkingCustomCtrl>;
// There are 2 possible cases to use HighlighterForWx: // There are 2 possible cases to use HighlighterForWx:
// - using a BlinkingBitmap. Change state of this bitmap // - 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 // - using OG_CustomCtrl where arrow will be rendered and flag indicated "show/hide" state of this arrow
OG_CustomCtrl* m_custom_ctrl { nullptr }; BlinkingCustomCtrls m_blinking_custom_ctrls;
bool* m_show_blink_ptr { nullptr };
public: public:
HighlighterForWx() {} HighlighterForWx() {}
@ -297,6 +309,7 @@ public:
void bind_timer(wxWindow* owner) override; void bind_timer(wxWindow* owner) override;
void init(BlinkingBitmap* blinking_bitmap); void init(BlinkingBitmap* blinking_bitmap);
void init(std::pair<OG_CustomCtrl*, bool*>); void init(std::pair<OG_CustomCtrl*, bool*>);
void init(const std::vector<std::pair<OG_CustomCtrl *, bool *>> &blinking_custom_ctrls_params);
void blink(); void blink();
void invalidate(); void invalidate();
}; };