Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorEnrico Turri <enricoturri@seznam.cz>2017-12-11 14:01:30 +0300
committerEnrico Turri <enricoturri@seznam.cz>2017-12-11 14:01:30 +0300
commit50a45949d1a2878e114cd5e8728275c2586a60e2 (patch)
tree3f7fa3dbd70d5b6b2bac0b6b3c5226a0fa775810 /lib
parentbea9628be08d9c2be9ba49c7538112d39b1d6fa8 (diff)
parent19388285205ff46379ce0f9b0291aff1badd6568 (diff)
merge with master
Diffstat (limited to 'lib')
-rw-r--r--lib/Slic3r.pm26
-rw-r--r--lib/Slic3r/Config.pm300
-rw-r--r--lib/Slic3r/GUI.pm251
-rw-r--r--lib/Slic3r/GUI/3DScene.pm2
-rw-r--r--lib/Slic3r/GUI/AboutDialog.pm2
-rw-r--r--lib/Slic3r/GUI/ConfigWizard.pm93
-rw-r--r--lib/Slic3r/GUI/Controller.pm58
-rw-r--r--lib/Slic3r/GUI/Controller/ManualControlDialog.pm2
-rw-r--r--lib/Slic3r/GUI/Controller/PrinterPanel.pm28
-rw-r--r--lib/Slic3r/GUI/MainFrame.pm393
-rw-r--r--lib/Slic3r/GUI/Notifier.pm2
-rw-r--r--lib/Slic3r/GUI/OptionsGroup/Field.pm4
-rw-r--r--lib/Slic3r/GUI/Plater.pm452
-rw-r--r--lib/Slic3r/GUI/Plater/2D.pm7
-rw-r--r--lib/Slic3r/GUI/Plater/3D.pm11
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm28
-rw-r--r--lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm4
-rw-r--r--lib/Slic3r/GUI/Preferences.pm31
-rw-r--r--lib/Slic3r/GUI/Tab.pm915
-rw-r--r--lib/Slic3r/Print.pm19
-rw-r--r--lib/Slic3r/Print/Object.pm3
21 files changed, 968 insertions, 1663 deletions
diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 7729b6126..22a6ee389 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -23,25 +23,14 @@ sub debugf {
our $loglevel = 0;
# load threads before Moo as required by it
-our $have_threads;
BEGIN {
# Test, whether the perl was compiled with ithreads support and ithreads actually work.
use Config;
- $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
- warn "threads.pm >= 1.96 is required, please update\n" if $have_threads && $threads::VERSION < 1.96;
-
- ### temporarily disable threads if using the broken Moo version
use Moo;
- $have_threads = 0 if $Moo::VERSION == 1.003000;
-
- # Disable multi threading completely by an environment value.
- # This is useful for debugging as the Perl debugger does not work
- # in multi-threaded context at all.
- # A good interactive perl debugger is the ActiveState Komodo IDE
- # or the EPIC http://www.epic-ide.org/
- $have_threads = 0 if (defined($ENV{'SLIC3R_SINGLETHREADED'}) && $ENV{'SLIC3R_SINGLETHREADED'} == 1);
- print "Threading disabled\n" if !$have_threads;
-
+ my $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
+ die "Slic3r Prusa Edition requires working Perl threads.\n" if ! $have_threads;
+ die "threads.pm >= 1.96 is required, please update\n" if $threads::VERSION < 1.96;
+ die "Perl threading is broken with this Moo version: " . $Moo::VERSION . "\n" if $Moo::VERSION == 1.003000;
$debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1);
print "Debugging output enabled\n" if $debug;
}
@@ -50,8 +39,10 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
if $^V == v5.16;
use FindBin;
-# Path to the images.
-our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] };
+
+# Let the XS module know where the GUI resources reside.
+set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
+set_var_dir(resources_dir() . "/icons");
use Moo 1.003001;
@@ -163,6 +154,7 @@ sub thread_cleanup {
*Slic3r::Surface::Collection::DESTROY = sub {};
*Slic3r::Print::SupportMaterial2::DESTROY = sub {};
*Slic3r::TriangleMesh::DESTROY = sub {};
+ *Slic3r::GUI::AppConfig::DESTROY = sub {};
*Slic3r::GUI::PresetBundle::DESTROY = sub {};
return undef; # this prevents a "Scalars leaked" warning
}
diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm
index 638a02c9b9..a9c822b96 100644
--- a/lib/Slic3r/Config.pm
+++ b/lib/Slic3r/Config.pm
@@ -8,42 +8,19 @@ use utf8;
use List::Util qw(first max);
-# cemetery of old config settings
-our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
- adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
- rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang
- randomize_start seal_position bed_size print_center g0 vibration_limit gcode_arcs pressure_advance);
-
# C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes.
# The C++ counterpart is a constant singleton.
our $Options = print_config_def();
-# overwrite the hard-coded readonly value (this information is not available in XS)
-$Options->{threads}{readonly} = !$Slic3r::have_threads;
-
-# generate accessors
+# Generate accessors.
{
no strict 'refs';
for my $opt_key (keys %$Options) {
- *{$opt_key} = sub { $_[0]->get($opt_key) };
- }
-}
-
-# Fill in the underlying C++ Slic3r::DynamicPrintConfig with the content of the defaults
-# provided by the C++ class Slic3r::FullPrintConfig.
-# Used by the UI.
-sub new_from_defaults {
- my ($class, @opt_keys) = @_;
- my $self = $class->new;
- # Instantiating the C++ class Slic3r::FullPrintConfig.
- my $defaults = Slic3r::Config::Full->new;
- if (@opt_keys) {
- $self->set($_, $defaults->get($_))
- for grep $defaults->has($_), @opt_keys;
- } else {
- $self->apply_static($defaults);
+ *{$opt_key} = sub {
+ #print "Slic3r::Config::accessor $opt_key\n";
+ $_[0]->get($opt_key)
+ };
}
- return $self;
}
# From command line parameters, used by slic3r.pl
@@ -87,273 +64,6 @@ sub new_from_cli {
return $self;
}
-sub merge {
- my $class = shift;
- my $config = $class->new;
- $config->apply($_) for @_;
- return $config;
-}
-
-# Load a flat ini file without a category into the underlying C++ Slic3r::DynamicConfig class,
-# convert legacy configuration names.
-sub load {
- my ($class, $file) = @_;
- # Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed
- # explicitely to avoid having to bundle the UTF8 Perl library.
- if ($file =~ /\.[gG][cC][oO][dD][eE]/ || $file =~ /\.[gG]/) {
- my $config = $class->new;
- $config->_load_from_gcode($file);
- return $config;
- } else {
- my $config = $class->new;
- $config->_load($file);
- return $config;
- }
-}
-
-# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class,
-# convert legacy configuration names.
-# Used to load a config bundle.
-sub load_ini_hash {
- my ($class, $ini_hash) = @_;
- my $config = $class->new;
- $config->set_deserialize($_, $ini_hash->{$_}) for keys %$ini_hash;
- return $config;
-}
-
-sub clone {
- my $self = shift;
- my $new = (ref $self)->new;
- $new->apply($self);
- return $new;
-}
-
-sub get_value {
- my ($self, $opt_key) = @_;
- return $Options->{$opt_key}{ratio_over}
- ? $self->get_abs_value($opt_key)
- : $self->get($opt_key);
-}
-
-# Create a hash of hashes from the underlying C++ Slic3r::DynamicPrintConfig.
-# The first hash key is '_' meaning no category.
-# Used to create a config bundle.
-sub as_ini {
- my ($self) = @_;
- my $ini = { _ => {} };
- foreach my $opt_key (sort @{$self->get_keys}) {
- next if $Options->{$opt_key}{shortcut};
- $ini->{_}{$opt_key} = $self->serialize($opt_key);
- }
- return $ini;
-}
-
-# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
-# objects because it performs cross checks
-sub validate {
- my $self = shift;
-
- # -j, --threads
- die "Invalid value for --threads\n"
- if $self->threads < 1;
-
- # --layer-height
- die "Invalid value for --layer-height\n"
- if $self->layer_height <= 0;
- die "--layer-height must be a multiple of print resolution\n"
- if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0;
-
- # --first-layer-height
- die "Invalid value for --first-layer-height\n"
- if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
- die "Invalid value for --first-layer-height\n"
- if $self->get_value('first_layer_height') <= 0;
-
- # --filament-diameter
- die "Invalid value for --filament-diameter\n"
- if grep $_ < 1, @{$self->filament_diameter};
-
- # --nozzle-diameter
- die "Invalid value for --nozzle-diameter\n"
- if grep $_ < 0, @{$self->nozzle_diameter};
-
- # --perimeters
- die "Invalid value for --perimeters\n"
- if $self->perimeters < 0;
-
- # --solid-layers
- die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0;
- die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
- die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
-
- # --gcode-flavor
- die "Invalid value for --gcode-flavor\n"
- if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
-
- die "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware\n"
- if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie'
- && $self->gcode_flavor ne 'reprap'
- && $self->gcode_flavor ne 'machinekit'
- && $self->gcode_flavor ne 'repetier';
-
- die "--use-firmware-retraction is not compatible with --wipe\n"
- if $self->use_firmware_retraction && first {$_} @{$self->wipe};
-
- # --fill-pattern
- die "Invalid value for --fill-pattern\n"
- if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}};
-
- # --external-fill-pattern
- die "Invalid value for --external-fill-pattern\n"
- if !first { $_ eq $self->external_fill_pattern } @{$Options->{external_fill_pattern}{values}};
-
- # --fill-density
- die "The selected fill pattern is not supposed to work at 100% density\n"
- if $self->fill_density == 100
- && !first { $_ eq $self->fill_pattern } @{$Options->{external_fill_pattern}{values}};
-
- # --infill-every-layers
- die "Invalid value for --infill-every-layers\n"
- if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
-
- # --skirt-height
- die "Invalid value for --skirt-height\n"
- if $self->skirt_height < -1; # -1 means as tall as the object
-
- # --bridge-flow-ratio
- die "Invalid value for --bridge-flow-ratio\n"
- if $self->bridge_flow_ratio <= 0;
-
- # extruder clearance
- die "Invalid value for --extruder-clearance-radius\n"
- if $self->extruder_clearance_radius <= 0;
- die "Invalid value for --extruder-clearance-height\n"
- if $self->extruder_clearance_height <= 0;
-
- # --extrusion-multiplier
- die "Invalid value for --extrusion-multiplier\n"
- if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
-
- # --default-acceleration
- die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
- if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
- && !$self->default_acceleration;
-
- # --spiral-vase
- if ($self->spiral_vase) {
- # Note that we might want to have more than one perimeter on the bottom
- # solid layers.
- die "Can't make more than one perimeter when spiral vase mode is enabled\n"
- if $self->perimeters > 1;
-
- die "Can't make less than one perimeter when spiral vase mode is enabled\n"
- if $self->perimeters < 1;
-
- die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
- if $self->fill_density > 0;
-
- die "Spiral vase mode is not compatible with top solid layers\n"
- if $self->top_solid_layers > 0;
-
- die "Spiral vase mode is not compatible with support material\n"
- if $self->support_material || $self->support_material_enforce_layers > 0;
- }
-
- # extrusion widths
- {
- my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
- die "Invalid extrusion width (too large)\n"
- if defined first { $_ > 10 * $max_nozzle_diameter }
- map $self->get_abs_value_over("${_}_extrusion_width", $max_nozzle_diameter),
- qw(perimeter infill solid_infill top_infill support_material first_layer);
- }
-
- # general validation, quick and dirty
- foreach my $opt_key (@{$self->get_keys}) {
- my $opt = $Options->{$opt_key};
- next unless defined $self->$opt_key;
- next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
- my $type = $1;
- my @values = ();
- if ($type =~ s/\@$//) {
- die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY';
- @values = @{ $self->$opt_key };
- } else {
- @values = ($self->$opt_key);
- }
- foreach my $value (@values) {
- if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') {
- $value =~ s/%$// if $opt->{type} eq 'percent';
- die "Invalid value for $opt_key\n"
- if ($type eq 'i' && $value !~ /^-?\d+$/)
- || (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/)
- || (defined $opt->{min} && $value < $opt->{min})
- || (defined $opt->{max} && $value > $opt->{max});
- } elsif ($type eq 's' && $opt->{type} eq 'select') {
- die "Invalid value for $opt_key\n"
- unless first { $_ eq $value } @{ $opt->{values} };
- }
- }
- }
-
- return 1;
-}
-
-# CLASS METHODS:
-
-# Write a "Windows" style ini file with categories enclosed in squre brackets.
-# Used by config-bundle-to-config.pl and to save slic3r.ini.
-sub write_ini {
- my $class = shift;
- my ($file, $ini) = @_;
-
- Slic3r::open(\my $fh, '>', $file);
- binmode $fh, ':utf8';
- my $localtime = localtime;
- printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime";
- # make sure the _ category is the first one written
- foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) {
- printf $fh "\n[%s]\n", $category if $category ne '_';
- foreach my $key (sort keys %{$ini->{$category}}) {
- printf $fh "%s = %s\n", $key, $ini->{$category}{$key};
- }
- }
- close $fh;
-}
-
-# Parse a "Windows" style ini file with categories enclosed in squre brackets.
-# Returns a hash of hashes over strings.
-# {category}{name}=value
-# Non-categorized entries are stored under a category '_'.
-# Used by config-bundle-to-config.pl and to read slic3r.ini.
-sub read_ini {
- my $class = shift;
- my ($file) = @_;
-
- local $/ = "\n";
- Slic3r::open(\my $fh, '<', $file)
- or die "Unable to open $file: $!\n";
- binmode $fh, ':utf8';
-
- my $ini = { _ => {} };
- my $category = '_';
- while (<$fh>) {
- s/\R+$//;
- next if /^\s+/;
- next if /^$/;
- next if /^\s*#/;
- if (/^\[(.+?)\]$/) {
- $category = $1;
- next;
- }
- /^(\w+) *= *(.*)/ or die "Unreadable configuration file (invalid data at line $.)\n";
- $ini->{$category}{$1} = $2;
- }
- close $fh;
-
- return $ini;
-}
-
package Slic3r::Config::Static;
use parent 'Slic3r::Config';
diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index dc93e513f..5176c9c91 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -52,27 +52,12 @@ use constant FILE_WILDCARDS => {
};
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)};
+# Datadir provided on the command line.
our $datadir;
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
-our $no_controller;
our $no_plater;
-our $autosave;
our @cb;
-our $Settings = {
- _ => {
- version_check => 1,
- autocenter => 1,
- # Disable background processing by default as it is not stable.
- background_processing => 0,
- # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
- # By default, Prusa has the controller hidden.
- no_controller => 1,
- # If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
- no_defaults => 1,
- },
-};
-
our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$small_font->SetPointSize(11) if &Wx::wxMAC;
our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
@@ -82,134 +67,71 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$medium_font->SetPointSize(12);
our $grey = Wx::Colour->new(200,200,200);
-#our $VERSION_CHECK_EVENT : shared = Wx::NewEventType;
-
-our $DLP_projection_screen;
-
sub OnInit {
my ($self) = @_;
- $self->SetAppName('Slic3r');
+ $self->SetAppName('Slic3rPE');
$self->SetAppDisplayName('Slic3r Prusa Edition');
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
-
- $self->{notifier} = Slic3r::GUI::Notifier->new;
- $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
- # locate or create data directory
+ # Set the Slic3r data directory at the Slic3r XS module.
# Unix: ~/.Slic3r
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
# Mac: "~/Library/Application Support/Slic3r"
- $datadir ||= Wx::StandardPaths::Get->GetUserDataDir;
- my $enc_datadir = Slic3r::encode_path($datadir);
- Slic3r::debugf "Data directory: %s\n", $datadir;
+ Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
+ Slic3r::GUI::set_wxapp($self);
+
+ $self->{notifier} = Slic3r::GUI::Notifier->new;
+ $self->{app_config} = Slic3r::GUI::AppConfig->new;
+ $self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
- # just checking for existence of $datadir is not enough: it may be an empty directory
+ # just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
# supplied as argument to --datadir; in that case we should still run the wizard
- my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1;
- foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") {
- next if -d $dir;
- if (!mkdir $dir) {
- my $error = "Slic3r was unable to create its data directory at $dir ($!).";
- warn "$error\n";
- fatal_error(undef, $error);
- }
+ eval { $self->{preset_bundle}->setup_directories() };
+ if ($@) {
+ warn $@ . "\n";
+ fatal_error(undef, $@);
}
-
+ my $run_wizard = ! $self->{app_config}->exists;
# load settings
- my $last_version;
- if (-f "$enc_datadir/slic3r.ini") {
- my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") };
- $Settings = $ini if $ini;
- $last_version = $Settings->{_}{version};
- $Settings->{_}{autocenter} //= 1;
- $Settings->{_}{background_processing} //= 1;
- # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
- $Settings->{_}{no_controller} //= 1;
- # If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
- $Settings->{_}{no_defaults} //= 1;
+ $self->{app_config}->load if ! $run_wizard;
+ $self->{app_config}->set('version', $Slic3r::VERSION);
+ $self->{app_config}->save;
+
+ # Suppress the '- default -' presets.
+ $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
+ eval { $self->{preset_bundle}->load_presets };
+ if ($@) {
+ warn $@ . "\n";
+ show_error(undef, $@);
}
- $Settings->{_}{version} = $Slic3r::VERSION;
- $self->save_settings;
+ eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
+ $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
# application frame
- Wx::Image::AddHandler(Wx::PNGHandler->new);
+ Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
- no_controller => $no_controller // $Settings->{_}{no_controller},
+ no_controller => $self->{app_config}->get('no_controller'),
no_plater => $no_plater,
);
$self->SetTopWindow($frame);
-
- # load init bundle
- {
- my @dirs = ($FindBin::Bin);
- if (&Wx::wxMAC) {
- push @dirs, qw();
- } elsif (&Wx::wxMSW) {
- push @dirs, qw();
- }
- my $init_bundle = first { -e $_ } map "$_/.init_bundle.ini", @dirs;
- if ($init_bundle) {
- Slic3r::debugf "Loading config bundle from %s\n", $init_bundle;
- $self->{mainframe}->load_configbundle($init_bundle, 1);
- $run_wizard = 0;
- }
- }
-
- if (!$run_wizard && (!defined $last_version || $last_version ne $Slic3r::VERSION)) {
- # user was running another Slic3r version on this computer
- if (!defined $last_version || $last_version =~ /^0\./) {
- show_info($self->{mainframe}, "Hello! Support material was improved since the "
- . "last version of Slic3r you used. It is strongly recommended to revert "
- . "your support material settings to the factory defaults and start from "
- . "those. Enjoy and provide feedback!", "Support Material");
- }
- if (!defined $last_version || $last_version =~ /^(?:0|1\.[01])\./) {
- show_info($self->{mainframe}, "Hello! In this version a new Bed Shape option was "
- . "added. If the bed coordinates in the plater preview screen look wrong, go "
- . "to Print Settings and click the \"Set\" button next to \"Bed Shape\".", "Bed Shape");
- }
+ if ($run_wizard) {
+ $self->{mainframe}->config_wizard;
}
- $self->{mainframe}->config_wizard if $run_wizard;
- eval { $self->{preset_bundle}->load_presets($datadir) };
-
-# $self->check_version
-# if $self->have_version_check
-# && ($Settings->{_}{version_check} // 1)
-# && (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400);
-
+
EVT_IDLE($frame, sub {
while (my $cb = shift @cb) {
$cb->();
}
+ $self->{app_config}->save if $self->{app_config}->dirty;
});
-# EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub {
-# my ($self, $event) = @_;
-# my ($success, $response, $manual_check) = @{$event->GetData};
-#
-# if ($success) {
-# if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) {
-# my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?",
-# 'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal;
-# Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES;
-# } else {
-# Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check;
-# }
-# $Settings->{_}{last_version_check} = time();
-# $self->save_settings;
-# } else {
-# Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check;
-# }
-# });
-
return 1;
}
sub about {
my ($self) = @_;
-
my $about = Slic3r::GUI::AboutDialog->new(undef);
$about->ShowModal;
$about->Destroy;
@@ -217,7 +139,6 @@ sub about {
sub system_info {
my ($self) = @_;
-
my $slic3r_info = Slic3r::slic3r_info(format => 'html');
my $copyright_info = Slic3r::copyright_info(format => 'html');
my $system_info = Slic3r::system_info(format => 'html');
@@ -295,11 +216,6 @@ sub notify {
$self->{notifier}->notify($message);
}
-sub save_settings {
- my ($self) = @_;
- Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
-}
-
# Called after the Preferences dialog is closed and the program settings are saved.
# Update the UI based on the current preferences.
sub update_ui_from_settings {
@@ -307,64 +223,11 @@ sub update_ui_from_settings {
$self->{mainframe}->update_ui_from_settings;
}
-sub presets {
- my ($self, $section) = @_;
-
- my %presets = ();
- opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
- or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
- # Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed
- # explicitely to avoid having to bundle the UTF8 Perl library.
- foreach my $file (grep /\.[iI][nN][iI]$/, readdir $dh) {
- $file = Slic3r::decode_path($file);
- my $name = basename($file);
- $name =~ s/\.ini$//;
- $presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
- }
- closedir $dh;
-
- return %presets;
-}
-
-#sub have_version_check {
-# my ($self) = @_;
-#
-# # return an explicit 0
-# return ($Slic3r::have_threads && $Slic3r::build && $have_LWP) || 0;
-#}
-
-#sub check_version {
-# my ($self, $manual_check) = @_;
-#
-# Slic3r::debugf "Checking for updates...\n";
-#
-# @_ = ();
-# threads->create(sub {
-# my $ua = LWP::UserAgent->new;
-# $ua->timeout(10);
-# my $response = $ua->get('http://slic3r.org/updatecheck');
-# Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $VERSION_CHECK_EVENT,
-# threads::shared::shared_clone([ $response->is_success, $response->decoded_content, $manual_check ])));
-# Slic3r::thread_cleanup();
-# })->detach;
-#}
-
-sub output_path {
- my ($self, $dir) = @_;
-
- return ($Settings->{_}{last_output_path} && $Settings->{_}{remember_output_path})
- ? $Settings->{_}{last_output_path}
- : $dir;
-}
-
sub open_model {
my ($self, $window) = @_;
- my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory}
- || $Slic3r::GUI::Settings->{recent}{config_directory}
- || '';
-
- my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):', $dir, "",
+ my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):',
+ $self->{app_config}->get_last_dir, "",
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
@@ -380,31 +243,6 @@ sub CallAfter {
push @cb, $cb;
}
-sub scan_serial_ports {
- my ($self) = @_;
-
- my @ports = ();
-
- if ($^O eq 'MSWin32') {
- # Windows
- if (eval "use Win32::TieRegistry; 1") {
- my $ts = Win32::TieRegistry->new("HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM",
- { Access => 'KEY_READ' });
- if ($ts) {
- # when no serial ports are available, the registry key doesn't exist and
- # TieRegistry->new returns undef
- $ts->Tie(\my %reg);
- push @ports, sort values %reg;
- }
- }
- } else {
- # UNIX and OS X
- push @ports, glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*';
- }
-
- return grep !/Bluetooth|FireFly/, @ports;
-}
-
sub append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
@@ -434,32 +272,31 @@ sub set_menu_item_icon {
# SetBitmap was not available on OS X before Wx 0.9927
if ($icon && $menuItem->can('SetBitmap')) {
- $menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
+ $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
}
}
sub save_window_pos {
my ($self, $window, $name) = @_;
- $Settings->{_}{"${name}_pos"} = join ',', $window->GetScreenPositionXY;
- $Settings->{_}{"${name}_size"} = join ',', $window->GetSizeWH;
- $Settings->{_}{"${name}_maximized"} = $window->IsMaximized;
- $self->save_settings;
+ $self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY);
+ $self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH);
+ $self->{app_config}->set("${name}_maximized", $window->IsMaximized);
+ $self->{app_config}->save;
}
sub restore_window_pos {
my ($self, $window, $name) = @_;
-
- if (defined $Settings->{_}{"${name}_pos"}) {
- my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ];
+ if ($self->{app_config}->has("${name}_pos")) {
+ my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ];
$window->SetSize($size);
my $display = Wx::Display->new->GetClientArea();
- my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ];
+ my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ];
if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) {
$window->Move($pos);
}
- $window->Maximize(1) if $Settings->{_}{"${name}_maximized"};
+ $window->Maximize(1) if $self->{app_config}->get("${name}_maximized");
}
}
diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index bb77ebd57..51c822590 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -1390,7 +1390,7 @@ sub _load_image_set_texture {
my ($self, $file_name) = @_;
# Load a PNG with an alpha channel.
my $img = Wx::Image->new;
- $img->LoadFile($Slic3r::var->($file_name), wxBITMAP_TYPE_PNG);
+ $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG);
# Get RGB & alpha raw data from wxImage, interleave them into a Perl array.
my @rgb = unpack 'C*', $img->GetData();
my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3);
diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm
index 398fde97d..0879ea35b 100644
--- a/lib/Slic3r/GUI/AboutDialog.pm
+++ b/lib/Slic3r/GUI/AboutDialog.pm
@@ -97,7 +97,7 @@ sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
- $self->{logo} = Wx::Bitmap->new($Slic3r::var->("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
+ $self->{logo} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
$self->SetMinSize(Wx::Size->new($self->{logo}->GetWidth, $self->{logo}->GetHeight));
EVT_PAINT($self, \&repaint);
diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm
index 3d94f9a68..c18741396 100644
--- a/lib/Slic3r/GUI/ConfigWizard.pm
+++ b/lib/Slic3r/GUI/ConfigWizard.pm
@@ -14,14 +14,14 @@ our $wizard = 'Wizard';
$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
sub new {
- my $class = shift;
- my ($parent) = @_;
+ my ($class, $parent, $presets) = @_;
my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
# initialize an empty repository
$self->{config} = Slic3r::Config->new;
- $self->add_page(Slic3r::GUI::ConfigWizard::Page::Welcome->new($self));
+ my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self);
+ $self->add_page($welcome_page);
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self));
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self));
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self));
@@ -32,12 +32,13 @@ sub new {
$_->build_index for @{$self->{pages}};
+ $welcome_page->set_selection_presets([@{$presets}, 'Other']);
+
return $self;
}
sub add_page {
- my $self = shift;
- my ($page) = @_;
+ my ($self, $page) = @_;
my $n = push @{$self->{pages}}, $page;
# add first page to the page area sizer
@@ -48,13 +49,13 @@ sub add_page {
}
sub run {
- my $self = shift;
-
+ my ($self) = @_;
+ my $result = undef;
if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
-
- # it would be cleaner to have these defined inside each page class,
- # in some event getting called before leaving the page
- {
+ my $preset_name = $self->{pages}[0]->{preset_name};
+ if ($preset_name eq 'Other') {
+ # it would be cleaner to have these defined inside each page class,
+ # in some event getting called before leaving the page
# set first_layer_height + layer_height based on nozzle_diameter
my $nozzle = $self->{config}->nozzle_diameter;
$self->{config}->set('first_layer_height', $nozzle->[0]);
@@ -66,14 +67,13 @@ sub run {
# set first_layer_bed_temperature to temperature + 5
$self->{config}->set('first_layer_bed_temperature',
[ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
+ $result = $self->{config};
+ } else {
+ $result = $preset_name;
}
-
- $self->Destroy;
- return $self->{config};
- } else {
- $self->Destroy;
- return undef;
}
+ $self->Destroy;
+ return $result;
}
package Slic3r::GUI::ConfigWizard::Index;
@@ -89,11 +89,11 @@ sub new {
push @{$self->{titles}}, $title;
$self->{own_index} = 0;
- $self->{bullets}->{before} = Wx::Bitmap->new($Slic3r::var->("bullet_black.png"), wxBITMAP_TYPE_PNG);
- $self->{bullets}->{own} = Wx::Bitmap->new($Slic3r::var->("bullet_blue.png"), wxBITMAP_TYPE_PNG);
- $self->{bullets}->{after} = Wx::Bitmap->new($Slic3r::var->("bullet_white.png"), wxBITMAP_TYPE_PNG);
+ $self->{bullets}->{before} = Wx::Bitmap->new(Slic3r::var("bullet_black.png"), wxBITMAP_TYPE_PNG);
+ $self->{bullets}->{own} = Wx::Bitmap->new(Slic3r::var("bullet_blue.png"), wxBITMAP_TYPE_PNG);
+ $self->{bullets}->{after} = Wx::Bitmap->new(Slic3r::var("bullet_white.png"), wxBITMAP_TYPE_PNG);
- $self->{background} = Wx::Bitmap->new($Slic3r::var->("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG);
+ $self->{background} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG);
$self->SetMinSize(Wx::Size->new($self->{background}->GetWidth, $self->{background}->GetHeight));
EVT_PAINT($self, \&repaint);
@@ -127,6 +127,8 @@ sub repaint {
$dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index};
$dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h));
+ # Only show the first bullet if this is the only wizard page to be displayed.
+ last if $i == 0 && $self->{just_welcome};
$i++;
}
@@ -202,7 +204,7 @@ sub append_option {
# populate repository with the factory default
my ($opt_key, $opt_index) = split /#/, $full_key, 2;
- $self->config->apply(Slic3r::Config->new_from_defaults($opt_key));
+ $self->config->apply(Slic3r::Config::new_from_defaults_keys([$opt_key]));
# draw the control
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
@@ -263,19 +265,58 @@ sub config {
package Slic3r::GUI::ConfigWizard::Page::Welcome;
use base 'Slic3r::GUI::ConfigWizard::Page';
+use Wx qw(:misc :sizer wxID_FORWARD);
+use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE);
sub new {
my $class = shift;
my ($parent) = @_;
my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
+ $self->{full_wizard_workflow} = 1;
+
+ $self->append_text('Hello, welcome to Slic3r Prusa Edition! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.');
+ $self->append_text('Please select your printer vendor and printer type. If your printer is not listed, you may try your luck and select a similar one. If you select "Other", this ' . lc($wizard) . ' will let you set the basic 3D printer parameters.');
+ # To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.');
+ $self->append_text('If you received a configuration file or a config bundle from your 3D printer vendor, cancel this '.lc($wizard).' and use the "File->Load Config" or "File->Load Config Bundle" menu.');
+
+ $self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
+ $self->{vsizer}->Add($choice, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
+
+ EVT_CHOICE($parent, $choice, sub {
+ my $sel = $self->{choice}->GetStringSelection;
+ $self->{preset_name} = $sel;
+ $self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq ''));
+ });
- $self->append_text('Hello, welcome to Slic3r! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.');
- $self->append_text('To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.');
- $self->append_text('To continue, click Next.');
+ EVT_ACTIVATE($parent, sub {
+ $self->set_full_wizard_workflow($self->{preset_name} eq 'Other');
+ });
return $self;
}
+sub set_full_wizard_workflow {
+ my ($self, $full_workflow) = @_;
+ $self->{full_wizard_workflow} = $full_workflow;
+ $self->{index}->{just_welcome} = !$full_workflow;
+ $self->{index}->Refresh;
+ my $next_button = $self->GetParent->FindWindow(wxID_FORWARD);
+ $next_button->SetLabel($full_workflow ? "&Next >" : "&Finish");
+}
+
+# Set the preset names, select the first item.
+sub set_selection_presets {
+ my ($self, $names) = @_;
+ $self->{choice}->Append($names);
+ $self->{choice}->SetSelection(0);
+ $self->{preset_name} = $names->[0];
+}
+
+sub GetNext {
+ my $self = shift;
+ return $self->{full_wizard_workflow} ? $self->{next_page} : undef;
+}
+
package Slic3r::GUI::ConfigWizard::Page::Firmware;
use base 'Slic3r::GUI::ConfigWizard::Page';
@@ -300,7 +341,7 @@ sub new {
$self->append_text('Set the shape of your printer\'s bed, then click Next.');
- $self->config->apply(Slic3r::Config->new_from_defaults('bed_shape'));
+ $self->config->apply(Slic3r::Config::new_from_defaults_keys(['bed_shape']));
$self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape);
$self->{bed_shape_panel}->on_change(sub {
$self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue);
diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm
index 8c4dcbec7..6aa7b34cb 100644
--- a/lib/Slic3r/GUI/Controller.pm
+++ b/lib/Slic3r/GUI/Controller.pm
@@ -10,6 +10,7 @@ use utf8;
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
use base qw(Wx::ScrolledWindow Class::Accessor);
+use List::Util qw(first);
__PACKAGE__->mk_accessors(qw(_selected_printer_preset));
@@ -32,27 +33,25 @@ sub new {
# button for adding new printer panels
{
- my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
+ my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
$btn->SetToolTipString("Add printer…")
if $btn->can('SetToolTipString');
EVT_LEFT_DOWN($btn, sub {
- my $menu = Wx::Menu->new;
- my %presets = wxTheApp->presets('printer');
-
+ my $menu = Wx::Menu->new;
+ my @panels = $self->print_panels;
# remove printers that already exist
- my @panels = $self->print_panels;
- delete $presets{$_} for map $_->printer_name, @panels;
-
- foreach my $preset_name (sort keys %presets) {
- my $config = Slic3r::Config->load($presets{$preset_name});
- next if !$config->serial_port;
-
+ # update configs of currently loaded print panels
+ foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) {
+ my $preset_name = $preset->name;
+ next if ! $preset->config->serial_port ||
+ defined first { defined $_ && $_->printer_name eq $preset_name } @panels;
+ my $myconfig = $preset->config->clone_only(\@ConfigOptions);
my $id = &Wx::NewId();
$menu->Append($id, $preset_name);
EVT_MENU($menu, $id, sub {
- $self->add_printer($preset_name, $config);
+ $self->add_printer($preset_name, $myconfig);
});
}
$self->PopupMenu($menu, $btn->GetPosition);
@@ -98,12 +97,8 @@ sub OnActivate {
my ($self) = @_;
# get all available presets
- my %presets = ();
- {
- my %all = wxTheApp->presets('printer');
- my %configs = map { my $name = $_; $name => Slic3r::Config->load($all{$name}) } keys %all;
- %presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
- }
+ my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) }
+ grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer};
# decide which ones we want to keep
my %active = ();
@@ -121,7 +116,7 @@ sub OnActivate {
}
if (!%active) {
# enable printers whose port is available
- my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports;
+ my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports;
$active{$_} = 1
for grep exists $ports{$presets{$_}->serial_port}, keys %presets;
}
@@ -177,26 +172,19 @@ sub print_panels {
map $_->GetWindow, $self->{sizer}->GetChildren;
}
-# Called by
-# Slic3r::GUI::Tab::Print::_on_presets_changed
-# Slic3r::GUI::Tab::Filament::_on_presets_changed
-# Slic3r::GUI::Tab::Printer::_on_presets_changed
-# when the presets are loaded or the user select another preset.
+# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed
+# when the presets are loaded or the user selects another preset.
sub update_presets {
- my $self = shift;
- my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_;
-
+ my ($self, $presets) = @_;
# update configs of currently loaded print panels
+ my @presets = @$presets;
foreach my $panel ($self->print_panels) {
- foreach my $preset (@$presets) {
- if ($panel->printer_name eq $preset->name) {
- my $config = $preset->config(\@ConfigOptions);
- $panel->config->apply($config);
- }
- }
+ my $preset = $presets->find_preset($panel->printer_name, 0);
+ $panel->config($preset->config->clone_only(\@ConfigOptions))
+ if defined $preset;
}
-
- $self->_selected_printer_preset($presets->[$selected]->name);
+
+ $self->_selected_printer_preset($presets->get_selected_preset->name);
}
1;
diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm
index 8dcc05424..de0565255 100644
--- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm
+++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm
@@ -34,7 +34,7 @@ sub new {
my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
wxBU_LEFT | wxBU_EXACTFIT);
$btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("$icon.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapPosition($pos);
EVT_BUTTON($self, $btn, $handler);
$sizer->Add($btn, 1, wxEXPAND | wxALL, 0);
diff --git a/lib/Slic3r/GUI/Controller/PrinterPanel.pm b/lib/Slic3r/GUI/Controller/PrinterPanel.pm
index 6dba614e3..794ea4e4e 100644
--- a/lib/Slic3r/GUI/Controller/PrinterPanel.pm
+++ b/lib/Slic3r/GUI/Controller/PrinterPanel.pm
@@ -103,7 +103,7 @@ sub new {
$serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
}
{
- $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new($Slic3r::var->("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG),
+ $self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE);
$btn->SetToolTipString("Rescan serial ports")
if $btn->can('SetToolTipString');
@@ -127,7 +127,7 @@ sub new {
{
$self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize);
$btn->SetFont($Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG));
$serial_speed_sizer->Add($btn, 0, wxLEFT, 5);
EVT_BUTTON($self, $btn, \&disconnect);
}
@@ -140,7 +140,7 @@ sub new {
my $font = $btn->GetFont;
$font->SetPointSize($font->GetPointSize + 2);
$btn->SetFont($font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("arrow_up.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("arrow_up.png"), wxBITMAP_TYPE_PNG));
$left_sizer->Add($btn, 0, wxTOP, 15);
EVT_BUTTON($self, $btn, \&connect);
}
@@ -160,7 +160,7 @@ sub new {
{
$self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize);
$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("cog.png"), wxBITMAP_TYPE_PNG));
$btn->Hide;
$left_sizer->Add($btn, 0, wxTOP, 15);
EVT_BUTTON($self, $btn, sub {
@@ -348,7 +348,7 @@ sub update_serial_ports {
my $cb = $self->{serial_port_combobox};
my $current = $cb->GetValue;
$cb->Clear;
- $cb->Append($_) for wxTheApp->scan_serial_ports;
+ $cb->Append($_) for Slic3r::GUI::scan_serial_ports;
$cb->SetValue($current);
}
@@ -577,7 +577,7 @@ sub new {
$btn->SetToolTipString("Delete this job from print queue")
if $btn->can('SetToolTipString');
$btn->SetFont($Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG));
if ($job->printing) {
$btn->Hide;
}
@@ -597,8 +597,8 @@ sub new {
my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
$button_style);
$btn->SetFont($Slic3r::GUI::small_bold_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG));
- $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
#$btn->SetBitmapPosition(wxRIGHT);
$btn->Hide;
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
@@ -616,8 +616,8 @@ sub new {
if (!$job->printing || $job->paused) {
$btn->Hide;
}
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_pause.png"), wxBITMAP_TYPE_PNG));
- $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_pause_blue.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
@@ -633,8 +633,8 @@ sub new {
if (!$job->printing || !$job->paused) {
$btn->Hide;
}
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG));
- $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
@@ -650,8 +650,8 @@ sub new {
if (!$job->printing) {
$btn->Hide;
}
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_stop.png"), wxBITMAP_TYPE_PNG));
- $btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_stop_blue.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index ad2eca91d..a6baef8f9 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -6,6 +6,7 @@ use warnings;
use utf8;
use File::Basename qw(basename dirname);
+use FindBin;
use List::Util qw(min);
use Slic3r::Geometry qw(X Y);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
@@ -21,13 +22,14 @@ sub new {
my ($class, %params) = @_;
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
+ Slic3r::GUI::set_main_frame($self);
if ($^O eq 'MSWin32') {
- # Load the icon either from the exe, or fron the ico file.
- my $iconfile = $Slic3r::var->('..\slic3r.exe');
- $iconfile = $Slic3r::var->("Slic3r.ico") unless -f $iconfile;
+ # Load the icon either from the exe, or from the ico file.
+ my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
+ $iconfile = Slic3r::var("Slic3r.ico") unless -f $iconfile;
$self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO));
} else {
- $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
+ $self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
}
# store input params
@@ -69,15 +71,15 @@ sub new {
# declare events
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
-
if ($event->CanVeto && !$self->check_unsaved_changes) {
$event->Veto;
return;
}
-
# save window size
wxTheApp->save_window_pos($self, "main_frame");
-
+ # Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
+ # but in rare cases it may not have been called yet.
+ wxTheApp->{app_config}->save;
# propagate event
$event->Skip;
});
@@ -91,6 +93,8 @@ sub _init_tabpanel {
my ($self) = @_;
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
+ Slic3r::GUI::set_tab_panel($panel);
+
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage;
$panel->OnActivate if $panel->can('OnActivate');
@@ -112,7 +116,7 @@ sub _init_tabpanel {
# Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value.
$tab->on_value_change(sub {
my ($opt_key, $value) = @_;
- my $config = $tab->config;
+ my $config = $tab->{presets}->get_current_preset->config;
if ($self->{plater}) {
$self->{plater}->on_config_change($config); # propagate config change events to the plater
$self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count';
@@ -126,24 +130,38 @@ 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->config);
- if ($self->{controller}) {
- $self->{controller}->update_presets($tab_name, @_);
+ 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);
}
});
- $tab->load_presets;
+ # Load the currently selected preset into the GUI, update the preset selection box.
+ $tab->load_current_preset;
$panel->AddPage($tab, $tab->title);
}
+
+#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
+# Slic3r::GUI::create_preset_tab("print");
if ($self->{plater}) {
$self->{plater}->on_select_preset(sub {
- my ($group, $i) = @_;
- $self->{options_tabs}{$group}->select_preset($i);
+ my ($group, $name) = @_;
+ $self->{options_tabs}{$group}->select_preset($name);
});
-
# load initial config
- $self->{plater}->on_config_change($self->config);
+ my $full_config = wxTheApp->{preset_bundle}->full_config;
+ $self->{plater}->on_config_change($full_config);
+ # Show a correct number of filament fields.
+ $self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
}
}
@@ -153,6 +171,9 @@ sub _init_menubar {
# File menu
my $fileMenu = Wx::Menu->new;
{
+ wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub {
+ $self->{plater}->add if $self->{plater};
+ }, undef, undef); #'brick_add.png');
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
$self->load_config_file;
}, undef, 'plugin_add.png');
@@ -262,13 +283,14 @@ sub _init_menubar {
# \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
# as the simple numeric accelerators spoil all numeric data entry.
# The camera control accelerators are captured by 3DScene Perl module instead.
- $self->_append_menu_item($self->{viewMenu}, "Iso\t\xA00" , 'Iso View' , sub { $self->select_view('iso' ); });
- $self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); });
- $self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); });
- $self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); });
- $self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); });
- $self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); });
- $self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); });
+ my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); });
+ $self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); });
}
# Help menu
@@ -317,6 +339,8 @@ sub _init_menubar {
$menubar->Append($windowMenu, "&Window");
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
$menubar->Append($helpMenu, "&Help");
+ # Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
+ Slic3r::GUI::add_debug_menu($menubar);
$self->SetMenuBar($menubar);
}
}
@@ -329,7 +353,6 @@ sub is_loaded {
# Selection of a 3D object changed on the platter.
sub on_plater_selection_changed {
my ($self, $have_selection) = @_;
-
return if !defined $self->{object_menu};
$self->{object_menu}->Enable($_->GetId, $have_selection)
for $self->{object_menu}->GetMenuItems;
@@ -337,20 +360,20 @@ sub on_plater_selection_changed {
# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
sub quick_slice {
- my $self = shift;
- my %params = @_;
+ my ($self, %params) = @_;
my $progress_dialog;
eval {
# validate configuration
- my $config = $self->config;
+ my $config = wxTheApp->{preset_bundle}->full_config();
$config->validate;
# select input file
my $input_file;
- my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
if (!$params{reslice}) {
- my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):',
+ wxTheApp->{app_config}->get_last_dir, "",
+ &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
@@ -372,8 +395,7 @@ sub quick_slice {
$input_file = $qs_last_input_file;
}
my $input_file_basename = basename($input_file);
- $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
- wxTheApp->save_settings;
+ wxTheApp->{app_config}->update_skein_dir(dirname($input_file));
my $print_center;
{
@@ -395,20 +417,19 @@ sub quick_slice {
$sprint->apply_config($config);
$sprint->set_model($model);
- {
- my $extra = $self->extra_variables;
- $sprint->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
- }
-
+ # Copy the names of active presets into the placeholder parser.
+ wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser);
+
# select output file
my $output_file;
if ($params{reslice}) {
$output_file = $qs_last_output_file if defined $qs_last_output_file;
} elsif ($params{save_as}) {
+ # The following line may die if the output_filename_format template substitution fails.
$output_file = $sprint->output_filepath;
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
- wxTheApp->output_path(dirname($output_file)),
+ wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
@@ -416,8 +437,7 @@ sub quick_slice {
}
$output_file = $dlg->GetPath;
$qs_last_output_file = $output_file unless $params{export_svg};
- $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file);
- wxTheApp->save_settings;
+ wxTheApp->{app_config}->update_last_output_dir(dirname($output_file));
$dlg->Destroy;
}
@@ -452,9 +472,7 @@ sub quick_slice {
sub reslice_now {
my ($self) = @_;
- if ($self->{plater}) {
- $self->{plater}->reslice;
- }
+ $self->{plater}->reslice if $self->{plater};
}
sub repair_stl {
@@ -462,8 +480,9 @@ sub repair_stl {
my $input_file;
{
- my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
- my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:',
+ wxTheApp->{app_config}->get_last_dir, "",
+ &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
@@ -492,36 +511,25 @@ sub repair_stl {
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
}
-sub extra_variables {
- my $self = shift;
- my %extra_variables = ();
- $extra_variables{"${_}_preset"} = $self->{options_tabs}{$_}->get_current_preset->name
- for qw(print filament printer);
- return { %extra_variables };
-}
-
sub export_config {
my $self = shift;
-
- my $config = $self->config;
- eval {
- # validate configuration
- $config->validate;
- };
+ # Generate a cummulative configuration for the selected print, filaments and printer.
+ my $config = wxTheApp->{preset_bundle}->full_config();
+ # Validate the cummulative configuration.
+ eval { $config->validate; };
Slic3r::GUI::catch_error($self) and return;
-
- my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
- my $filename = $last_config ? basename($last_config) : "config.ini";
- my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
+ # Ask user for the file name for the config file.
+ my $dlg = Wx::FileDialog->new($self, 'Save configuration as:',
+ $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
+ $last_config ? basename($last_config) : "config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
- if ($dlg->ShowModal == wxID_OK) {
- my $file = $dlg->GetPath;
- $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
- wxTheApp->save_settings;
+ my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
+ $dlg->Destroy;
+ if (defined $file) {
+ wxTheApp->{app_config}->update_config_dir(dirname($file));
$last_config = $file;
$config->save($file);
}
- $dlg->Destroy;
}
# Load a config file containing a Print, Filament & Printer preset.
@@ -529,222 +537,139 @@ sub load_config_file {
my ($self, $file) = @_;
if (!$file) {
return unless $self->check_unsaved_changes;
- my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
- my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
- 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
+ $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
+ "config.ini",
+ 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK;
$file = $dlg->GetPaths;
$dlg->Destroy;
}
- for my $tab (values %{$self->{options_tabs}}) {
- # Dont proceed further if the config file cannot be loaded.
- return undef if ! $tab->load_config_file($file);
- }
- $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
- wxTheApp->save_settings;
+ eval { wxTheApp->{preset_bundle}->load_config_file($file); };
+ # Dont proceed further if the config file cannot be loaded.
+ return if Slic3r::GUI::catch_error($self);
+ $_->load_current_preset for (values %{$self->{options_tabs}});
+ wxTheApp->{app_config}->update_config_dir(dirname($file));
$last_config = $file;
}
sub export_configbundle {
- my $self = shift;
-
- eval {
- # validate current configuration in case it's dirty
- $self->config->validate;
- };
+ my ($self) = @_;
+ return unless $self->check_unsaved_changes;
+ # validate current configuration in case it's dirty
+ eval { wxTheApp->{preset_bundle}->full_config->validate; };
Slic3r::GUI::catch_error($self) and return;
-
- my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
- my $filename = "Slic3r_config_bundle.ini";
- my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename,
+ # Ask user for a file name.
+ my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:',
+ $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
+ "Slic3r_config_bundle.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
- if ($dlg->ShowModal == wxID_OK) {
- my $file = $dlg->GetPath;
- $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
- wxTheApp->save_settings;
-
- # leave default category empty to prevent the bundle from being parsed as a normal config file
- my $ini = { _ => {} };
- $ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter);
- $ini->{presets} = $Slic3r::GUI::Settings->{presets};
-
- foreach my $section (qw(print filament printer)) {
- my %presets = wxTheApp->presets($section);
- foreach my $preset_name (keys %presets) {
- my $config = Slic3r::Config->load($presets{$preset_name});
- $ini->{"$section:$preset_name"} = $config->as_ini->{_};
- }
- }
-
- Slic3r::Config->write_ini($file, $ini);
- }
+ my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
$dlg->Destroy;
+ if (defined $file) {
+ # Export the config bundle.
+ wxTheApp->{app_config}->update_config_dir(dirname($file));
+ eval { wxTheApp->{preset_bundle}->export_configbundle($file); };
+ Slic3r::GUI::catch_error($self) and return;
+ }
}
+# Loading a config bundle with an external file name used to be used
+# to auto-install a config bundle on a fresh user account,
+# but that behavior was not documented and likely buggy.
sub load_configbundle {
- my ($self, $file, $skip_no_id) = @_;
-
+ my ($self, $file) = @_;
+ return unless $self->check_unsaved_changes;
if (!$file) {
- my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
- my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
- &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
+ my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
+ $last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
+ "config.ini",
+ &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK;
$file = $dlg->GetPaths;
$dlg->Destroy;
}
- $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
- wxTheApp->save_settings;
-
- # load .ini file
- my $ini = Slic3r::Config->read_ini($file);
-
- if ($ini->{settings}) {
- $Slic3r::GUI::Settings->{_}{$_} = $ini->{settings}{$_} for keys %{$ini->{settings}};
- wxTheApp->save_settings;
- }
- if ($ini->{presets}) {
- $Slic3r::GUI::Settings->{presets} = $ini->{presets};
- wxTheApp->save_settings;
- }
+ wxTheApp->{app_config}->update_config_dir(dirname($file));
- my $imported = 0;
- INI_BLOCK: foreach my $ini_category (sort keys %$ini) {
- next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
- my ($section, $preset_name) = ($1, $2);
- my $config = Slic3r::Config->load_ini_hash($ini->{$ini_category});
- next if $skip_no_id && !$config->get($section . "_settings_id");
-
- {
- my %current_presets = Slic3r::GUI->presets($section);
- my %current_ids = map { $_ => 1 }
- grep $_,
- map Slic3r::Config->load($_)->get($section . "_settings_id"),
- values %current_presets;
- next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
- }
-
- $config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $section, $preset_name);
- Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name;
- $imported++;
- }
+ my $presets_imported = 0;
+ eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); };
+ Slic3r::GUI::catch_error($self) and return;
+
+ # Load the currently selected preset into the GUI, update the preset selection box.
foreach my $tab (values %{$self->{options_tabs}}) {
- $tab->load_presets;
+ $tab->load_current_preset;
}
- return if !$imported;
-
- my $message = sprintf "%d presets successfully imported.", $imported;
+ my $message = sprintf "%d presets successfully imported.", $presets_imported;
Slic3r::GUI::show_info($self, $message);
}
# Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
# Also update the platter with the new presets.
sub load_config {
- my $self = shift;
- my ($config) = @_;
-
- foreach my $tab (values %{$self->{options_tabs}}) {
- $tab->load_config($config);
- }
- if ($self->{plater}) {
- $self->{plater}->on_config_change($config);
- }
+ my ($self, $config) = @_;
+ $_->load_config($config) foreach values %{$self->{options_tabs}};
+ $self->{plater}->on_config_change($config) if $self->{plater};
}
sub config_wizard {
- my $self = shift;
-
+ my ($self) = @_;
+ # Exit wizard if there are unsaved changes and the user cancels the action.
return unless $self->check_unsaved_changes;
- if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
- for my $tab (values %{$self->{options_tabs}}) {
- $tab->select_default_preset;
- }
- $self->load_config($config);
- for my $tab (values %{$self->{options_tabs}}) {
- $tab->save_preset('My Settings');
+ # Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
+ my $directory = Slic3r::resources_dir() . "/profiles";
+ my @profiles = ();
+ if (opendir(DIR, Slic3r::encode_path($directory))) {
+ while (my $file = readdir(DIR)) {
+ if ($file =~ /\.ini$/) {
+ $file =~ s/\.ini$//;
+ push @profiles, Slic3r::decode_path($file);
+ }
}
+ closedir(DIR);
}
-}
-
-=head2 config
-
-This method collects all config values from the tabs and merges them into a single config object.
-
-=cut
-
-sub config {
- my $self = shift;
-
- return Slic3r::Config->new_from_defaults
- if !exists $self->{options_tabs}{print}
- || !exists $self->{options_tabs}{filament}
- || !exists $self->{options_tabs}{printer};
-
- # retrieve filament presets and build a single config object for them
- my $filament_config;
- if (!$self->{plater} || $self->{plater}->filament_presets == 1) {
- $filament_config = $self->{options_tabs}{filament}->config;
- } else {
- my $i = -1;
- foreach my $preset_idx ($self->{plater}->filament_presets) {
- $i++;
- my $config;
- if ($preset_idx == $self->{options_tabs}{filament}->current_preset) {
- # the selected preset for this extruder is the one in the tab
- # use the tab's config instead of the preset in case it is dirty
- # perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
- $config = $self->{options_tabs}{filament}->config;
- } else {
- my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
- $config = $self->{options_tabs}{filament}->get_preset_config($preset);
+ # Open the wizard.
+ if (my $config = Slic3r::GUI::ConfigWizard->new($self, \@profiles)->run) {
+ if (ref($config)) {
+ # Wizard returned a config. Add the config to each of the preset types.
+ for my $tab (values %{$self->{options_tabs}}) {
+ # Select the first visible preset, force.
+ $tab->select_preset(undef, 1);
}
- if (!$filament_config) {
- $filament_config = $config->clone;
- next;
+ # Load the config over the previously selected defaults.
+ $self->load_config($config);
+ for my $tab (values %{$self->{options_tabs}}) {
+ # Save the settings under a new name, select the name.
+ $tab->save_preset('My Settings');
}
- foreach my $opt_key (@{$config->get_keys}) {
- my $value = $filament_config->get($opt_key);
- next unless ref $value eq 'ARRAY';
- $value->[$i] = $config->get($opt_key)->[0];
- $filament_config->set($opt_key, $value);
+ } else {
+ # Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
+ eval { wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $config . '.ini'); };
+ Slic3r::GUI::catch_error($self) and return;
+ # Load the currently selected preset into the GUI, update the preset selection box.
+ foreach my $tab (values %{$self->{options_tabs}}) {
+ $tab->load_current_preset;
}
}
}
-
- my $config = Slic3r::Config->merge(
- Slic3r::Config->new_from_defaults,
- $self->{options_tabs}{print}->config,
- $self->{options_tabs}{printer}->config,
- $filament_config,
- );
-
- my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
- $config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
- for qw(perimeter infill solid_infill support_material support_material_interface);
-
- return $config;
-}
-
-sub filament_preset_names {
- my ($self) = @_;
- return map $self->{options_tabs}{filament}->get_preset($_)->name,
- $self->{plater}->filament_presets;
}
+# This is called when closing the application, when loading a config file or when starting the config wizard
+# to notify the user whether he is aware that some preset changes will be lost.
sub check_unsaved_changes {
my $self = shift;
my @dirty = ();
foreach my $tab (values %{$self->{options_tabs}}) {
- push @dirty, $tab->title if $tab->is_dirty;
+ push @dirty, $tab->title if $tab->{presets}->current_is_dirty;
}
if (@dirty) {
my $titles = join ', ', @dirty;
my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?",
'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
- return ($confirm->ShowModal == wxID_YES);
+ return $confirm->ShowModal == wxID_YES;
}
return 1;
@@ -765,21 +690,18 @@ sub select_view {
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon) = @_;
-
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description);
$self->_set_menu_item_icon($item, $icon);
-
EVT_MENU($self, $id, $cb);
return $item;
}
sub _set_menu_item_icon {
my ($self, $menuItem, $icon) = @_;
-
# SetBitmap was not available on OS X before Wx 0.9927
if ($icon && $menuItem->can('SetBitmap')) {
- $menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
+ $menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
}
}
@@ -787,8 +709,11 @@ sub _set_menu_item_icon {
# Update the UI based on the current preferences.
sub update_ui_from_settings {
my ($self) = @_;
- $self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing});
+ $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/Notifier.pm b/lib/Slic3r/GUI/Notifier.pm
index eb548d014..54afffae0 100644
--- a/lib/Slic3r/GUI/Notifier.pm
+++ b/lib/Slic3r/GUI/Notifier.pm
@@ -6,7 +6,7 @@ use Moo;
has 'growler' => (is => 'rw');
-my $icon = $Slic3r::var->("Slic3r.png");
+my $icon = Slic3r::var("Slic3r.png");
sub BUILD {
my ($self) = @_;
diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm
index 531e13a5d..4ef2ce2ca 100644
--- a/lib/Slic3r/GUI/OptionsGroup/Field.pm
+++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm
@@ -124,6 +124,10 @@ sub BUILD {
});
}
+sub get_value {
+ my ($self) = @_;
+ return $self->wxWindow->GetValue ? 1 : 0;
+}
package Slic3r::GUI::OptionsGroup::Field::SpinCtrl;
use Moo;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 0f56da7ca..f687dcec4 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -46,15 +46,14 @@ use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
my $PreventListEvents = 0;
sub new {
- my $class = shift;
- my ($parent) = @_;
+ my ($class, $parent) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
- $self->{config} = Slic3r::Config->new_from_defaults(qw(
+ $self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
serial_port serial_speed octoprint_host octoprint_apikey
nozzle_diameter single_extruder_multi_material
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
- ));
+ )]);
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
$self->{model} = Slic3r::Model->new;
# C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
@@ -64,12 +63,7 @@ sub new {
$self->{print}->set_status_cb(sub {
my ($percent, $message) = @_;
-
- if ($Slic3r::have_threads) {
- Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message])));
- } else {
- $self->on_progress_event($percent, $message);
- }
+ Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message])));
});
# Initialize preview notebook
@@ -105,6 +99,7 @@ sub new {
$self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click);
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
+ $self->{canvas3D}->set_on_arrange(sub { $self->arrange });
$self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') });
$self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') });
$self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
@@ -120,7 +115,7 @@ sub new {
$self->GetFrame->{options_tabs}{print}->load_config($cfg);
});
$self->{canvas3D}->set_on_model_update(sub {
- if ($Slic3r::GUI::Settings->{_}{background_processing}) {
+ if (wxTheApp->{app_config}->get("background_processing")) {
$self->schedule_background_process;
} else {
# Hide the print info box, it is no more valid.
@@ -166,22 +161,22 @@ sub new {
if (!&Wx::wxMSW) {
Wx::ToolTip::Enable(1);
$self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
- $self->{htoolbar}->AddTool(TB_ADD, "Add…", Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new($Slic3r::var->("cross.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new($Slic3r::var->("bricks.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_ADD, "Add…", Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new($Slic3r::var->("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new($Slic3r::var->("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new($Slic3r::var->("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
- $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
- $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
+ $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
+ $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
} else {
my %tbar_buttons = (
add => "Add…",
@@ -256,7 +251,7 @@ sub new {
settings cog.png
);
for (grep $self->{"btn_$_"}, keys %icons) {
- $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icons{$_}), wxBITMAP_TYPE_PNG));
+ $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new(Slic3r::var($icons{$_}), wxBITMAP_TYPE_PNG));
}
$self->selection_changed(0);
$self->object_list_changed;
@@ -331,7 +326,7 @@ sub new {
$self->on_process_completed($event->GetData);
});
- if ($Slic3r::have_threads) {
+ {
my $timer_id = Wx::NewId();
$self->{apply_config_timer} = Wx::Timer->new($self, $timer_id);
EVT_TIMER($self, $timer_id, sub {
@@ -366,20 +361,17 @@ sub new {
# once a printer preset with multiple extruders is activated.
# $self->{preset_choosers}{$group}[$idx]
$self->{preset_choosers} = {};
- # Boolean indicating whether the '- default -' preset is shown by the combo box.
- $self->{preset_choosers_default_suppressed} = {};
for my $group (qw(print filament printer)) {
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
$text->SetFont($Slic3r::GUI::small_font);
my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } );
$self->{preset_choosers}{$group} = [$choice];
- $self->{preset_choosers_default_suppressed}{$group} = 0;
# setup the listener
EVT_COMBOBOX($choice, $choice, sub {
my ($choice) = @_;
wxTheApp->CallAfter(sub {
- $self->_on_select_preset($group, $choice);
+ $self->_on_select_preset($group, $choice, 0);
});
});
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
@@ -414,7 +406,7 @@ sub new {
$self->{"object_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
$self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
if ($field eq 'manifold') {
- $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new($Slic3r::var->("error.png"), wxBITMAP_TYPE_PNG));
+ $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
$self->{object_info_manifold_warning_icon}->Hide;
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -438,6 +430,7 @@ sub new {
$grid_sizer->AddGrowableCol(3, 1);
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
my @info = (
+ fil_m => "Used Filament (m)",
fil_mm3 => "Used Filament (mm^3)",
fil_g => "Used Filament (g)",
cost => "Cost",
@@ -453,7 +446,6 @@ sub new {
$self->{"print_info_$field"}->SetFont($Slic3r::GUI::small_font);
$grid_sizer->Add($self->{"print_info_$field"}, 0);
}
-
}
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -505,30 +497,26 @@ sub on_select_preset {
$self->{on_select_preset} = $cb;
}
+# Called from the platter combo boxes selecting the active print, filament or printer.
sub _on_select_preset {
- my $self = shift;
- my ($group, $choice) = @_;
-
+ my ($self, $group, $choice, $idx) = @_;
# If user changed a filament preset and the selected machine is equipped with multiple extruders,
# there are multiple filament selection combo boxes shown at the platter. In that case
# don't propagate the filament selection changes to the tab.
- my $default_suppressed = $self->{preset_choosers_default_suppressed}{$group};
+ if ($group eq 'filament') {
+ wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
+ }
if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
- # Indices of the filaments selected.
- my @filament_presets = $self->filament_presets;
- $Slic3r::GUI::Settings->{presets}{filament} = $choice->GetString($filament_presets[0] - $default_suppressed) . ".ini";
- $Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_] - $default_suppressed)
- for 1 .. $#filament_presets;
- wxTheApp->save_settings;
- $self->update_filament_colors_preview($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, scalar($choice->GetSelection) + $default_suppressed)
+ $self->{on_select_preset}->($group, $choice->GetStringSelection)
if $self->{on_select_preset};
}
-
+ # Synchronize config.ini with the current selections.
+ wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
# get new config and generate on_config_change() event for updating plater and other things
- $self->on_config_change($self->GetFrame->config);
+ $self->on_config_change(wxTheApp->{preset_bundle}->full_config);
}
sub on_layer_editing_toggled {
@@ -558,8 +546,8 @@ sub GetFrame {
sub update_ui_from_settings
{
my ($self) = @_;
- if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! $Slic3r::GUI::Settings->{_}{background_processing})) {
- $self->{buttons_sizer}->Show($self->{btn_reslice}, ! $Slic3r::GUI::Settings->{_}{background_processing});
+ if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! wxTheApp->{app_config}->get("background_processing"))) {
+ $self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing"));
$self->{buttons_sizer}->Layout;
}
}
@@ -573,128 +561,41 @@ sub update_ui_from_settings
# For Print settings and Printer, synchronize the selection index with their tabs.
# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection.
sub update_presets {
- my $self = shift;
- # $presets: one of qw(print filament printer)
- # $selected: index of the selected preset in the array. This may not correspond
- # with the index of selection in the UI element, where not all items are displayed.
- my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_;
-
- my @choosers = @{ $self->{preset_choosers}{$group} };
- my $choice_idx = 0;
- foreach my $choice (@choosers) {
- if ($group eq 'filament' && @choosers > 1) {
- # if we have more than one filament chooser, keep our selection
- # instead of importing the one from the tab
- $selected = $choice->GetSelection + $self->{preset_choosers_default_suppressed}{$group};
- $is_dirty = 0;
- }
- $choice->Clear;
- foreach my $preset (@$presets) {
- next if ($preset->default && $default_suppressed);
- my $bitmap;
- if ($group eq 'filament') {
- $bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG);
- } elsif ($group eq 'print') {
- $bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG);
- } elsif ($group eq 'printer') {
- $bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG);
- }
- $choice->AppendString($preset->name, $bitmap);
- }
-
- if ($selected <= $#$presets) {
- my $idx = $selected - $default_suppressed;
- if ($idx >= 0) {
- if ($is_dirty) {
- $choice->SetString($idx, $choice->GetString($idx) . " (modified)");
- }
- # call SetSelection() only after SetString() otherwise the new string
- # won't be picked up as the visible string
- $choice->SetSelection($idx);
- }
+ # $group: one of qw(print filament printer)
+ # $presets: PresetCollection
+ my ($self, $group, $presets) = @_;
+ my @choosers = @{$self->{preset_choosers}{$group}};
+ if ($group eq 'filament') {
+ my $choice_idx = 0;
+ if (int(@choosers) == 1) {
+ # Single filament printer, synchronize the filament presets.
+ wxTheApp->{preset_bundle}->set_filament_preset(0, wxTheApp->{preset_bundle}->filament->get_selected_preset->name);
}
- $choice_idx += 1;
- }
-
- $self->{preset_choosers_default_suppressed}{$group} = $default_suppressed;
-
- wxTheApp->CallAfter(sub { $self->update_filament_colors_preview }) if $group eq 'filament' || $group eq 'printer';
-}
-
-# Update the color icon in front of each filament selection on the platter.
-# If the extruder has a preview color assigned, apply the extruder color to the active selection.
-# Always apply the filament color to the non-active selections.
-sub update_filament_colors_preview {
- my ($self, $extruder_idx) = shift;
-
- my @choosers = @{$self->{preset_choosers}{filament}};
-
- if (ref $extruder_idx) {
- # $extruder_idx is the chooser.
- foreach my $chooser (@choosers) {
- if ($extruder_idx == $chooser) {
- $extruder_idx = $chooser;
- last;
- }
+ foreach my $choice (@choosers) {
+ wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice);
+ $choice_idx += 1;
}
- }
-
- my @extruder_colors = @{$self->{config}->extruder_colour};
-
- my @extruder_list;
- if (defined $extruder_idx) {
- @extruder_list = ($extruder_idx);
- } else {
- # Collect extruder indices.
- @extruder_list = (0..$#extruder_colors);
- }
-
- my $filament_tab = $self->GetFrame->{options_tabs}{filament};
- my $presets = $filament_tab->{presets};
- my $default_suppressed = $filament_tab->{default_suppressed};
-
- foreach my $extruder_idx (@extruder_list) {
- my $chooser = $choosers[$extruder_idx];
- my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx];
- my $preset_idx = 0;
- my $selection_idx = $chooser->GetSelection;
- foreach my $preset (@$presets) {
- my $bitmap;
- if ($preset->default) {
- next if $default_suppressed;
- } else {
- # Assign an extruder color to the selected item if the extruder color is defined.
- my $filament_rgb = $preset->config(['filament_colour'])->filament_colour->[0];
- my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb;
- $filament_rgb =~ s/^#//;
- $extruder_rgb =~ s/^#//;
- my $image = Wx::Image->new(24,16);
- if ($filament_rgb ne $extruder_rgb) {
- my @rgb = unpack 'C*', pack 'H*', $extruder_rgb;
- $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb);
- @rgb = unpack 'C*', pack 'H*', $filament_rgb;
- $image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb);
- } else {
- my @rgb = unpack 'C*', pack 'H*', $filament_rgb;
- $image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb);
- }
- $bitmap = Wx::Bitmap->new($image);
- }
- $chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap;
- $preset_idx += 1;
+ } elsif ($group eq 'print') {
+ wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]);
+ } elsif ($group eq 'printer') {
+ # 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($choice_idx, $choice);
+ $choice_idx += 1;
}
}
-}
-
-# Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes.
-sub filament_presets {
- my $self = shift;
- # force scalar context for GetSelection() as it's context-aware
- return map scalar($_->GetSelection) + $self->{preset_choosers_default_suppressed}{filament}, @{ $self->{preset_choosers}{filament} };
+ # Synchronize config.ini with the current selections.
+ wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
}
sub add {
- my $self = shift;
+ my ($self) = @_;
my @input_files = wxTheApp->open_model($self);
$self->load_files(\@input_files);
}
@@ -759,8 +660,7 @@ sub load_files {
}
# Note the current directory for the file open dialog.
- $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_files->[-1]);
- wxTheApp->save_settings;
+ wxTheApp->{app_config}->update_skein_dir(dirname($input_files->[-1]));
$process_dialog->Destroy;
$self->statusbar->SetStatusText("Loaded " . join(',', @loaded_files));
@@ -808,7 +708,7 @@ sub load_model_objects {
}
# if user turned autocentering off, automatic arranging would disappoint them
- if (!$Slic3r::GUI::Settings->{_}{autocenter}) {
+ if (! wxTheApp->{app_config}->get("autocenter")) {
$need_arrange = 0;
}
@@ -921,7 +821,7 @@ sub increase {
# only autoarrange if user has autocentering enabled
$self->stop_background_process;
- if ($Slic3r::GUI::Settings->{_}{autocenter}) {
+ if (wxTheApp->{app_config}->get("autocenter")) {
$self->arrange;
} else {
$self->update;
@@ -1164,7 +1064,7 @@ sub arrange {
$self->pause_background_process;
my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
- my $success = $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb);
+ my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed
@@ -1225,7 +1125,7 @@ sub async_apply_config {
$self->pause_background_process;
# apply new config
- my $invalidated = $self->{print}->apply_config($self->GetFrame->config);
+ my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config);
# Just redraw the 3D canvas without reloading the scene.
# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled);
@@ -1234,7 +1134,7 @@ sub async_apply_config {
# Hide the slicing results if the current slicing status is no more valid.
$self->{"print_info_box_show"}->(0) if $invalidated;
- if ($Slic3r::GUI::Settings->{_}{background_processing}) {
+ if (wxTheApp->{app_config}->get("background_processing")) {
if ($invalidated) {
# kill current thread if any
$self->stop_background_process;
@@ -1256,7 +1156,6 @@ sub async_apply_config {
sub start_background_process {
my ($self) = @_;
- return if !$Slic3r::have_threads;
return if !@{$self->{objects}};
return if $self->{process_thread};
@@ -1267,7 +1166,7 @@ sub start_background_process {
# don't start process thread if config is not valid
eval {
# this will throw errors if config is not valid
- $self->GetFrame->config->validate;
+ wxTheApp->{preset_bundle}->full_config->validate;
$self->{print}->validate;
};
if ($@) {
@@ -1275,11 +1174,8 @@ sub start_background_process {
return;
}
- # apply extra variables
- {
- my $extra = $self->GetFrame->extra_variables;
- $self->{print}->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
- }
+ # Copy the names of active presets into the placeholder parser.
+ wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser);
# start thread
@_ = ();
@@ -1350,7 +1246,7 @@ sub reslice {
# explicitly cancel a previous thread and start a new one.
my ($self) = @_;
# Don't reslice if export of G-code or sending to OctoPrint is running.
- if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) {
+ if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) {
# Stop the background processing threads, stop the async update timer.
$self->stop_background_process;
# Rather perform one additional unnecessary update of the print object instead of skipping a pending async update.
@@ -1379,66 +1275,57 @@ sub export_gcode {
# (we assume that if it is running, config is valid)
eval {
# this will throw errors if config is not valid
- $self->GetFrame->config->validate;
+ wxTheApp->{preset_bundle}->full_config->validate;
$self->{print}->validate;
};
Slic3r::GUI::catch_error($self) and return;
# apply config and validate print
- my $config = $self->GetFrame->config;
+ my $config = wxTheApp->{preset_bundle}->full_config;
eval {
# this will throw errors if config is not valid
$config->validate;
$self->{print}->apply_config($config);
$self->{print}->validate;
};
- if (!$Slic3r::have_threads) {
- Slic3r::GUI::catch_error($self) and return;
- }
+ Slic3r::GUI::catch_error($self) and return;
# select output file
if ($output_file) {
- $self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file);
+ $self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) };
+ Slic3r::GUI::catch_error($self) and return;
} else {
- my $default_output_file = $self->{print}->output_filepath($main::opt{output} // '');
- my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)),
+ my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
+ Slic3r::GUI::catch_error($self) and return;
+ my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
+ wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return;
}
my $path = $dlg->GetPath;
- $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path);
- wxTheApp->save_settings;
+ wxTheApp->{app_config}->update_last_output_dir(dirname($path));
$self->{export_gcode_output_file} = $path;
$dlg->Destroy;
}
$self->statusbar->StartBusy;
- if ($Slic3r::have_threads) {
- $self->statusbar->SetCancelCallback(sub {
- $self->stop_background_process;
- $self->statusbar->SetStatusText("Export cancelled");
- $self->{export_gcode_output_file} = undef;
- $self->{send_gcode_file} = undef;
-
- # this updates buttons status
- $self->object_list_changed;
- });
+ $self->statusbar->SetCancelCallback(sub {
+ $self->stop_background_process;
+ $self->statusbar->SetStatusText("Export cancelled");
+ $self->{export_gcode_output_file} = undef;
+ $self->{send_gcode_file} = undef;
- # start background process, whose completion event handler
- # will detect $self->{export_gcode_output_file} and proceed with export
- $self->start_background_process;
- } else {
- eval {
- $self->{print}->process;
- $self->{print}->export_gcode(output_file => $self->{export_gcode_output_file});
- };
- my $result = !Slic3r::GUI::catch_error($self);
- $self->on_export_completed($result);
- }
+ # this updates buttons status
+ $self->object_list_changed;
+ });
+
+ # start background process, whose completion event handler
+ # will detect $self->{export_gcode_output_file} and proceed with export
+ $self->start_background_process;
# this updates buttons status
$self->object_list_changed;
@@ -1542,6 +1429,7 @@ sub on_export_completed {
$self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight));
$self->{"print_info_fil_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume));
$self->{"print_info_time"}->SetLabel(sprintf("%.2f" , $self->{print}->estimated_print_time));
+ $self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000));
$self->{"print_info_box_show"}->(1);
# this updates buttons status
@@ -1551,14 +1439,12 @@ sub on_export_completed {
sub do_print {
my ($self) = @_;
- my $printer_tab = $self->GetFrame->{options_tabs}{printer};
- my $printer_name = $printer_tab->get_current_preset->name;
-
my $controller = $self->GetFrame->{controller};
- my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config);
+ my $printer_preset = wxTheApp->{preset_bundle}->printer->get_edited_preset;
+ my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config);
my $filament_stats = $self->{print}->filament_stats;
- my @filament_names = $self->GetFrame->filament_preset_names;
+ my @filament_names = wxTheApp->{preset_bundle}->filament_presets;
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats };
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
@@ -1599,11 +1485,11 @@ sub send_gcode {
}
sub export_stl {
- my $self = shift;
-
+ my ($self) = @_;
return if !@{$self->{objects}};
-
+ # Ask user for a file name to write into.
my $output_file = $self->_get_export_file('STL') or return;
+ # Store a binary STL.
$self->{model}->store_stl($output_file, 1);
$self->statusbar->SetStatusText("STL file exported to $output_file");
}
@@ -1615,6 +1501,7 @@ sub reload_from_disk {
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
+ #FIXME convert to local file encoding
return if !$model_object->input_file
|| !-e $model_object->input_file;
@@ -1625,59 +1512,55 @@ sub reload_from_disk {
my $o = $self->{model}->objects->[$new_obj_idx];
$o->clear_instances;
$o->add_instance($_) for @{$model_object->instances};
+ #$o->invalidate_bounding_box;
if ($o->volumes_count == $model_object->volumes_count) {
for my $i (0..($o->volumes_count-1)) {
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
}
}
+ #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
}
$self->remove($obj_idx);
}
sub export_object_stl {
- my $self = shift;
-
+ my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
-
my $model_object = $self->{model}->objects->[$obj_idx];
-
+ # Ask user for a file name to write into.
my $output_file = $self->_get_export_file('STL') or return;
$model_object->mesh->write_binary($output_file);
$self->statusbar->SetStatusText("STL file exported to $output_file");
}
sub export_amf {
- my $self = shift;
-
+ my ($self) = @_;
return if !@{$self->{objects}};
-
+ # Ask user for a file name to write into.
my $output_file = $self->_get_export_file('AMF') or return;
$self->{model}->store_amf($output_file);
$self->statusbar->SetStatusText("AMF file exported to $output_file");
}
+# Ask user to select an output file for a given file format (STl, AMF, 3MF).
+# Propose a default file name based on the 'output_filename_format' configuration value.
sub _get_export_file {
- my $self = shift;
- my ($format) = @_;
-
+ my ($self, $format) = @_;
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
-
- my $output_file = $main::opt{output};
- {
- $output_file = $self->{print}->output_filepath($output_file);
- $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
- my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
- basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
- if ($dlg->ShowModal != wxID_OK) {
- $dlg->Destroy;
- return undef;
- }
- $output_file = $dlg->GetPath;
+ my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
+ Slic3r::GUI::catch_error($self) and return undef;
+ $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
+ my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
+ basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+ if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
+ return undef;
}
+ $output_file = $dlg->GetPath;
+ $dlg->Destroy;
return $output_file;
}
@@ -1691,7 +1574,7 @@ sub reset_thumbnail {
sub update {
my ($self, $force_autocenter) = @_;
- if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
+ if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) {
$self->{model}->center_instances_around_point($self->bed_centerf);
}
@@ -1714,11 +1597,13 @@ sub update {
}
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
+# Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly
+# and some reasonable default has to be selected for the additional extruders.
sub on_extruders_change {
my ($self, $num_extruders) = @_;
-
my $choices = $self->{preset_choosers}{filament};
- while (@$choices < $num_extruders) {
+
+ while (int(@$choices) < $num_extruders) {
# copy strings from first choice
my @presets = $choices->[0]->GetStrings;
@@ -1727,25 +1612,20 @@ sub on_extruders_change {
my $extruder_idx = scalar @$choices;
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } );
push @$choices, $choice;
-
# copy icons from first choice
$choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
-
# insert new choice into sizer
$self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0);
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
-
# setup the listener
EVT_COMBOBOX($choice, $choice, sub {
my ($choice) = @_;
wxTheApp->CallAfter(sub {
- $self->_on_select_preset('filament', $choice);
+ $self->_on_select_preset('filament', $choice, $extruder_idx);
});
});
-
# initialize selection
- my $i = first { $choice->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets;
- $choice->SetSelection($i || 0);
+ wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $choice);
}
# remove unused choices if any
@@ -1759,8 +1639,7 @@ sub on_extruders_change {
}
sub on_config_change {
- my $self = shift;
- my ($config) = @_;
+ my ($self, $config) = @_;
my $update_scheduled;
foreach my $opt_key (@{$self->{config}->diff($config)}) {
@@ -1817,7 +1696,6 @@ sub on_config_change {
sub list_item_deselected {
my ($self, $event) = @_;
return if $PreventListEvents;
-
if ($self->{list}->GetFirstSelected == -1) {
$self->select_object(undef);
$self->{canvas}->Refresh;
@@ -1829,7 +1707,6 @@ sub list_item_deselected {
sub list_item_selected {
my ($self, $event) = @_;
return if $PreventListEvents;
-
my $obj_idx = $event->GetIndex;
$self->select_object($obj_idx);
$self->{canvas}->Refresh;
@@ -1861,19 +1738,18 @@ sub filament_color_box_lmouse_down
my $dialog = Wx::ColourDialog->new($self->GetFrame, $data);
if ($dialog->ShowModal == wxID_OK) {
my $cfg = Slic3r::Config->new;
- my $colors = $self->GetFrame->config->get('extruder_colour');
+ my $colors = wxTheApp->{preset_bundle}->full_config->get('extruder_colour');
$colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX);
$cfg->set('extruder_colour', $colors);
$self->GetFrame->{options_tabs}{printer}->load_config($cfg);
- $self->update_filament_colors_preview($extruder_idx);
+ wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $combobox);
}
$dialog->Destroy();
}
}
sub object_cut_dialog {
- my $self = shift;
- my ($obj_idx) = @_;
+ my ($self, $obj_idx) = @_;
if (!defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
@@ -1898,23 +1774,20 @@ sub object_cut_dialog {
}
sub object_settings_dialog {
- my $self = shift;
- my ($obj_idx) = @_;
-
- if (!defined $obj_idx) {
- ($obj_idx, undef) = $self->selected_object;
- }
+ my ($self, $obj_idx) = @_;
+ ($obj_idx, undef) = $self->selected_object if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
# validate config before opening the settings dialog because
# that dialog can't be closed if validation fails, but user
# can't fix any error which is outside that dialog
- return unless $self->validate_config;
+ eval { wxTheApp->{preset_bundle}->full_config->validate; };
+ return if Slic3r::GUI::catch_error($_[0]);
my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self,
object => $self->{objects}[$obj_idx],
model_object => $model_object,
- config => $self->GetFrame->config,
+ config => wxTheApp->{preset_bundle}->full_config,
);
$self->pause_background_process;
$dlg->ShowModal;
@@ -1965,9 +1838,9 @@ sub object_list_changed {
for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
}
+# Selection of an active 3D object changed.
sub selection_changed {
- my $self = shift;
-
+ my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
my $have_sel = defined $obj_idx;
@@ -2028,7 +1901,6 @@ sub select_object {
$_->selected(0) for @{ $self->{objects} };
if (defined $obj_idx) {
$self->{objects}->[$obj_idx]->selected(1);
-
# We use this flag to avoid circular event handling
# Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows,
# whose event handler calls this method again and again and again
@@ -2042,26 +1914,13 @@ sub select_object {
}
sub selected_object {
- my $self = shift;
-
+ my ($self) = @_;
my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} };
- return undef if !defined $obj_idx;
- return ($obj_idx, $self->{objects}[$obj_idx]),
-}
-
-sub validate_config {
- my $self = shift;
-
- eval {
- $self->GetFrame->config->validate;
- };
- return 0 if Slic3r::GUI::catch_error($self);
- return 1;
+ return defined $obj_idx ? ($obj_idx, $self->{objects}[$obj_idx]) : undef;
}
sub statusbar {
- my $self = shift;
- return $self->GetFrame->{statusbar};
+ return $_[0]->GetFrame->{statusbar};
}
sub object_menu {
@@ -2069,23 +1928,24 @@ sub object_menu {
my $frame = $self->GetFrame;
my $menu = Wx::Menu->new;
- $frame->_append_menu_item($menu, "Delete\t\xA0Del", 'Remove the selected object', sub {
+ my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
+ $frame->_append_menu_item($menu, $accel->('Delete', 'Del'), 'Remove the selected object', sub {
$self->remove;
}, undef, 'brick_delete.png');
- $frame->_append_menu_item($menu, "Increase copies\t\xA0+", 'Place one more copy of the selected object', sub {
+ $frame->_append_menu_item($menu, $accel->('Increase copies', '+'), 'Place one more copy of the selected object', sub {
$self->increase;
}, undef, 'add.png');
- $frame->_append_menu_item($menu, "Decrease copies\t\xA0-", 'Remove one copy of the selected object', sub {
+ $frame->_append_menu_item($menu, $accel->('Decrease copies', '-'), 'Remove one copy of the selected object', sub {
$self->decrease;
}, undef, 'delete.png');
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
$self->set_number_of_copies;
}, undef, 'textfield.png');
$menu->AppendSeparator();
- $frame->_append_menu_item($menu, "Rotate 45° clockwise\t\xA0l", 'Rotate the selected object by 45° clockwise', sub {
+ $frame->_append_menu_item($menu, $accel->('Rotate 45° clockwise', 'l'), 'Rotate the selected object by 45° clockwise', sub {
$self->rotate(-45, Z, 'relative');
}, undef, 'arrow_rotate_clockwise.png');
- $frame->_append_menu_item($menu, "Rotate 45° counter-clockwise\t\xA0r", 'Rotate the selected object by 45° counter-clockwise', sub {
+ $frame->_append_menu_item($menu, $accel->('Rotate 45° counter-clockwise', 'r'), 'Rotate the selected object by 45° counter-clockwise', sub {
$self->rotate(+45, Z, 'relative');
}, undef, 'arrow_rotate_anticlockwise.png');
@@ -2118,7 +1978,7 @@ sub object_menu {
my $scaleMenu = Wx::Menu->new;
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
- $frame->_append_menu_item($scaleMenu, "Uniformly…\t\xA0s", 'Scale the selected object along the XYZ axes', sub {
+ $frame->_append_menu_item($scaleMenu, $accel->('Uniformly…', 's'), 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef);
});
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
@@ -2187,24 +2047,19 @@ use Wx::DND;
use base 'Wx::FileDropTarget';
sub new {
- my $class = shift;
- my ($window) = @_;
+ my ($class, $window) = @_;
my $self = $class->SUPER::new;
$self->{window} = $window;
return $self;
}
sub OnDropFiles {
- my $self = shift;
- my ($x, $y, $filenames) = @_;
-
+ my ($self, $x, $y, $filenames) = @_;
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@_ = ();
-
# only accept STL, OBJ and AMF files
return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames;
-
$self->{window}->load_files($filenames);
}
@@ -2220,10 +2075,8 @@ has 'selected' => (is => 'rw', default => sub { 0 });
sub make_thumbnail {
my ($self, $model, $obj_idx) = @_;
-
# make method idempotent
$self->thumbnail->clear;
-
# raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
my $mesh = $model->objects->[$obj_idx]->raw_mesh;
#FIXME The "correct" variant could be extremely slow.
@@ -2239,7 +2092,6 @@ sub make_thumbnail {
my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
$self->thumbnail->append($convex_hull);
# }
-
return $self->thumbnail;
}
diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm
index 089b25947..a1e077591 100644
--- a/lib/Slic3r/GUI/Plater/2D.pm
+++ b/lib/Slic3r/GUI/Plater/2D.pm
@@ -9,7 +9,7 @@ use utf8;
use List::Util qw(min max first);
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl);
-use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
+use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
use base 'Wx::Panel';
@@ -102,7 +102,7 @@ sub repaint {
}
# draw print center
- if (@{$self->{objects}} && $Slic3r::GUI::Settings->{_}{autocenter}) {
+ if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) {
my $center = $self->unscaled_point_to_pixel($self->{print_center});
$dc->SetPen($self->{print_center_pen});
$dc->DrawLine($center->[X], 0, $center->[X], $size[Y]);
@@ -197,7 +197,6 @@ sub repaint {
sub mouse_event {
my ($self, $event) = @_;
-
my $pos = $event->GetPosition;
my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]]
if ($event->ButtonDown) {
@@ -257,7 +256,7 @@ sub mouse_event {
}
sub update_bed_size {
- my $self = shift;
+ my ($self) = @_;
# when the canvas is not rendered yet, its GetSize() method returns 0,0
my $canvas_size = $self->GetSize;
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 503a3d159..1c123e741 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -9,7 +9,7 @@ use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
use base qw(Slic3r::GUI::3DScene Class::Accessor);
__PACKAGE__->mk_accessors(qw(
- on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
+ on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
on_remove_object on_increase_objects on_decrease_objects));
sub new {
@@ -88,7 +88,9 @@ sub new {
$event->Skip;
} else {
my $key = $event->GetKeyCode;
- if ($key == ord('l')) {
+ if ($key == ord('a')) {
+ $self->on_arrange->() if $self->on_arrange;
+ } elsif ($key == ord('l')) {
$self->on_rotate_object_left->() if $self->on_rotate_object_left;
} elsif ($key == ord('r')) {
$self->on_rotate_object_right->() if $self->on_rotate_object_right;
@@ -122,6 +124,11 @@ sub set_on_right_click {
$self->on_right_click($cb);
}
+sub set_on_arrange {
+ my ($self, $cb) = @_;
+ $self->on_arrange($cb);
+}
+
sub set_on_rotate_object_left {
my ($self, $cb) = @_;
$self->on_rotate_object_left($cb);
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 707929e33..8a8e6064c 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -42,9 +42,9 @@ sub new {
{
$self->{tree_icons} = Wx::ImageList->new(16, 16, 1);
$tree->AssignImageList($self->{tree_icons});
- $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
- $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
- $self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
+ $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
+ $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
+ $self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
$tree->SetPlData($rootId, { type => 'object' });
@@ -58,13 +58,13 @@ sub new {
$self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
$self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
- $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_split}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG));
- $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG));
+ $self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG));
# buttons sizer
my $buttons_sizer = Wx::GridSizer->new(2, 3);
@@ -312,7 +312,7 @@ sub selection_changed {
$config = $self->{model_object}->config;
}
# get default values
- my $default_config = Slic3r::Config->new_from_defaults(@opt_keys);
+ my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
# append default extruder
push @opt_keys, 'extruder';
@@ -490,12 +490,12 @@ sub CanClose {
# validate options before allowing user to dismiss the dialog
# the validate method only works on full configs so we have
# to merge our settings with the default ones
- my $config = Slic3r::Config->merge($self->GetParent->GetParent->GetParent->GetParent->GetParent->config, $self->model_object->config);
+ my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone;
eval {
+ $config->apply($self->model_object->config);
$config->validate;
};
- return 0 if Slic3r::GUI::catch_error($self);
- return 1;
+ return ! Slic3r::GUI::catch_error($self);
}
sub PartsChanged {
diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
index 2f8b8ce73..3b10ed99f 100644
--- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm
@@ -46,7 +46,7 @@ sub new {
# option selector
{
# create the button
- my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
+ my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
@@ -146,7 +146,7 @@ sub update_optgroup {
# disallow deleting fixed options
return undef if $self->{fixed_options}{$opt_key};
- my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
+ my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
EVT_BUTTON($self, $btn, sub {
$self->{config}->erase($opt_key);
diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm
index b19d623cd..d20ccb53f 100644
--- a/lib/Slic3r/GUI/Preferences.pm
+++ b/lib/Slic3r/GUI/Preferences.pm
@@ -10,6 +10,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, "Preferences", wxDefaultPosition, wxDefaultSize);
$self->{values} = {};
+ my $app_config = wxTheApp->{app_config};
my $optgroup;
$optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $self,
@@ -25,7 +26,7 @@ sub new {
# type => 'bool',
# label => 'Check for updates',
# tooltip => 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.',
-# default => $Slic3r::GUI::Settings->{_}{version_check} // 1,
+# default => $app_config->get("version_check") // 1,
# readonly => !wxTheApp->have_version_check,
# ));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
@@ -33,36 +34,43 @@ sub new {
type => 'bool',
label => 'Remember output directory',
tooltip => 'If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files.',
- default => $Slic3r::GUI::Settings->{_}{remember_output_path},
+ default => $app_config->get("remember_output_path"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'autocenter',
type => 'bool',
label => 'Auto-center parts',
tooltip => 'If this is enabled, Slic3r will auto-center objects around the print bed center.',
- default => $Slic3r::GUI::Settings->{_}{autocenter},
+ default => $app_config->get("autocenter"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'background_processing',
type => 'bool',
label => 'Background processing',
tooltip => 'If this is enabled, Slic3r will pre-process objects as soon as they\'re loaded in order to save time when exporting G-code.',
- default => $Slic3r::GUI::Settings->{_}{background_processing},
- readonly => !$Slic3r::have_threads,
+ default => $app_config->get("background_processing"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_controller',
type => 'bool',
label => 'Disable USB/serial connection',
tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.',
- default => $Slic3r::GUI::Settings->{_}{no_controller},
+ default => $app_config->get("no_controller"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_defaults',
type => 'bool',
label => 'Suppress "- default -" presets',
tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.',
- default => $Slic3r::GUI::Settings->{_}{no_defaults},
+ 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);
@@ -79,14 +87,15 @@ sub new {
}
sub _accept {
- my $self = shift;
+ my ($self) = @_;
- if (defined($self->{values}{no_controller})) {
+ if (defined($self->{values}{no_controller}) ||
+ defined($self->{values}{no_defaults})) {
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
}
- $Slic3r::GUI::Settings->{_}{$_} = $self->{values}{$_} for keys %{$self->{values}};
- wxTheApp->save_settings;
+ my $app_config = wxTheApp->{app_config};
+ $app_config->set($_, $self->{values}{$_}) for keys %{$self->{values}};
$self->EndModal(wxID_OK);
$self->Close; # needed on Linux
diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm
index fb9d7e8b0..30429e88b 100644
--- a/lib/Slic3r/GUI/Tab.pm
+++ b/lib/Slic3r/GUI/Tab.pm
@@ -20,16 +20,12 @@ 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);
-# Index of the currently active preset.
-__PACKAGE__->mk_accessors(qw(current_preset));
-
sub new {
- my $class = shift;
- my ($parent, %params) = @_;
+ my ($class, $parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
# Vertical sizer to hold the choice menu and the rest of the page.
@@ -41,23 +37,33 @@ 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
- $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("disk.png"), wxBITMAP_TYPE_PNG),
+ $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("disk.png"), wxBITMAP_TYPE_PNG),
+ 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->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
+ $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.
@@ -72,8 +78,11 @@ sub new {
$self->{treectrl} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [185, -1], wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS);
$left_sizer->Add($self->{treectrl}, 1, wxEXPAND);
$self->{icons} = Wx::ImageList->new(16, 16, 1);
+ # Map from an icon file name to its index in $self->{icons}.
+ $self->{icon_index} = {};
+ # Index of the last icon inserted into $self->{icons}.
+ $self->{icon_count} = -1;
$self->{treectrl}->AssignImageList($self->{icons});
- $self->{iconcount} = -1;
$self->{treectrl}->AddRoot("root");
$self->{pages} = [];
$self->{treectrl}->SetIndent(0);
@@ -96,76 +105,28 @@ sub new {
}
});
- EVT_CHOICE($parent, $self->{presets_choice}, sub {
- $self->on_select_preset;
- $self->_on_presets_changed;
+ 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 });
- EVT_BUTTON($self, $self->{btn_delete_preset}, sub {
- my $i = $self->current_preset;
- # Don't let the user delete the '- default -' configuration.
- # This shall not happen as the 'delete' button is disabled for the '- default -' entry,
- # but better be safe than sorry.
- return if ($i == 0 && $self->get_current_preset->{default});
- my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal;
- return unless $res == wxID_YES;
- # Delete the file.
- my $path = $self->{presets}[$i]->file;
- my $enc_path = Slic3r::encode_path($path);
- if (-e $enc_path && ! unlink $enc_path) {
- # Cannot delete the file, therefore the item will not be removed from the selection.
- Slic3r::GUI::show_error($self, "Cannot delete file $path : $!");
- return;
- }
- # Delete the preset.
- splice @{$self->{presets}}, $i, 1;
- # Delete the item from the UI component.
- $self->{presets_choice}->Delete($i - $self->{default_suppressed});
- $self->current_preset(undef);
- if ($self->{default_suppressed} && scalar(@{$self->{presets}}) == 1) {
- # Empty selection. Add the '- default -' item into the drop down selection.
- $self->{presets_choice}->Append($self->{presets}->[0]->name);
- # and remember that the '- default -' is shown.
- $self->{default_suppressed} = 0;
- }
- # Select the 0th item. If default is suppressed, select the first valid.
- $self->select_preset($self->{default_suppressed});
- $self->_on_presets_changed;
- });
-
- # C++ instance DynamicPrintConfig
- $self->{config} = Slic3r::Config->new;
# Initialize the DynamicPrintConfig by default keys/values.
# Possible %params keys: no_controller
$self->build(%params);
- $self->update_tree;
+ $self->rebuild_page_tree;
$self->_update;
- if ($self->hidden_options) {
- $self->{config}->apply(Slic3r::Config->new_from_defaults($self->hidden_options));
- }
return $self;
}
-# Are the '- default -' selections suppressed by the Slic3r GUI preferences?
-sub no_defaults {
- return $Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0;
-}
-
-# Get a currently active preset (Perl class Slic3r::GUI::Tab::Preset).
-sub get_current_preset {
- my $self = shift;
- return $self->get_preset($self->current_preset);
-}
-
-# Get a preset (Perl class Slic3r::GUI::Tab::Preset) with an index $i.
-sub get_preset {
- my ($self, $i) = @_;
- return $self->{presets}[$i];
-}
-
+# Save the current preset into file.
+# This removes the "dirty" flag of the preset, possibly creates a new preset under a new name,
+# and activates the new preset.
+# Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method
+# opens a Slic3r::GUI::SavePresetWindow dialog.
sub save_preset {
my ($self, $name) = @_;
@@ -175,10 +136,9 @@ sub save_preset {
$self->{treectrl}->SetFocus;
if (!defined $name) {
- my $preset = $self->get_current_preset;
+ my $preset = $self->{presets}->get_selected_preset;
my $default_name = $preset->default ? 'Untitled' : $preset->name;
$default_name =~ s/\.[iI][nN][iI]$//;
-
my $dlg = Slic3r::GUI::SavePresetWindow->new($self,
title => lc($self->title),
default => $default_name,
@@ -187,184 +147,238 @@ sub save_preset {
return unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name;
}
-
- $self->config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $name);
- $self->load_presets;
- $self->select_preset_by_name($name);
+ # Save the preset into Slic3r::data_dir/presets/section_name/preset_name.ini
+ 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->{show_incompatible_presets});
+ # Update the selection boxes at the platter.
$self->_on_presets_changed;
}
+# Called for a currently selected preset.
+sub delete_preset {
+ my ($self) = @_;
+ my $current_preset = $self->{presets}->get_selected_preset;
+ # Don't let the user delete the '- default -' configuration.
+ my $msg = 'Are you sure you want to ' . ($current_preset->external ? 'remove' : 'delete') . ' the selected preset?';
+ my $title = ($current_preset->external ? 'Remove' : 'Delete') . ' Preset';
+ return if $current_preset->default ||
+ wxID_YES != Wx::MessageDialog->new($self, $msg, $title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal;
+ # Delete the file and select some other reasonable preset.
+ # The 'external' presets will only be removed from the preset list, their files will not be deleted.
+ eval { $self->{presets}->delete_current_preset; };
+ Slic3r::GUI::catch_error($self) and return;
+ # Load the newly selected preset into the UI, update selection combo boxes with their dirty flags.
+ $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) = @_;
$self->{on_value_change} = $cb;
}
+# Register the on_presets_changed callback.
sub on_presets_changed {
my ($self, $cb) = @_;
$self->{on_presets_changed} = $cb;
}
# This method is called whenever an option field is changed by the user.
-# Propagate event to the parent through the 'on_value_changed' callback
+# Propagate event to the parent through the 'on_value_change' callback
# and call _update.
+# The on_value_change callback triggers Platter::on_config_change() to configure the 3D preview
+# (colors, wipe tower positon etc) and to restart the background slicing process.
sub _on_value_change {
my ($self, $key, $value) = @_;
$self->{on_value_change}->($key, $value) if $self->{on_value_change};
- $self->_update({ $key => 1 });
+ $self->_update;
}
# Override this to capture changes of configuration caused either by loading or switching a preset,
# or by a user changing an option field.
+# This callback is useful for cross-validating configuration values of a single preset.
sub _update {}
+# Call a callback to update the selection of presets on the platter:
+# To update the content of the selection boxes,
+# to update the filament colors of the selection boxes,
+# to update the "dirty" flags of the selection boxes,
+# to uddate number of "filament" selection boxes when the number of extruders change.
sub _on_presets_changed {
- my $self = shift;
-
- $self->{on_presets_changed}->(
- $self->{presets},
- $self->{default_suppressed},
- scalar($self->{presets_choice}->GetSelection) + $self->{default_suppressed},
- $self->is_dirty,
- ) if $self->{on_presets_changed};
+ my ($self) = @_;
+ $self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed};
}
+# For the printer profile, generate the extruder pages after a preset is loaded.
sub on_preset_loaded {}
-sub hidden_options {}
-sub config { $_[0]->{config}->clone }
-
-sub select_default_preset {
- my $self = shift;
- $self->select_preset(0);
-}
-sub select_preset {
- my ($self, $i) = @_;
- if ($self->{default_suppressed} && $i == 0) {
- # Selecting the '- default -'. Add it to the combo box.
- $self->{default_suppressed} = 0;
- $self->{presets_choice}->Insert($self->{presets}->[0]->name, 0);
- } elsif ($self->no_defaults && ! $self->{default_suppressed} && $i > 0) {
- # The user wants to hide the '- default -' items and a non-default item has been added to the presets.
- # Hide the '- default -' item.
- $self->{presets_choice}->Delete(0);
- $self->{default_suppressed} = 1;
+# 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_dirty_preset
+{
+ 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;
}
- $self->{presets_choice}->SetSelection($i - $self->{default_suppressed});
- $self->on_select_preset;
-}
-
-sub select_preset_by_name {
- my ($self, $name) = @_;
-
- $name = Slic3r::normalize_utf8_nfc($name);
- $self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}});
+ # 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;
}
-sub on_select_preset {
- my $self = shift;
-
- if ($self->is_dirty) {
- # Display a dialog showing the dirty options in a human readable form.
- my $old_preset = $self->get_current_preset;
- my $name = $old_preset->default ? 'Default preset' : "Preset \"" . $old_preset->name . "\"";
-
- my @option_names = ();
- foreach my $opt_key (@{$self->dirty_options}) {
- my $opt = $Slic3r::Config::Options->{$opt_key};
- my $name = $opt->{full_label} // $opt->{label};
- if ($opt->{category}) {
- $name = $opt->{category} . " > $name";
+# Called by the UI combo box when the user switches profiles.
+# Select a preset by a name. If ! defined(name), then the default preset is selected.
+# If the current profile is modified, user is asked to save the changes.
+sub select_preset {
+ my ($self, $name, $force) = @_;
+ $force //= 0;
+ 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;
}
- push @option_names, $name;
}
-
- 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);
- if ($confirm->ShowModal == wxID_NO) {
- $self->{presets_choice}->SetSelection($self->current_preset - $self->{default_suppressed});
-
- # trigger the on_presets_changed event so that we also restore the previous value
- # in the plater selector
- $self->_on_presets_changed;
- return;
+ 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;
+ }
}
}
-
- $self->current_preset($self->{presets_choice}->GetSelection + $self->{default_suppressed});
- my $preset = $self->get_current_preset;
- my $preset_config = $self->get_preset_config($preset);
+ 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;
+ } else {
+ 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.
+sub load_current_preset {
+ my ($self) = @_;
+ my $preset = $self->{presets}->get_current_preset;
eval {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
- my %keys_modified = ();
- foreach my $opt_key (@{$self->{config}->get_keys}) {
- if ($preset_config->has($opt_key)) {
- if ($self->{config}->serialize($opt_key) ne $preset_config->serialize($opt_key)) {
- $self->{config}->set($opt_key, $preset_config->get($opt_key));
- $keys_modified{$opt_key} = 1;
- }
- }
- }
- ($preset->default || $preset->external)
- ? $self->{btn_delete_preset}->Disable
- : $self->{btn_delete_preset}->Enable;
-
- $self->_update(\%keys_modified);
+ my $method = $preset->default ? 'Disable' : 'Enable';
+ $self->{btn_delete_preset}->$method;
+ $self->_update;
+ # For the printer profile, generate the extruder pages.
$self->on_preset_loaded;
- $self->reload_config;
- $Slic3r::GUI::Settings->{presets}{$self->name} = $preset->file ? basename($preset->file) : '';
+ # Reload preset pages with the new configuration values.
+ $self->_reload_config;
};
- if ($@) {
- $@ = "I was unable to load the selected config file: $@";
- Slic3r::GUI::catch_error($self);
- $self->select_default_preset;
- }
-
# use CallAfter because some field triggers schedule on_change calls using CallAfter,
# and we don't want them to be called after this update_dirty() as they would mark the
# preset dirty again
# (not sure this is true anymore now that update_dirty is idempotent)
wxTheApp->CallAfter(sub {
+ $self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
$self->_on_presets_changed;
- $self->update_dirty;
});
-
- wxTheApp->save_settings;
-}
-
-sub init_config_options {
- my ($self, @opt_keys) = @_;
- $self->{config}->apply(Slic3r::Config->new_from_defaults(@opt_keys));
}
sub add_options_page {
- my $self = shift;
- my ($title, $icon, %params) = @_;
-
+ my ($self, $title, $icon, %params) = @_;
+ # Index of $icon in an icon list $self->{icons}.
+ my $icon_idx = 0;
if ($icon) {
- my $bitmap = Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG);
- $self->{icons}->Add($bitmap);
- $self->{iconcount}++;
+ $icon_idx = $self->{icon_index}->{$icon};
+ if (! defined $icon_idx) {
+ # Add a new icon to the icon list.
+ my $bitmap = Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG);
+ $self->{icons}->Add($bitmap);
+ $icon_idx = $self->{icon_count} + 1;
+ $self->{icon_count} = $icon_idx;
+ $self->{icon_index}->{$icon} = $icon_idx;
+ }
}
-
- my $page = Slic3r::GUI::Tab::Page->new($self, $title, $self->{iconcount});
+ # Initialize the page.
+ my $page = Slic3r::GUI::Tab::Page->new($self, $title, $icon_idx);
$page->Hide;
$self->{hsizer}->Add($page, 1, wxEXPAND | wxLEFT, 5);
push @{$self->{pages}}, $page;
return $page;
}
-sub reload_config {
- my $self = shift;
+# Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields.
+sub _reload_config {
+ my ($self) = @_;
+ $self->Freeze;
$_->reload_config for @{$self->{pages}};
+ $self->Thaw;
}
-sub update_tree {
+# Regerenerate content of the page tree.
+sub rebuild_page_tree {
my ($self) = @_;
-
+ $self->Freeze;
# get label of the currently selected item
- my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection);
-
+ my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection);
my $rootItem = $self->{treectrl}->GetRootItem;
$self->{treectrl}->DeleteChildren($rootItem);
my $have_selection = 0;
@@ -377,147 +391,158 @@ sub update_tree {
$have_selection = 1;
}
}
-
if (!$have_selection) {
# this is triggered on first load, so we don't disable the sel change event
$self->{treectrl}->SelectItem($self->{treectrl}->GetFirstChild($rootItem));
}
+ $self->Thaw;
}
# Update the combo box label of the selected preset based on its "dirty" state,
# comparing the selected preset config with $self->{config}.
sub update_dirty {
my ($self) = @_;
- my $list_updated;
- foreach my $i ($self->{default_suppressed}..$#{$self->{presets}}) {
- my $preset = $self->get_preset($i);
- my $label = ($i == $self->current_preset && $self->is_dirty) ? $preset->name . " (modified)" : $preset->name;
- my $idx = $i - $self->{default_suppressed};
- if ($self->{presets_choice}->GetString($idx) ne $label) {
- $self->{presets_choice}->SetString($idx, $label);
- $list_updated = 1;
- }
- }
- $self->{presets_choice}->SetSelection($self->current_preset - $self->{default_suppressed}) if ($list_updated); # http://trac.wxwidgets.org/ticket/13769
- $self->_on_presets_changed;
-}
-
-# Has the selected preset been modified?
-sub is_dirty {
- my ($self) = @_;
- return @{$self->dirty_options} > 0;
-}
-
-# Which options of the selected preset were modified?
-sub dirty_options {
- my ($self) = @_;
- return [] if !defined $self->current_preset; # happens during initialization
- return $self->get_preset_config($self->get_current_preset)->diff($self->{config});
-}
-
-# Search all ini files in the presets directory, add them into the list of $self->{presets} in the form of Slic3r::GUI::Tab::Preset.
-# Initialize the drop down list box.
-sub load_presets {
- my ($self) = @_;
-
- $self->{presets} = [
- Slic3r::GUI::Tab::Preset->new(
- default => 1,
- name => '- default -',
- ),
- ];
-
- my %presets = wxTheApp->presets($self->name);
- foreach my $preset_name (sort keys %presets) {
- push @{$self->{presets}}, Slic3r::GUI::Tab::Preset->new(
- name => $preset_name,
- file => $presets{$preset_name},
- );
- }
- $self->current_preset(undef);
- $self->{default_suppressed} = Slic3r::GUI::Tab->no_defaults && scalar(@{$self->{presets}}) > 1;
- $self->{presets_choice}->Clear;
- foreach my $preset (@{$self->{presets}}) {
- next if ($preset->default && $self->{default_suppressed});
- $self->{presets_choice}->Append($preset->name);
- }
- {
- # load last used preset
- my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}};
- $self->select_preset($i || $self->{default_suppressed});
- }
+ $self->{presets}->update_dirty_ui($self->{presets_choice});
$self->_on_presets_changed;
}
-# Load a config file containing a Print, Filament & Printer preset.
-sub load_config_file {
- my ($self, $file) = @_;
- # look for the loaded config among the existing menu items
- my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}};
- if (!$i) {
- my $preset_name = basename($file); # keep the .ini suffix
- my $preset_new = Slic3r::GUI::Tab::Preset->new(
- file => $file,
- name => $preset_name,
- external => 1,
- );
- # Try to load the config file before it is entered into the list. If the loading fails, an undef is returned.
- return undef if ! defined $preset_new->config;
- push @{$self->{presets}}, $preset_new;
- $self->{presets_choice}->Append($preset_name);
- $i = $#{$self->{presets}};
- }
- $self->{presets_choice}->SetSelection($i - $self->{default_suppressed});
- $self->on_select_preset;
- $self->_on_presets_changed;
- return 1;
-}
-
# Load a provied DynamicConfig into the tab, modifying the active preset.
# 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 %keys_modified = ();
+ my $modified = 0;
foreach my $opt_key (@{$self->{config}->diff($config)}) {
$self->{config}->set($opt_key, $config->get($opt_key));
- $keys_modified{$opt_key} = 1;
+ $modified = 1;
}
- if (keys(%keys_modified)) {
+ if ($modified) {
$self->update_dirty;
# Initialize UI components with the config values.
- $self->reload_config;
- $self->_update(\%keys_modified);
+ $self->_reload_config;
+ $self->_update;
}
}
-# Load and return a config from the file associated with the $preset (Perl type Slic3r::GUI::Tab::Preset).
-sub get_preset_config {
- my ($self, $preset) = @_;
- return $preset->config($self->{config}->get_keys);
+# 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 {
my ($self, $opt_key, $opt_index) = @_;
-
- foreach my $page (@{ $self->{pages} }) {
+ foreach my $page (@{$self->{pages}}) {
my $field = $page->get_field($opt_key, $opt_index);
return $field if defined $field;
}
return undef;
}
+# Set a key/value pair on this page. Return true if the value has been modified.
+# Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer
+# after a preset is loaded.
sub set_value {
- my $self = shift;
- my ($opt_key, $value) = @_;
-
+ my ($self, $opt_key, $value) = @_;
my $changed = 0;
- foreach my $page (@{ $self->{pages} }) {
+ foreach my $page (@{$self->{pages}}) {
$changed = 1 if $page->set_value($opt_key, $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';
@@ -530,7 +555,8 @@ sub title { 'Print Settings' }
sub build {
my $self = shift;
- $self->{config}->apply(wxTheApp->{preset_bundle}->prints->default_preset->config);
+ $self->{presets} = wxTheApp->{preset_bundle}->print;
+ $self->{config} = $self->{presets}->get_edited_preset->config;
{
my $page = $self->add_options_page('Layers and perimeters', 'layers.png');
@@ -734,7 +760,7 @@ sub build {
$optgroup->append_single_option_line('clip_multipart_objects');
$optgroup->append_single_option_line('elefant_foot_compensation');
$optgroup->append_single_option_line('xy_size_compensation');
-# $optgroup->append_single_option_line('threads') if $Slic3r::have_threads;
+# $optgroup->append_single_option_line('threads');
$optgroup->append_single_option_line('resolution');
}
}
@@ -787,19 +813,33 @@ 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);
+ }
+ }
+ }
}
-sub reload_config {
+# 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;
+ $self->_reload_compatible_printers_widget;
+ $self->SUPER::_reload_config;
}
# Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user.
sub _update {
- # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
- my ($self, $keys_modified) = @_;
- $keys_modified //= {};
+ my ($self) = @_;
+ $self->Freeze;
my $config = $self->{config};
@@ -879,10 +919,6 @@ sub _update {
$self->load_config($new_conf);
}
- if ($keys_modified->{'layer_height'}) {
- # If the user had set the variable layer height, reset it and let the user know.
- }
-
if ($config->support_material) {
# Ask only once.
if (! $self->{support_material_overhangs_queried}) {
@@ -997,9 +1033,9 @@ sub _update {
my $have_wipe_tower = $config->wipe_tower;
$self->get_field($_)->toggle($have_wipe_tower)
for qw(wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe);
-}
-#sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () }
+ $self->Thaw;
+}
package Slic3r::GUI::Tab::Filament;
use base 'Slic3r::GUI::Tab';
@@ -1011,7 +1047,8 @@ sub title { 'Filament Settings' }
sub build {
my $self = shift;
- $self->{config}->apply(wxTheApp->{preset_bundle}->filaments->default_preset->config);
+ $self->{presets} = wxTheApp->{preset_bundle}->filament;
+ $self->{config} = $self->{presets}->get_edited_preset->config;
{
my $page = $self->add_options_page('Filament', 'spool.png');
@@ -1059,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);
@@ -1098,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);
}
}
@@ -1135,14 +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 {
- # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
- my ($self, $keys_modified) = @_;
+ 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];
@@ -1152,35 +1222,6 @@ sub _update {
for qw(min_fan_speed disable_fan_first_layers);
}
-sub _update_description {
- my $self = shift;
-
- 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);
@@ -1190,37 +1231,31 @@ sub name { 'printer' }
sub title { 'Printer Settings' }
sub build {
- my $self = shift;
- my (%params) = @_;
-
- $self->{config}->apply(wxTheApp->{preset_bundle}->printers->default_preset->config);
+ my ($self, %params) = @_;
+ $self->{presets} = wxTheApp->{preset_bundle}->printer;
+ $self->{config} = $self->{presets}->get_edited_preset->config;
+ $self->{extruders_count} = scalar @{$self->{config}->nozzle_diameter};
+
my $bed_shape_widget = sub {
my ($parent) = @_;
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;
};
-
- $self->{extruders_count} = 1;
-
+
{
my $page = $self->add_options_page('General', 'printer_empty.png');
{
@@ -1249,13 +1284,16 @@ sub build {
$optgroup->append_single_option_line('single_extruder_multi_material');
}
$optgroup->on_change(sub {
- my ($opt_id) = @_;
- if ($opt_id eq 'extruders_count') {
- wxTheApp->CallAfter(sub {
+ my ($opt_key, $value) = @_;
+ wxTheApp->CallAfter(sub {
+ if ($opt_key eq 'extruders_count') {
$self->_extruders_count_changed($optgroup->get_value('extruders_count'));
- });
- $self->update_dirty;
- }
+ $self->update_dirty;
+ } else {
+ $self->update_dirty;
+ $self->_on_value_change($opt_key, $value);
+ }
+ });
});
}
if (!$params{no_controller})
@@ -1268,7 +1306,7 @@ sub build {
$serial_port->side_widget(sub {
my ($parent) = @_;
- my $btn = Wx::BitmapButton->new($parent, -1, Wx::Bitmap->new($Slic3r::var->("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG),
+ my $btn = Wx::BitmapButton->new($parent, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE);
$btn->SetToolTipString("Rescan serial ports")
if $btn->can('SetToolTipString');
@@ -1282,7 +1320,7 @@ sub build {
my $btn = $self->{serial_test_btn} = Wx::Button->new($parent, -1,
"Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
$btn->SetFont($Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("wrench.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("wrench.png"), wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
my $sender = Slic3r::GCode::Sender->new;
@@ -1312,7 +1350,7 @@ sub build {
my $btn = Wx::Button->new($parent, -1, "Browse…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$btn->SetFont($Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("zoom.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("zoom.png"), wxBITMAP_TYPE_PNG));
if (!eval "use Net::Bonjour; 1") {
$btn->Disable;
@@ -1328,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;
}
@@ -1348,7 +1381,7 @@ sub build {
my $btn = $self->{octoprint_host_test_btn} = Wx::Button->new($parent, -1,
"Test", wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
$btn->SetFont($Slic3r::GUI::small_font);
- $btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("wrench.png"), wxBITMAP_TYPE_PNG));
+ $btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("wrench.png"), wxBITMAP_TYPE_PNG));
EVT_BUTTON($self, $btn, sub {
my $ua = LWP::UserAgent->new;
@@ -1434,6 +1467,15 @@ sub build {
$option->height(150);
$optgroup->append_single_option_line($option);
}
+ {
+ my $optgroup = $page->new_optgroup('Between objects G-code (for sequential printing)',
+ label_width => 0,
+ );
+ my $option = $optgroup->get_option('between_objects_gcode');
+ $option->full_width(1);
+ $option->height(150);
+ $optgroup->append_single_option_line($option);
+ }
}
{
@@ -1458,43 +1500,23 @@ sub build {
sub _update_serial_ports {
my ($self) = @_;
- $self->get_field('serial_port')->set_values([ wxTheApp->scan_serial_ports ]);
+ $self->get_field('serial_port')->set_values([ Slic3r::GUI::scan_serial_ports ]);
}
sub _extruders_count_changed {
my ($self, $extruders_count) = @_;
-
$self->{extruders_count} = $extruders_count;
+ wxTheApp->{preset_bundle}->printer->get_edited_preset->set_num_extruders($extruders_count);
+ wxTheApp->{preset_bundle}->update_multi_material_filament_presets;
$self->_build_extruder_pages;
$self->_on_value_change('extruders_count', $extruders_count);
}
-sub _extruder_options {
- qw(nozzle_diameter min_layer_height max_layer_height extruder_offset
- retract_length retract_lift retract_lift_above retract_lift_below retract_speed deretract_speed
- retract_before_wipe retract_restart_extra retract_before_travel wipe
- retract_layer_change retract_length_toolchange retract_restart_extra_toolchange extruder_colour) }
-
sub _build_extruder_pages {
- my $self = shift;
-
+ my ($self) = @_;
my $default_config = Slic3r::Config::Full->new;
-
+
foreach my $extruder_idx (@{$self->{extruder_pages}} .. $self->{extruders_count}-1) {
- # extend options
- foreach my $opt_key ($self->_extruder_options) {
- my $values = $self->{config}->get($opt_key);
- if (!defined $values) {
- $values = [ $default_config->get_at($opt_key, 0) ];
- } else {
- # use last extruder's settings for the new one
- my $last_value = $values->[-1];
- $values->[$extruder_idx] //= $last_value;
- }
- $self->{config}->set($opt_key, $values)
- or die "Unable to extend $opt_key";
- }
-
# build page
my $page = $self->{extruder_pages}[$extruder_idx] = $self->add_options_page("Extruder " . ($extruder_idx + 1), 'funnel.png');
{
@@ -1544,14 +1566,6 @@ sub _build_extruder_pages {
splice @{$self->{extruder_pages}}, $self->{extruders_count};
}
- # remove extra config values
- foreach my $opt_key ($self->_extruder_options) {
- my $values = $self->{config}->get($opt_key);
- splice @$values, $self->{extruders_count} if $self->{extruders_count} <= $#$values;
- $self->{config}->set($opt_key, $values)
- or die "Unable to truncate $opt_key";
- }
-
# rebuild page list
my @pages_without_extruders = (grep $_->{title} !~ /^Extruder \d+/, @{$self->{pages}});
my $page_notes = pop @pages_without_extruders;
@@ -1560,14 +1574,14 @@ sub _build_extruder_pages {
@{$self->{extruder_pages}}[ 0 .. $self->{extruders_count}-1 ],
$page_notes
);
- $self->update_tree;
+ $self->rebuild_page_tree;
}
# Slic3r::GUI::Tab::Printer::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user.
sub _update {
- # $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
- my ($self, $keys_modified) = @_;
-
+ my ($self) = @_;
+ $self->Freeze;
+
my $config = $self->{config};
my $serial_speed = $self->get_field('serial_speed');
@@ -1639,42 +1653,27 @@ sub _update {
$self->get_field('retract_restart_extra_toolchange', $i)->toggle
($have_multiple_extruders && $toolchange_retraction);
}
+
+ $self->Thaw;
}
# this gets executed after preset is loaded and before GUI fields are updated
sub on_preset_loaded {
- my $self = shift;
-
+ my ($self) = @_;
# update the extruders count field
- {
- # update the GUI field according to the number of nozzle diameters supplied
- my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
- $self->set_value('extruders_count', $extruders_count);
- $self->_extruders_count_changed($extruders_count);
- }
-}
-
-# Load a config file containing a Print, Filament & Printer preset.
-sub load_config_file {
- my $self = shift;
- if ($self->SUPER::load_config_file(@_)) {
- Slic3r::GUI::warning_catcher($self)->(
- "Your configuration was imported. However, Slic3r is currently only able to import settings "
- . "for the first defined filament. We recommend you don't use exported configuration files "
- . "for multi-extruder setups and rely on the built-in preset management system instead.")
- if @{ $self->{config}->nozzle_diameter } > 1;
- return 1;
- }
- return undef;
+ my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
+ $self->set_value('extruders_count', $extruders_count);
+ # update the GUI field according to the number of nozzle diameters supplied
+ $self->_extruders_count_changed($extruders_count);
}
+# Single Tab page containing a {vsizer} of {optgroups}
package Slic3r::GUI::Tab::Page;
use Wx qw(wxTheApp :misc :panel :sizer);
use base 'Wx::ScrolledWindow';
sub new {
- my $class = shift;
- my ($parent, $title, $iconID) = @_;
+ my ($class, $parent, $title, $iconID) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{optgroups} = [];
$self->{title} = $title;
@@ -1718,7 +1717,6 @@ sub reload_config {
sub get_field {
my ($self, $opt_key, $opt_index) = @_;
-
foreach my $optgroup (@{ $self->{optgroups} }) {
my $field = $optgroup->get_fieldc($opt_key, $opt_index);
return $field if defined $field;
@@ -1727,9 +1725,7 @@ sub get_field {
}
sub set_value {
- my $self = shift;
- my ($opt_key, $value) = @_;
-
+ my ($self, $opt_key, $value) = @_;
my $changed = 0;
foreach my $optgroup (@{$self->{optgroups}}) {
$changed = 1 if $optgroup->set_value($opt_key, $value);
@@ -1737,14 +1733,15 @@ sub set_value {
return $changed;
}
+# Dialog to select a new file name for a modified preset to be saved.
+# Called from Tab::save_preset().
package Slic3r::GUI::SavePresetWindow;
use Wx qw(:combobox :dialog :id :misc :sizer);
use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER);
use base 'Wx::Dialog';
sub new {
- my $class = shift;
- my ($parent, %params) = @_;
+ my ($class, $parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, "Save preset", wxDefaultPosition, wxDefaultSize);
my @values = @{$params{values}};
@@ -1770,8 +1767,7 @@ sub new {
sub accept {
my ($self, $event) = @_;
-
- if (($self->{chosen_name} = $self->{combo}->GetValue)) {
+ if (($self->{chosen_name} = Slic3r::normalize_utf8_nfc($self->{combo}->GetValue))) {
if ($self->{chosen_name} !~ /^[^<>:\/\\|?*\"]+$/) {
Slic3r::GUI::show_error($self, "The supplied name is not valid; the following characters are not allowed: <>:/\|?*\"");
} elsif ($self->{chosen_name} eq '- default -') {
@@ -1783,57 +1779,8 @@ sub accept {
}
sub get_name {
- my $self = shift;
+ my ($self) = @_;
return $self->{chosen_name};
}
-package Slic3r::GUI::Tab::Preset;
-use Moo;
-use List::Util qw(any);
-
-# The preset represents a "default" set of properties.
-has 'default' => (is => 'ro', default => sub { 0 });
-has 'external' => (is => 'ro', default => sub { 0 });
-has 'name' => (is => 'rw', required => 1);
-has 'file' => (is => 'rw');
-
-# Load a config file, return a C++ class Slic3r::DynamicPrintConfig with $keys initialized from the config file.
-# In case of a "default" config item, return the default values.
-sub config {
- my ($self, $keys) = @_;
-
- if ($self->default) {
- # Perl class Slic3r::Config extends the C++ class Slic3r::DynamicPrintConfig
- return Slic3r::Config->new_from_defaults(@$keys);
- } else {
- if (!-e Slic3r::encode_path($self->file)) {
- Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
- return undef;
- }
-
- # apply preset values on top of defaults
- my $config = Slic3r::Config->new_from_defaults(@$keys);
- my $external_config = eval { Slic3r::Config->load($self->file); };
- if ($@) {
- Slic3r::GUI::show_error(undef, $@);
- return undef;
- }
- $config->set($_, $external_config->get($_))
- for grep $external_config->has($_), @$keys;
-
- if (any { $_ eq 'nozzle_diameter' } @$keys) {
- # Loaded the Printer settings. Verify, that all extruder dependent values have enough values.
- my $nozzle_diameter = $config->nozzle_diameter;
- my $num_extruders = scalar(@{$nozzle_diameter});
- foreach my $key (qw(deretract_speed extruder_colour retract_before_wipe)) {
- my $vec = $config->get($key);
- push @{$vec}, ($vec->[0]) x ($num_extruders - @{$vec});
- $config->set($key, $vec);
- }
- }
-
- return $config;
- }
-}
-
1;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 160f70e06..12ad2f12f 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -65,6 +65,9 @@ sub process {
}
# G-code export process, running at a background thread.
+# The export_gcode may die for various reasons (fails to process output_filename_format,
+# write error into the G-code, cannot execute post-processing scripts).
+# It is up to the caller to show an error message.
sub export_gcode {
my $self = shift;
my %params = @_;
@@ -73,11 +76,12 @@ sub export_gcode {
$self->process;
# output everything to a G-code file
+ # The following call may die if the output_filename_format template substitution fails.
my $output_file = $self->output_filepath($params{output_file} // '');
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
- die "G-code export to " . $output_file . " failed\n"
- if ! Slic3r::GCode->new->do_export($self, $output_file);
+ # The following line may die for multiple reasons.
+ Slic3r::GCode->new->do_export($self, $output_file);
# run post-processing scripts
if (@{$self->config->post_process}) {
@@ -99,6 +103,7 @@ sub export_gcode {
}
# Export SVG slices for the offline SLA printing.
+# The export_svg is expected to be executed inside an eval block.
sub export_svg {
my $self = shift;
my %params = @_;
@@ -107,6 +112,7 @@ sub export_svg {
my $fh = $params{output_fh};
if (!$fh) {
+ # The following line may die if the output_filename_format template substitution fails.
my $output_file = $self->output_filepath($params{output_file});
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
@@ -255,13 +261,4 @@ sub make_wipe_tower {
$self->set_step_done(STEP_WIPE_TOWER);
}
-# Wrapper around the C++ Slic3r::Print::validate()
-# to produce a Perl exception without a hang-up on some Strawberry perls.
-sub validate
-{
- my $self = shift;
- my $err = $self->_validate;
- die $err . "\n" if (defined($err) && $err ne '');
-}
-
1;
diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm
index 793654300..e275fdde5 100644
--- a/lib/Slic3r/Print/Object.pm
+++ b/lib/Slic3r/Print/Object.pm
@@ -11,9 +11,6 @@ use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union
use Slic3r::Print::State ':steps';
use Slic3r::Surface ':types';
-# If enabled, phases of prepare_infill will be written into SVG files to an "out" directory.
-our $SLIC3R_DEBUG_SLICE_PROCESSING = 0;
-
sub layers {
my $self = shift;
return [ map $self->get_layer($_), 0..($self->layer_count - 1) ];