Просмотр исходного кода

Looks like the reworked C++ preferences start to work again.

bubnikv 7 лет назад
Родитель
Сommit
e8b6d92d4d

+ 5 - 15
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;
 }
@@ -164,6 +153,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
 }

+ 5 - 60
lib/Slic3r/Config.pm

@@ -12,14 +12,14 @@ use List::Util qw(first max);
 # 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) };
+        *{$opt_key} = sub { 
+            #print "Slic3r::Config::accessor $opt_key\n"; 
+            $_[0]->get($opt_key)
+        };
     }
 }
 
@@ -64,61 +64,6 @@ sub new_from_cli {
     return $self;
 }
 
-# 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';
 

+ 27 - 129
lib/Slic3r/GUI.pm

@@ -53,25 +53,9 @@ use constant FILE_WILDCARDS => {
 use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)};
 
 # 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);
@@ -89,91 +73,47 @@ sub OnInit {
     Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
     
     $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 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 $enc_datadir = Slic3r::encode_path(Slic3r::data_dir);
-    Slic3r::debugf "Data directory: %s\n", $enc_datadir;
-    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(Slic3r::data_dir . "/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;
-    }
-    $Settings->{_}{version} = $Slic3r::VERSION;
-    $self->save_settings;
+    $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($Slic3r::GUI::Settings->{_}{no_defaults} ? 1 : 0);
-    eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) };
+    $self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
+    eval { 
+        $self->{preset_bundle}->load_presets(Slic3r::data_dir);
+        $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);
     $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
-    #FIXME this is undocumented and the use case is unclear.
-#    {
-#        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;
-        eval { $self->{preset_bundle}->load_presets(Slic3r::data_dir) };
     }
 
     EVT_IDLE($frame, sub {
         while (my $cb = shift @cb) {
             $cb->();
         }
+        $self->{app_config}->save if $self->{app_config}->dirty;
     });
     
     return 1;
@@ -265,11 +205,6 @@ sub notify {
     $self->{notifier}->notify($message);
 }
 
-sub save_settings {
-    my ($self) = @_;
-    Slic3r::Config->write_ini(Slic3r::data_dir . "/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 {
@@ -277,22 +212,11 @@ sub update_ui_from_settings {
     $self->{mainframe}->update_ui_from_settings;
 }
 
-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;
@@ -308,31 +232,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) = @_;
     
@@ -369,25 +268,24 @@ sub set_menu_item_icon {
 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");
     }
 }
 

+ 1 - 1
lib/Slic3r/GUI/Controller.pm

@@ -121,7 +121,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;
         }

+ 1 - 1
lib/Slic3r/GUI/Controller/PrinterPanel.pm

@@ -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);
 }
 

+ 55 - 57
lib/Slic3r/GUI/MainFrame.pm

@@ -76,6 +76,9 @@ sub new {
         }
         # 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;
     });
@@ -140,9 +143,11 @@ sub _init_tabpanel {
             my ($group, $name) = @_;
 	        $self->{options_tabs}{$group}->select_preset($name);
         });
-        
         # load initial config
-        $self->{plater}->on_config_change(wxTheApp->{preset_bundle}->full_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}));
     }
 }
 
@@ -340,14 +345,15 @@ sub quick_slice {
     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;
@@ -369,8 +375,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;
         {
@@ -392,11 +397,9 @@ 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}) {
@@ -405,7 +408,7 @@ sub quick_slice {
             $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;
@@ -413,8 +416,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;
         }
         
@@ -457,8 +459,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;
@@ -487,15 +490,6 @@ sub repair_stl {
     Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
 }
 
-# Extra variables for the placeholder parser generating a G-code.
-sub extra_variables {
-    my $self = shift;
-    my %extra_variables = ();
-    $extra_variables{"${_}_preset"} = wxTheApp->{preset_bundle}->{$_}->get_current_preset_name
-        for qw(print filament printer);
-    return { %extra_variables };
-}
-
 sub export_config {
     my $self = shift;
     # Generate a cummulative configuration for the selected print, filaments and printer.    
@@ -504,15 +498,14 @@ sub export_config {
     eval { $config->validate; };
     Slic3r::GUI::catch_error($self) and return;
     # Ask user for the file name for the config file.
-    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, 
+    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);
     my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
     $dlg->Destroy;
     if (defined $file) {
-        $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
-        wxTheApp->save_settings;
+        wxTheApp->{app_config}->update_config_dir(dirname($file));
         $last_config = $file;
         $config->save($file);
     }
@@ -523,9 +516,10 @@ 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;
@@ -534,49 +528,51 @@ sub load_config_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}});
-    $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
-    wxTheApp->save_settings;
+    wxTheApp->{app_config}->update_config_dir(dirname($file));
     $last_config = $file;
 }
 
 sub export_configbundle {
-    my $self = shift;
+    my ($self) = @_;
+    return unless $self->check_unsaved_changes;
     # validate current configuration in case it's dirty
-    eval { $self->config->validate; };
+    eval { wxTheApp->{preset_bundle}->full_config->validate; };
     Slic3r::GUI::catch_error($self) and return;
     # Ask user for a file name.
-    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, 
+    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);
     my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
     $dlg->Destroy;
     if (defined $file) {
         # Export the config bundle.
-        $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
-        wxTheApp->save_settings;
-        eval { $self->{presets}->export_configbundle($file); };
+        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;
+    wxTheApp->{app_config}->update_config_dir(dirname($file));
 
     my $presets_imported = 0;
-    eval { $presets_imported = $self->{presets}->load_configbundle($file); };
+    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.
@@ -597,16 +593,18 @@ sub load_config {
 }
 
 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}}) {
-            # Select the first visible preset.
-            $tab->select_preset(undef);
+            # Select the first visible preset, force.
+            $tab->select_preset(undef, 1);
         }
+        # 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');
         }
     }
@@ -666,7 +664,7 @@ 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});
 }
 

+ 4 - 0
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;

+ 33 - 54
lib/Slic3r/GUI/Plater.pm

@@ -63,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
@@ -119,7 +114,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.
@@ -330,7 +325,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 {
@@ -448,7 +443,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);
@@ -510,13 +504,14 @@ sub _on_select_preset {
         wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
     }
 	if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
-		wxTheApp->save_settings;
         wxTheApp->{preset_bundle}->update_platter_filament_ui_colors($idx, $choice);
 	} else {
     	# call GetSelection() in scalar context as it's context-aware
     	$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(wxTheApp->{preset_bundle}->full_config);
 }
@@ -548,8 +543,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;
     }
 }
@@ -587,6 +582,8 @@ sub update_presets {
             $choice_idx += 1;
         }
     }
+    # Synchronize config.ini with the current selections.
+    wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
 }
 
 sub add {
@@ -655,8 +652,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));
@@ -704,7 +700,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;
     }
     
@@ -817,7 +813,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;
@@ -1130,7 +1126,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;
@@ -1152,7 +1148,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};
     
@@ -1171,11 +1166,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
     @_ = ();
@@ -1246,7 +1238,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.
@@ -1289,52 +1281,41 @@ sub export_gcode {
         $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);
     } 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 $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;
@@ -1577,7 +1558,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);
     }
     
@@ -1604,9 +1585,7 @@ sub update {
 # 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};
-    wxTheApp->{preset_bundle}->update_multi_material_filament_presets;
 
     while (int(@$choices) < $num_extruders) {
         # copy strings from first choice

+ 3 - 4
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;

+ 10 - 10
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,35 @@ 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"),
     ));
     
     my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@@ -79,15 +79,15 @@ sub new {
 }
 
 sub _accept {
-    my $self = shift;
+    my ($self) = @_;
     
     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

Некоторые файлы не были показаны из-за большого количества измененных файлов