From bfce6dba9bd9a6ce66259e5fa766876af36a75d2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 10 Nov 2017 17:27:05 +0100 Subject: [PATCH] Integrated the "compatible printers" idea by @alexrj with Vojtech's twist: The incompatible presets are hidden in the tabs if show_incompatible_presets is false. If show_incompatible_presets is true, there is a button to show / hide the incompatible presets from the tab selector. --- lib/Slic3r/GUI/MainFrame.pm | 17 +- lib/Slic3r/GUI/Plater.pm | 15 +- lib/Slic3r/GUI/Preferences.pm | 8 + lib/Slic3r/GUI/Tab.pm | 341 ++++++++++++++++++++++------- var/flag-green-icon.png | Bin 0 -> 672 bytes var/flag-red-icon.png | Bin 0 -> 665 bytes xs/src/slic3r/GUI/AppConfig.cpp | 2 + xs/src/slic3r/GUI/Preset.cpp | 82 +++++-- xs/src/slic3r/GUI/Preset.hpp | 28 ++- xs/src/slic3r/GUI/PresetBundle.cpp | 175 +++++++-------- xs/src/slic3r/GUI/PresetBundle.hpp | 11 +- xs/xsp/GUI_Preset.xsp | 29 ++- 12 files changed, 482 insertions(+), 226 deletions(-) create mode 100644 var/flag-green-icon.png create mode 100644 var/flag-red-icon.png diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 836c3e9d09..09a64ca4bc 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -127,10 +127,18 @@ sub _init_tabpanel { if ($self->{plater}) { # Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs. $self->{plater}->update_presets($tab_name, @_); - $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); - if ($self->{controller} && $tab_name eq 'printer') { - $self->{controller}->update_presets(@_); + if ($tab_name eq 'printer') { + # Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors. + wxTheApp->{preset_bundle}->print->update_tab_ui( + $self->{options_tabs}{'print'}->{presets_choice}, + $self->{options_tabs}{'print'}->{show_incompatible_presets}); + wxTheApp->{preset_bundle}->filament->update_tab_ui( + $self->{options_tabs}{'filament'}->{presets_choice}, + $self->{options_tabs}{'filament'}->{show_incompatible_presets}); + # Update the controller printers. + $self->{controller}->update_presets(@_) if $self->{controller}; } + $self->{plater}->on_config_change($tab->{presets}->get_current_preset->config); } }); # Load the currently selected preset into the GUI, update the preset selection box. @@ -666,6 +674,9 @@ sub update_ui_from_settings { my ($self) = @_; $self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing")); $self->{plater}->update_ui_from_settings if ($self->{plater}); + for my $tab_name (qw(print filament printer)) { + $self->{options_tabs}{$tab_name}->update_ui_from_settings; + } } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 6b853e410d..a6915bb7c2 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -504,7 +504,7 @@ sub _on_select_preset { wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection); } if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($idx, $choice); + wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice); } else { # call GetSelection() in scalar context as it's context-aware $self->{on_select_preset}->($group, $choice->GetStringSelection) @@ -573,12 +573,17 @@ sub update_presets { $choice_idx += 1; } } elsif ($group eq 'print') { - wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); + wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]); } elsif ($group eq 'printer') { - wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]); + # Update the print choosers to only contain the compatible presets, update the dirty flags. + wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]); + # Update the printer choosers, update the dirty flags. + wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]); + # Update the filament choosers to only contain the compatible presets, update the color preview, + # update the dirty flags. my $choice_idx = 0; foreach my $choice (@{$self->{preset_choosers}{filament}}) { - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($choice_idx, $choice); + wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice); $choice_idx += 1; } } @@ -1726,7 +1731,7 @@ sub filament_color_box_lmouse_down $colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX); $cfg->set('extruder_colour', $colors); $self->GetFrame->{options_tabs}{printer}->load_config($cfg); - wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($extruder_idx, $combobox); + wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $combobox); } $dialog->Destroy(); } diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index ca953e2152..d20ccb53fd 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -64,6 +64,14 @@ sub new { tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.', default => $app_config->get("no_defaults"), )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'show_incompatible_presets', + type => 'bool', + label => 'Show incompatible print and filament presets', + tooltip => 'When checked, the print and filament presets are shown in the preset editor even ' . + 'if they are marked as incompatible with the active printer', + default => $app_config->get("show_incompatible_presets"), + )); my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 2718d4f5eb..d479eb9115 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -20,8 +20,8 @@ use utf8; use File::Basename qw(basename); use List::Util qw(first); use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window - :button wxTheApp); -use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); + :button wxTheApp wxCB_READONLY); +use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_KEY_DOWN EVT_CHECKBOX EVT_TREE_SEL_CHANGED); use base qw(Wx::Panel Class::Accessor); sub new { @@ -37,7 +37,7 @@ sub new { { # choice menu - $self->{presets_choice} = Wx::Choice->new($self, -1, wxDefaultPosition, [270, -1], []); + $self->{presets_choice} = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, [270, -1], [], wxCB_READONLY); $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); # buttons @@ -45,15 +45,25 @@ sub new { wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + $self->{show_incompatible_presets} = 0; + $self->{bmp_show_incompatible_presets} = Wx::Bitmap->new(Slic3r::var("flag-red-icon.png"), wxBITMAP_TYPE_PNG); + $self->{bmp_hide_incompatible_presets} = Wx::Bitmap->new(Slic3r::var("flag-green-icon.png"), wxBITMAP_TYPE_PNG); + $self->{btn_hide_incompatible_presets} = Wx::BitmapButton->new($self, -1, + $self->{bmp_hide_incompatible_presets}, + wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); $self->{btn_delete_preset}->Disable; - my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); + my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer}->Add($hsizer, 0, wxBOTTOM, 3); $hsizer->Add($self->{presets_choice}, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); + $hsizer->AddSpacer(4); $hsizer->Add($self->{btn_save_preset}, 0, wxALIGN_CENTER_VERTICAL); + $hsizer->AddSpacer(4); $hsizer->Add($self->{btn_delete_preset}, 0, wxALIGN_CENTER_VERTICAL); + $hsizer->AddSpacer(16); + $hsizer->Add($self->{btn_hide_incompatible_presets}, 0, wxALIGN_CENTER_VERTICAL); } # Horizontal sizer to hold the tree and the selected page. @@ -95,12 +105,13 @@ sub new { } }); - EVT_CHOICE($parent, $self->{presets_choice}, sub { + EVT_COMBOBOX($parent, $self->{presets_choice}, sub { $self->select_preset($self->{presets_choice}->GetStringSelection); }); EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset }); EVT_BUTTON($self, $self->{btn_delete_preset}, sub { $self->delete_preset }); + EVT_BUTTON($self, $self->{btn_hide_incompatible_presets}, sub { $self->_toggle_show_hide_incompatible }); # Initialize the DynamicPrintConfig by default keys/values. # Possible %params keys: no_controller @@ -140,7 +151,7 @@ sub save_preset { eval { $self->{presets}->save_current_preset($name); }; Slic3r::GUI::catch_error($self) and return; # Add the new item into the UI component, remove dirty flags and activate the saved item. - $self->{presets}->update_tab_ui($self->{presets_choice}); + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); # Update the selection boxes at the platter. $self->_on_presets_changed; } @@ -162,6 +173,22 @@ sub delete_preset { $self->load_current_preset; } +sub _toggle_show_hide_incompatible { + my ($self) = @_; + $self->{show_incompatible_presets} = ! $self->{show_incompatible_presets}; + $self->_update_show_hide_incompatible_button; + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); +} + +sub _update_show_hide_incompatible_button { + my ($self) = @_; + $self->{btn_hide_incompatible_presets}->SetBitmap($self->{show_incompatible_presets} ? + $self->{bmp_show_incompatible_presets} : $self->{bmp_hide_incompatible_presets}); + $self->{btn_hide_incompatible_presets}->SetToolTipString($self->{show_incompatible_presets} ? + "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : + "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); +} + # Register the on_value_change callback. sub on_value_change { my ($self, $cb) = @_; @@ -205,28 +232,33 @@ sub on_preset_loaded {} # If the current preset is dirty, the user is asked whether the changes may be discarded. # if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. -sub may_discard_current_preset_if_dirty +sub may_discard_current_dirty_preset { - my ($self) = @_; - if ($self->{presets}->current_is_dirty) { - # Display a dialog showing the dirty options in a human readable form. - my $old_preset = $self->{presets}->get_current_preset; - my $name = $old_preset->default ? 'Default preset' : "Preset \"" . $old_preset->name . "\""; - # Collect descriptions of the dirty options. - my @option_names = (); - foreach my $opt_key (@{$self->{presets}->current_dirty_options}) { - my $opt = $Slic3r::Config::Options->{$opt_key}; - my $name = $opt->{full_label} // $opt->{label}; - $name = $opt->{category} . " > $name" if $opt->{category}; - push @option_names, $name; - } - # Show a confirmation dialog with the list of dirty options. - my $changes = join "\n", map "- $_", @option_names; - my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?", - 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return 0 if $confirm->ShowModal == wxID_NO; + my ($self, $presets, $new_printer_name) = @_; + $presets //= $self->{presets}; + # Display a dialog showing the dirty options in a human readable form. + my $old_preset = $presets->get_current_preset; + my $type_name = $presets->name; + my $name = $old_preset->default ? + ('Default ' . $type_name . ' preset') : + ($type_name . " preset \"" . $old_preset->name . "\""); + # Collect descriptions of the dirty options. + my @option_names = (); + foreach my $opt_key (@{$presets->current_dirty_options}) { + my $opt = $Slic3r::Config::Options->{$opt_key}; + my $name = $opt->{full_label} // $opt->{label}; + $name = $opt->{category} . " > $name" if $opt->{category}; + push @option_names, $name; } - return 1; + # Show a confirmation dialog with the list of dirty options. + my $changes = join "\n", map "- $_", @option_names; + my $message = (defined $new_printer_name) ? + "$name is not compatible with printer \"$new_printer_name\"\n and it has unsaved changes:" : + "$name has unsaved changes:"; + my $confirm = Wx::MessageDialog->new($self, + $message . "\n$changes\n\nDiscard changes and continue anyway?", + 'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); + return $confirm->ShowModal == wxID_YES; } # Called by the UI combo box when the user switches profiles. @@ -235,25 +267,60 @@ sub may_discard_current_preset_if_dirty sub select_preset { my ($self, $name, $force) = @_; $force //= 0; - if (! $force && ! $self->may_discard_current_preset_if_dirty) { - $self->{presets}->update_tab_ui($self->{presets_choice}); + my $current_dirty = $self->{presets}->current_is_dirty; + my $canceled = 0; + my $printer_tab = $self->{presets}->name eq 'printer'; + if (! $force && $current_dirty && ! $self->may_discard_current_dirty_preset) { + $canceled = 1; + } elsif ($printer_tab) { + # Before switching the printer to a new one, verify, whether the currently active print and filament + # are compatible with the new printer. + # If they are not compatible and the the current print or filament are dirty, let user decide + # whether to discard the changes or keep the current printer selection. + my $new_printer_name = $name // ''; + my $new_printer_preset = $self->{presets}->find_preset($new_printer_name, 1); + # my $new_nozzle_dmrs = $new_printer_preset->config->get('nozzle_diameter'); + my $print_presets = wxTheApp->{preset_bundle}->print; + if ($print_presets->current_is_dirty && + ! $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { + if ($self->may_discard_current_dirty_preset($print_presets, $new_printer_name)) { + $canceled = 1; + } else { + $print_presets->discard_current_changes; + } + } + my $filament_presets = wxTheApp->{preset_bundle}->filament; + # if ((@$new_nozzle_dmrs <= 1) && + if (! $canceled && $filament_presets->current_is_dirty && + ! $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) { + if ($self->may_discard_current_dirty_preset($filament_presets, $new_printer_name)) { + $canceled = 1; + } else { + $filament_presets->discard_current_changes; + } + } + } + if ($canceled) { + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); # Trigger the on_presets_changed event so that we also restore the previous value in the plater selector. $self->_on_presets_changed; - return; - } - if (defined $name) { - $self->{presets}->select_preset_by_name($name); } else { - $self->{presets}->select_preset(0); + if (defined $name) { + $self->{presets}->select_preset_by_name($name); + } else { + $self->{presets}->select_preset(0); + } + # Mark the print & filament enabled if they are compatible with the currently selected preset. + wxTheApp->{preset_bundle}->update_compatible_with_printer(1) + if $current_dirty || $printer_tab; + # Initialize the UI from the current preset. + $self->load_current_preset; } - # Initialize the UI from the current preset. - $self->load_current_preset; } # Initialize the UI from the current preset. sub load_current_preset { my ($self) = @_; - $self->{presets}->update_tab_ui($self->{presets_choice}); my $preset = $self->{presets}->get_current_preset; eval { local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); @@ -270,9 +337,8 @@ sub load_current_preset { # preset dirty again # (not sure this is true anymore now that update_dirty is idempotent) wxTheApp->CallAfter(sub { - $self->update_dirty; - #the following is called by update_dirty - #$self->_on_presets_changed; + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); + $self->_on_presets_changed; }); } @@ -344,7 +410,6 @@ sub update_dirty { # This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. sub load_config { my ($self, $config) = @_; - my $modified = 0; foreach my $opt_key (@{$self->{config}->diff($config)}) { $self->{config}->set($opt_key, $config->get($opt_key)); @@ -358,6 +423,22 @@ sub load_config { } } +# To be called by custom widgets, load a value into a config, +# update the preset selection boxes (the dirty flags) +sub _load_key_value { + my ($self, $opt_key, $value) = @_; + $self->{config}->set($opt_key, $value); + # Mark the print & filament enabled if they are compatible with the currently selected preset. + if ($opt_key eq 'compatible_printers') { + wxTheApp->{preset_bundle}->update_compatible_with_printer(0); + $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets}); + } else { + $self->{presets}->update_dirty_ui($self->{presets_choice}); + } + $self->_on_presets_changed; + $self->_update; +} + # Find a field with an index over all pages of this tab. # This method is used often and everywhere, therefore it shall be quick. sub get_field { @@ -381,6 +462,87 @@ sub set_value { return $changed; } +# Return a callback to create a Tab widget to mark the preferences as compatible / incompatible to the current printer. +sub _compatible_printers_widget { + my ($self) = @_; + + return sub { + my ($parent) = @_; + + my $checkbox = $self->{compatible_printers_checkbox} = Wx::CheckBox->new($parent, -1, "All"); + + my $btn = $self->{compatible_printers_btn} = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, + wxBU_LEFT | wxBU_EXACTFIT); + $btn->SetFont($Slic3r::GUI::small_font); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG)); + + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($checkbox, 0, wxALIGN_CENTER_VERTICAL); + $sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL); + + EVT_CHECKBOX($self, $checkbox, sub { + my $method = $checkbox->GetValue ? 'Disable' : 'Enable'; + $btn->$method; + # All printers have been made compatible with this preset. + $self->_load_key_value('compatible_printers', []) if $checkbox->GetValue; + }); + + EVT_BUTTON($self, $btn, sub { + # Collect names of non-default non-external printer profiles. + my @presets = map $_->name, grep !$_->default && !$_->external, + @{wxTheApp->{preset_bundle}->printer}; + my $dlg = Wx::MultiChoiceDialog->new($self, + "Select the printers this profile is compatible with.", + "Compatible printers", \@presets); + # Collect and set indices of printers marked as compatible. + my @selections = (); + foreach my $preset_name (@{ $self->{config}->get('compatible_printers') }) { + my $idx = first { $presets[$_] eq $preset_name } 0..$#presets; + push @selections, $idx if defined $idx; + } + $dlg->SetSelections(@selections); + # Show the dialog. + if ($dlg->ShowModal == wxID_OK) { + my $value = [ @presets[$dlg->GetSelections] ]; + if (!@$value) { + $checkbox->SetValue(1); + $btn->Disable; + } + # All printers have been made compatible with this preset. + $self->_load_key_value('compatible_printers', $value); + } + }); + + return $sizer; + }; +} + +sub _reload_compatible_printers_widget { + my ($self) = @_; + my $has_any = int(@{$self->{config}->get('compatible_printers')}) > 0; + my $method = $has_any ? 'Enable' : 'Disable'; + $self->{compatible_printers_checkbox}->SetValue(! $has_any); + $self->{compatible_printers_btn}->$method; +} + +sub update_ui_from_settings { + my ($self) = @_; + # Show the 'show / hide presets' button only for the print and filament tabs, and only if enabled + # in application preferences. + my $show = wxTheApp->{app_config}->get("show_incompatible_presets") && $self->{presets}->name ne 'printer'; + my $method = $show ? 'Show' : 'Hide'; + $self->{btn_hide_incompatible_presets}->$method; + # If the 'show / hide presets' button is hidden, hide the incompatible presets. + if ($show) { + $self->_update_show_hide_incompatible_button; + } else { + if ($self->{show_incompatible_presets}) { + $self->{show_incompatible_presets} = 0; + $self->{presets}->update_tab_ui($self->{presets_choice}, 0); + } + } +} + package Slic3r::GUI::Tab::Print; use base 'Slic3r::GUI::Tab'; @@ -651,12 +813,26 @@ sub build { $optgroup->append_single_option_line($option); } } + + { + my $page = $self->add_options_page('Dependencies', 'wrench.png'); + { + my $optgroup = $page->new_optgroup('Profile dependencies'); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Compatible printers', + widget => $self->_compatible_printers_widget, + ); + $optgroup->append_line($line); + } + } + } } # Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. sub _reload_config { my ($self) = @_; -# $self->_reload_compatible_printers_widget; + $self->_reload_compatible_printers_widget; $self->SUPER::_reload_config; } @@ -920,7 +1096,7 @@ sub build { full_width => 1, widget => sub { my ($parent) = @_; - return $self->{description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); + return $self->{cooling_description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); }, ); $optgroup->append_line($line); @@ -959,6 +1135,16 @@ sub build { $optgroup = $page->new_optgroup('Print speed override'); $optgroup->append_single_option_line('filament_max_volumetric_speed', 0); + + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => '', + full_width => 1, + widget => sub { + my ($parent) = @_; + return $self->{volumetric_speed_description_line} = Slic3r::GUI::OptionsGroup::StaticText->new($parent); + }, + ); + $optgroup->append_line($line); } } @@ -996,13 +1182,37 @@ sub build { $optgroup->append_single_option_line($option); } } + + { + my $page = $self->add_options_page('Dependencies', 'wrench.png'); + { + my $optgroup = $page->new_optgroup('Profile dependencies'); + { + my $line = Slic3r::GUI::OptionsGroup::Line->new( + label => 'Compatible printers', + widget => $self->_compatible_printers_widget, + ); + $optgroup->append_line($line); + } + } + } +} + +# Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. +sub _reload_config { + my ($self) = @_; + $self->_reload_compatible_printers_widget; + $self->SUPER::_reload_config; } # Slic3r::GUI::Tab::Filament::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user. sub _update { my ($self) = @_; - $self->_update_description; + $self->{cooling_description_line}->SetText( + Slic3r::GUI::PresetHints::cooling_description($self->{presets}->get_edited_preset)); + $self->{volumetric_speed_description_line}->SetText( + Slic3r::GUI::PresetHints::maximum_volumetric_flow_description(wxTheApp->{preset_bundle})); my $cooling = $self->{config}->cooling->[0]; my $fan_always_on = $cooling || $self->{config}->fan_always_on->[0]; @@ -1012,33 +1222,6 @@ sub _update { for qw(min_fan_speed disable_fan_first_layers); } -sub _update_description { - my ($self) = @_; - my $config = $self->{config}; - my $msg = ""; - my $fan_other_layers = $config->fan_always_on->[0] - ? sprintf "will always run at %d%%%s.", $config->min_fan_speed->[0], - ($config->disable_fan_first_layers->[0] > 1 - ? " except for the first " . $config->disable_fan_first_layers->[0] . " layers" - : $config->disable_fan_first_layers->[0] == 1 - ? " except for the first layer" - : "") - : "will be turned off."; - - if ($config->cooling->[0]) { - $msg = sprintf "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).", - $config->slowdown_below_layer_time->[0], $config->max_fan_speed->[0], $config->slowdown_below_layer_time->[0], $config->min_print_speed->[0]; - if ($config->fan_below_layer_time->[0] > $config->slowdown_below_layer_time->[0]) { - $msg .= sprintf "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.", - $config->fan_below_layer_time->[0], $config->max_fan_speed->[0], $config->min_fan_speed->[0]; - } - $msg .= "\nDuring the other layers, fan $fan_other_layers" - } else { - $msg = "Fan $fan_other_layers"; - } - $self->{description_line}->SetText($msg); -} - package Slic3r::GUI::Tab::Printer; use base 'Slic3r::GUI::Tab'; use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog); @@ -1060,19 +1243,14 @@ sub build { my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); $btn->SetFont($Slic3r::GUI::small_font); - $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG)); + $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("printer_empty.png"), wxBITMAP_TYPE_PNG)); my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($btn); EVT_BUTTON($self, $btn, sub { my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape); - if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue; - $self->{config}->set('bed_shape', $value); - $self->update_dirty; - $self->_on_value_change('bed_shape', $value); - } + $self->_load_key_value('bed_shape', $dlg->GetValue) if $dlg->ShowModal == wxID_OK; }); return $sizer; @@ -1188,13 +1366,8 @@ sub build { } if (@{$entries}) { my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); - if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue . ":" . $dlg->GetPort; - $self->{config}->set('octoprint_host', $value); - $self->update_dirty; - $self->_on_value_change('octoprint_host', $value); - $self->_reload_config; - } + $self->_load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort) + if $dlg->ShowModal == wxID_OK; } else { Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; } diff --git a/var/flag-green-icon.png b/var/flag-green-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e4bc611f87b454e5969078aa167f48798adfd208 GIT binary patch literal 672 zcmV;R0$=@!P)1R5;6} z(_KtbVHgK+*KHTwb z3U8KEr|M6CKHF5d&eQVNxr*Xd#pN8o{Cp0%jbBO9Pl3P2cIMh>fmjREstPwbsR%UAN&a~0&W97_+{w2 zzK;Pe5g_}Da_b3Y$=5(~fG&DU3P+ZQ@qA?rj`(8?e(XnAv>p1lR%ogzEDpDzrF16) z>>$A2s?J^XXy3bIMu@`YByICjm?Ii^mmGBZV2)~`4mZPIM`5A2nrR066HnGEYA&=F z{ItC{p*~!JTj5FqSfV=gCp=^dW8^+e7-xWNN zxg9h5B6J9LGr&<6$h%weF5k=nw=Wx9Cml;Jj>-7nQg?!GIQQV{9x_4d_iwmJ{{Z)# z5>8sc{Dg*|7C@Q@mHF%nZwj!Ba~VLi89@E#6*A|x)Vtp=Lby^Yqxhx(0000W2MxECPn{*ELdZ1op0xyVpxzDdhz3h^YVPo^S(T97!Wp}{CC5s zOO0&MCbYN{*S_u5sKy(UFm7-Lr)M|odnZ1F`|tp z;QLC~;7ZS9V!0QqC_yL9nVW1CJTMZN4M(R$+ zOTZBsFr_4h`&3JNv0I_O8fwI~`z}0v3|$@I8UFpf@&;uE8s7DoF&~bRfJhmzX*a6< zs}(O~Kx~cpZxH}pS*S{qZRPWYW?G`zW8AQcn3#1@S*BcNV6f!zir{5lPjYZSt>eR~E27PJ0+es0y~s0ch&nN;M*NkCc% zXiQ#beHkC&om4IpK8qPut?)aNVjs<%39&#|d3=N1!OZi|I!ONj#Vr@M%?lVEC`+Fg zAQwL{?FfzVoB-$9WC=Ju7r^r86-w*!nR~wgLM67#gs;7-00000NkvXXu0mjfgI^=` literal 0 HcmV?d00001 diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp index f978fcbb49..3161c5b788 100644 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ b/xs/src/slic3r/GUI/AppConfig.cpp @@ -40,6 +40,8 @@ void AppConfig::set_defaults() // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. if (get("no_defaults").empty()) set("no_defaults", "1"); + if (get("show_incompatible_presets").empty()) + set("show_incompatible_presets", "0"); // Version check is enabled by default in the config, but it is not implemented yet. if (get("version_check").empty()) set("version_check", "1"); diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index 596e07c8ed..5a9ceb04bf 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -96,6 +96,8 @@ void Preset::normalize(DynamicPrintConfig &config) size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->values.size(); const auto &defaults = FullPrintConfig::defaults(); for (const std::string &key : Preset::filament_options()) { + if (key == "compatible_printers") + continue; auto *opt = config.option(key, false); assert(opt != nullptr); assert(opt->is_vector()); @@ -138,13 +140,18 @@ std::string Preset::label() const return this->name + (this->is_dirty ? g_suffix_modified : ""); } -bool Preset::enable_compatible(const std::string &active_printer) +bool Preset::is_compatible_with_printer(const std::string &active_printer) const { - auto *compatible_printers = dynamic_cast(this->config.optptr("compatible_printers")); - this->is_visible = compatible_printers && ! compatible_printers->values.empty() && + auto *compatible_printers = dynamic_cast(this->config.option("compatible_printers")); + return this->is_default || active_printer.empty() || + compatible_printers == nullptr || compatible_printers->values.empty() || std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer) != compatible_printers->values.end(); - return this->is_visible; +} + +bool Preset::update_compatible_with_printer(const std::string &active_printer) +{ + return this->is_compatible = is_compatible_with_printer(active_printer); } const std::vector& Preset::print_options() @@ -171,7 +178,8 @@ const std::vector& Preset::print_options() "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_per_color_wipe" + "wipe_tower_width", "wipe_tower_per_color_wipe", + "compatible_printers" }; return s_opts; } @@ -183,7 +191,8 @@ const std::vector& Preset::filament_options() "extrusion_multiplier", "filament_density", "filament_cost", "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", - "end_filament_gcode" + "end_filament_gcode", + "compatible_printers" }; return s_opts; } @@ -361,6 +370,18 @@ size_t PresetCollection::first_visible_idx() const return idx; } +// Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. +size_t PresetCollection::first_compatible_idx() const +{ + size_t idx = m_default_suppressed ? 1 : 0; + for (; idx < this->m_presets.size(); ++ idx) + if (m_presets[idx].is_compatible) + break; + if (idx == this->m_presets.size()) + idx = 0; + return idx; +} + void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { @@ -369,13 +390,29 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) } } -void PresetCollection::enable_disable_compatible_to_printer(const std::string &active_printer) +void PresetCollection::update_compatible_with_printer(const std::string &active_printer, bool select_other_if_incompatible) { size_t num_visible = 0; - for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) - if (m_presets[idx_preset].enable_compatible(active_printer)) + for (size_t idx_preset = 1; idx_preset < m_presets.size(); ++ idx_preset) { + bool selected = idx_preset == m_idx_selected; + Preset &preset_selected = m_presets[idx_preset]; + Preset &preset_edited = selected ? m_edited_preset : preset_selected; + if (preset_edited.update_compatible_with_printer(active_printer)) + // Mark compatible presets as visible. + preset_selected.is_visible = true; + else if (selected && select_other_if_incompatible) { + preset_selected.is_visible = false; + m_idx_selected = (size_t)-1; + } + if (selected) + preset_selected.is_compatible = preset_edited.is_compatible; + if (preset_selected.is_visible) ++ num_visible; - if (num_visible == 0) + } + if (m_idx_selected == (size_t)-1) + // Find some other visible preset. + this->select_preset(first_visible_idx()); + else if (num_visible == 0) // Show the "-- default --" preset. m_presets.front().is_visible = true; } @@ -399,15 +436,18 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; - const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (bmp == 0) ? wxNullBitmap : *bmp, (void*)i); + if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) + continue; + const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), + (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } ui->Thaw(); } -void PresetCollection::update_tab_ui(wxChoice *ui) +void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) { if (ui == nullptr) return; @@ -415,8 +455,11 @@ void PresetCollection::update_tab_ui(wxChoice *ui) ui->Clear(); for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) { const Preset &preset = this->m_presets[i]; - const wxBitmap *bmp = (i == 0 || preset.is_visible) ? m_bitmap_compatible : m_bitmap_incompatible; - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), (void*)&preset); + if (! show_incompatible && ! preset.is_compatible && i != m_idx_selected) + continue; + const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; + ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), + (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); if (i == m_idx_selected) ui->SetSelection(ui->GetCount() - 1); } @@ -425,8 +468,9 @@ void PresetCollection::update_tab_ui(wxChoice *ui) // Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Return true if the dirty flag changed. -bool PresetCollection::update_dirty_ui(wxItemContainer *ui) +bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) { + wxWindowUpdateLocker noUpdates(ui); // 1) Update the dirty flag of the current preset. bool was_dirty = this->get_selected_preset().is_dirty; bool is_dirty = current_is_dirty(); @@ -445,12 +489,6 @@ bool PresetCollection::update_dirty_ui(wxItemContainer *ui) return was_dirty != is_dirty; } -bool PresetCollection::update_dirty_ui(wxChoice *ui) -{ - wxWindowUpdateLocker noUpdates(ui); - return update_dirty_ui(dynamic_cast(ui)); -} - Preset& PresetCollection::select_preset(size_t idx) { for (Preset &preset : m_presets) diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp index a9bc8812c0..f44fd1590c 100644 --- a/xs/src/slic3r/GUI/Preset.hpp +++ b/xs/src/slic3r/GUI/Preset.hpp @@ -49,6 +49,8 @@ public: bool is_visible = true; // Has this preset been modified? bool is_dirty = false; + // Is this preset compatible with the currently active printer? + bool is_compatible = true; // Name of the preset, usually derived form the file name. std::string name; @@ -77,8 +79,9 @@ public: void set_dirty(bool dirty = true) { this->is_dirty = dirty; } void reset_dirty() { this->is_dirty = false; } - // Mark this preset as visible if it is compatible with active_printer. - bool enable_compatible(const std::string &active_printer); + bool is_compatible_with_printer(const std::string &active_printer) const; + // Mark this preset as compatible if it is compatible with active_printer. + bool update_compatible_with_printer(const std::string &active_printer); // Resize the extruder specific fields, initialize them with the content of the 1st extruder. void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } @@ -147,6 +150,7 @@ public: // Return the selected preset, without the user modifications applied. Preset& get_selected_preset() { return m_presets[m_idx_selected]; } const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; } + int get_selected_idx() const { return m_idx_selected; } // Return the selected preset including the user modifications. Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } @@ -155,6 +159,7 @@ public: // Return a preset by an index. If the preset is active, a temporary copy is returned. Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } const Preset& preset(size_t idx) const { return const_cast(this)->preset(idx); } + void discard_current_changes() { m_edited_preset = m_presets[m_idx_selected]; } // Return a preset by its name. If the preset is active, a temporary copy is returned. // If a preset is not found by its name, null is returned. @@ -163,16 +168,19 @@ public: { return const_cast(this)->find_preset(name, first_visible_if_not_found); } size_t first_visible_idx() const; + size_t first_compatible_idx() const; // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. // Return the first visible preset. Certainly at least the '- default -' preset shall be visible. - Preset& first_visible() { return this->preset(this->first_visible_idx()); } - const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } + Preset& first_visible() { return this->preset(this->first_visible_idx()); } + const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } + Preset& first_compatible() { return this->preset(this->first_compatible_idx()); } + const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); } // Return number of presets including the "- default -" preset. size_t size() const { return this->m_presets.size(); } // For Print / Filament presets, disable those, which are not compatible with the printer. - void enable_disable_compatible_to_printer(const std::string &active_printer); + void update_compatible_with_printer(const std::string &active_printer, bool select_other_if_incompatible); size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); } @@ -182,13 +190,17 @@ public: std::vector current_dirty_options() { return this->get_selected_preset().config.diff(this->get_edited_preset().config); } // Update the choice UI from the list of presets. - void update_tab_ui(wxChoice *ui); + // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. + // If an incompatible preset is selected, it is shown as well. + void update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); + // Update the choice UI from the list of presets. + // Only the compatible presets are shown. + // If an incompatible preset is selected, it is shown as well. void update_platter_ui(wxBitmapComboBox *ui); // Update a dirty floag of the current preset, update the labels of the UI component accordingly. // Return true if the dirty flag changed. - bool update_dirty_ui(wxItemContainer *ui); - bool update_dirty_ui(wxChoice *ui); + bool update_dirty_ui(wxBitmapComboBox *ui); // Select a profile by its name. Return true if the selection changed. // Without force, the selection is only updated if the index changes. diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp index 915bec6aaa..cf253e6f1f 100644 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ b/xs/src/slic3r/GUI/PresetBundle.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -46,9 +47,7 @@ PresetBundle::PresetBundle() : this->prints .load_bitmap_default("cog.png"); this->filaments.load_bitmap_default("spool.png"); this->printers .load_bitmap_default("printer_empty.png"); - - // FIXME select some icons indicating compatibility. - this->load_compatible_bitmaps("cog.png", "cog.png"); + this->load_compatible_bitmaps(); } PresetBundle::~PresetBundle() @@ -110,6 +109,9 @@ void PresetBundle::load_selections(const AppConfig &config) break; this->set_filament_preset(i, remove_ini_suffix(config.get("presets", name))); } + // Update visibility of presets based on their compatibility with the active printer. + // This will switch the print or filament presets to compatible if the active presets are incompatible. + this->update_compatible_with_printer(false); } // Export selections (current print, current filaments, current printer) into config.ini @@ -137,8 +139,10 @@ void PresetBundle::export_selections(PlaceholderParser &pp) pp.set("printer_preset", printers.get_selected_preset().name); } -bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible) +bool PresetBundle::load_compatible_bitmaps() { + const std::string path_bitmap_compatible = "flag-green-icon.png"; + const std::string path_bitmap_incompatible = "flag-red-icon.png"; bool loaded_compatible = m_bitmapCompatible ->LoadFile( wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); bool loaded_incompatible = m_bitmapIncompatible->LoadFile( @@ -146,12 +150,12 @@ bool PresetBundle::load_compatible_bitmaps(const std::string &path_bitmap_compat if (loaded_compatible) { prints .set_bitmap_compatible(m_bitmapCompatible); filaments.set_bitmap_compatible(m_bitmapCompatible); - printers .set_bitmap_compatible(m_bitmapCompatible); +// printers .set_bitmap_compatible(m_bitmapCompatible); } if (loaded_incompatible) { - prints .set_bitmap_compatible(m_bitmapIncompatible); - filaments.set_bitmap_compatible(m_bitmapIncompatible); - printers .set_bitmap_compatible(m_bitmapIncompatible); + prints .set_bitmap_incompatible(m_bitmapIncompatible); + filaments.set_bitmap_incompatible(m_bitmapIncompatible); +// printers .set_bitmap_incompatible(m_bitmapIncompatible); } return loaded_compatible && loaded_incompatible; } @@ -180,6 +184,8 @@ DynamicPrintConfig PresetBundle::full_config() const std::vector filament_opts(num_extruders, nullptr); // loop through options and apply them to the resulting config. for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { + if (key == "compatible_printers") + continue; // Get a destination option. ConfigOption *opt_dst = out.option(key, false); if (opt_dst->is_scalar()) { @@ -434,6 +440,20 @@ void PresetBundle::update_multi_material_filament_presets() this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); } +void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible) +{ + this->prints.update_compatible_with_printer(this->printers.get_selected_preset().name, select_other_if_incompatible); + this->filaments.update_compatible_with_printer(this->printers.get_selected_preset().name, select_other_if_incompatible); + if (select_other_if_incompatible) { + // Verify validity of the current filament presets. + for (std::string &filament_name : this->filament_presets) { + Preset *preset = this->filaments.find_preset(filament_name, false); + if (preset == nullptr || ! preset->is_compatible) + filament_name = this->filaments.first_compatible().name; + } + } +} + void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings { boost::nowide::ofstream c; @@ -526,42 +546,66 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma // Fill in the list from scratch. ui->Freeze(); ui->Clear(); - for (size_t i = this->filaments().front().is_visible ? 0 : 1; i < this->filaments().size(); ++ i) { - const Preset &preset = this->filaments.preset(i); - if (! preset.is_visible) + const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); + // Show wide icons if the currently selected preset is not compatible with the current printer, + // and draw a red flag in front of the selected preset. + bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; + assert(selected_preset != nullptr); + for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) { + const Preset &preset = this->filaments.preset(i); + bool selected = this->filament_presets[idx_extruder] == preset.name; + if (! preset.is_visible || (! preset.is_compatible && ! selected)) continue; - bool selected = this->filament_presets[idx_extruder] == preset.name; // Assign an extruder color to the selected item if the extruder color is defined. std::string filament_rgb = preset.config.opt_string("filament_colour", 0); std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = bitmap = new wxBitmap(image); - } else { - bitmap = it->second; - } + bool single_bar = filament_rgb == extruder_rgb; + std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; + // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left + // to the filament color image. + if (wide_icons) + bitmap_key += preset.is_compatible ? "comp" : "notcomp"; + auto it = m_mapColorToBitmap.find(bitmap_key); + wxBitmap *bitmap = (it == m_mapColorToBitmap.end()) ? nullptr : it->second; + if (bitmap == nullptr) { + // Create the bitmap with color bars. + bitmap = new wxBitmap((wide_icons ? 16 : 0) + 24, 16); + bitmap->UseAlpha(); + wxMemoryDC memDC; + memDC.SelectObject(*bitmap); + memDC.SetBackground(*wxTRANSPARENT_BRUSH); + memDC.Clear(); + if (wide_icons && ! preset.is_compatible) + // Paint the red flag. + memDC.DrawBitmap(*m_bitmapIncompatible, 0, 0, false); + // Paint the color bars. + parse_color(filament_rgb, rgb); + wxImage image(24, 16); + image.InitAlpha(); + unsigned char* imgdata = image.GetData(); + unsigned char* imgalpha = image.GetAlpha(); + for (size_t i = 0; i < image.GetWidth() * image.GetHeight(); ++ i) { + *imgdata ++ = rgb[0]; + *imgdata ++ = rgb[1]; + *imgdata ++ = rgb[2]; + *imgalpha ++ = wxALPHA_OPAQUE; + } + if (! single_bar) { + parse_color(extruder_rgb, rgb); + imgdata = image.GetData(); + for (size_t r = 0; r < 16; ++ r) { + imgdata = image.GetData() + r * image.GetWidth() * 3; + for (size_t c = 0; c < 16; ++ c) { + *imgdata ++ = rgb[0]; + *imgdata ++ = rgb[1]; + *imgdata ++ = rgb[2]; + } + } + } + memDC.DrawBitmap(wxBitmap(image), wide_icons ? 16 : 0, 0, false); + memDC.SelectObject(wxNullBitmap); + m_mapColorToBitmap[bitmap_key] = bitmap; } - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), (bitmap == 0) ? wxNullBitmap : *bitmap); if (selected) ui->SetSelection(ui->GetCount() - 1); @@ -569,59 +613,6 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma ui->Thaw(); } -// Update the colors preview at the platter extruder combo box. -void PresetBundle::update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui) -{ - this->update_platter_filament_ui(idx_extruder, ui); - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - ui->Freeze(); - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string preset_name = ui->GetString(ui_id).utf8_str().data(); - size_t filament_preset_id = size_t(ui->GetClientData(ui_id)); - const Preset *filament_preset = filaments.find_preset(preset_name, false); - assert(filament_preset != nullptr); - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = filament_preset->config.opt_string("filament_colour", 0); - std::string extruder_rgb = (int(ui_id) == ui->GetSelection() && ! extruder_color.empty()) ? extruder_color : filament_rgb; - wxBitmap *bitmap = nullptr; - if (filament_rgb == extruder_rgb) { - auto it = m_mapColorToBitmap.find(filament_rgb); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - parse_color(filament_rgb, rgb); - wxImage image(24, 16); - image.SetRGB(wxRect(0, 0, 24, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } else { - std::string bitmap_key = filament_rgb + extruder_rgb; - auto it = m_mapColorToBitmap.find(bitmap_key); - if (it == m_mapColorToBitmap.end()) { - // Create the bitmap. - wxImage image(24, 16); - parse_color(extruder_rgb, rgb); - image.SetRGB(wxRect(0, 0, 16, 16), rgb[0], rgb[1], rgb[2]); - parse_color(filament_rgb, rgb); - image.SetRGB(wxRect(16, 0, 8, 16), rgb[0], rgb[1], rgb[2]); - m_mapColorToBitmap[filament_rgb] = new wxBitmap(image); - } else { - bitmap = it->second; - } - } - ui->SetItemBitmap(ui_id, *bitmap); - } - ui->Thaw(); -} - void PresetBundle::set_default_suppressed(bool default_suppressed) { prints.set_default_suppressed(default_suppressed); diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp index b1077c11cf..571dea0be8 100644 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ b/xs/src/slic3r/GUI/PresetBundle.hpp @@ -58,8 +58,6 @@ public: // Update a filament selection combo box on the platter for an idx_extruder. void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); - // Update the colors preview at the platter extruder combo box. - void update_platter_filament_ui_colors(unsigned int idx_extruder, wxBitmapComboBox *ui); // Enable / disable the "- default -" preset. void set_default_suppressed(bool default_suppressed); @@ -72,10 +70,17 @@ public: // update size and content of filament_presets. void update_multi_material_filament_presets(); + // Update the is_compatible flag of all print and filament presets depending on whether they are marked + // as compatible with the currently selected printer. + // Also updates the is_visible flag of each preset. + // If select_other_if_incompatible is true, then the print or filament preset is switched to some compatible + // preset if the current print or filament preset is not compatible. + void update_compatible_with_printer(bool select_other_if_incompatible); + private: void load_config_file_config(const std::string &path, const DynamicPrintConfig &config); void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - bool load_compatible_bitmaps(const std::string &path_bitmap_compatible, const std::string &path_bitmap_incompatible); + bool load_compatible_bitmaps(); // Indicator, that the preset is compatible with the selected printer. wxBitmap *m_bitmapCompatible; diff --git a/xs/xsp/GUI_Preset.xsp b/xs/xsp/GUI_Preset.xsp index a084d7f2aa..e1f8a72c4e 100644 --- a/xs/xsp/GUI_Preset.xsp +++ b/xs/xsp/GUI_Preset.xsp @@ -4,6 +4,7 @@ #include #include "slic3r/GUI/Preset.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/PresetHints.hpp" %} %name{Slic3r::GUI::Preset} class Preset { @@ -13,6 +14,7 @@ bool external() %code%{ RETVAL = THIS->is_external; %}; bool visible() %code%{ RETVAL = THIS->is_visible; %}; bool dirty() %code%{ RETVAL = THIS->is_dirty; %}; + bool is_compatible_with_printer(char *active_printer) const; const char* name() %code%{ RETVAL = THIS->name.c_str(); %}; const char* file() %code%{ RETVAL = THIS->file.c_str(); %}; @@ -30,6 +32,7 @@ Ref default_preset() %code%{ RETVAL = &THIS->default_preset(); %}; size_t size() const; size_t num_visible() const; + std::string name() const; Ref get_selected_preset() %code%{ RETVAL = &THIS->get_selected_preset(); %}; Ref get_current_preset() %code%{ RETVAL = &THIS->get_edited_preset(); %}; @@ -41,19 +44,20 @@ bool current_is_dirty(); std::vector current_dirty_options(); - void update_tab_ui(SV *ui) - %code%{ auto cb = (wxChoice*)wxPli_sv_2_object( aTHX_ ui, "Wx::Choice" ); - THIS->update_tab_ui(cb); %}; + void update_tab_ui(SV *ui, bool show_incompatible) + %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" ); + THIS->update_tab_ui(cb, show_incompatible); %}; void update_platter_ui(SV *ui) %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object( aTHX_ ui, "Wx::BitmapComboBox" ); THIS->update_platter_ui(cb); %}; bool update_dirty_ui(SV *ui) - %code%{ RETVAL = THIS->update_dirty_ui((wxChoice*)wxPli_sv_2_object(aTHX_ ui, "Wx::Choice")); %}; + %code%{ RETVAL = THIS->update_dirty_ui((wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox")); %}; void select_preset(int idx); bool select_preset_by_name(char *name) %code%{ RETVAL = THIS->select_preset_by_name(name, true); %}; + void discard_current_changes(); void save_current_preset(char *new_name) %code%{ @@ -150,14 +154,21 @@ PresetCollection::arrayref() void set_filament_preset(int idx, const char *name); void update_multi_material_filament_presets(); + void update_compatible_with_printer(bool select_other_if_incompatible); + Clone full_config() %code%{ RETVAL = THIS->full_config(); %}; void update_platter_filament_ui(int extruder_idx, SV *ui) %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); THIS->update_platter_filament_ui(extruder_idx, cb); %}; - - void update_platter_filament_ui_colors(int extruder_idx, SV *ui) - %code%{ auto cb = (wxBitmapComboBox*)wxPli_sv_2_object(aTHX_ ui, "Wx::BitmapComboBox"); - THIS->update_platter_filament_ui_colors(extruder_idx, cb); %}; - +}; + +%name{Slic3r::GUI::PresetHints} class PresetHints { + PresetHints(); + ~PresetHints(); + + static std::string cooling_description(Preset *preset) + %code%{ RETVAL = PresetHints::cooling_description(*preset); %}; + static std::string maximum_volumetric_flow_description(PresetBundle *preset) + %code%{ RETVAL = PresetHints::maximum_volumetric_flow_description(*preset); %}; };