Merge pull request #3770 from alexrj/gui3

Changes to the GUI
This commit is contained in:
Alessandro Ranellucci 2017-03-26 20:08:03 +02:00 committed by GitHub
commit 9e71fe2bf3
23 changed files with 1586 additions and 1337 deletions

View File

@ -116,7 +116,6 @@ Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Le
--gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab
--gui-mode Overrides the configured mode (simple/expert)
--autosave <file> Automatically export current configuration to the specified file
Output options:

View File

@ -30,12 +30,11 @@ sub new_from_defaults {
my (@opt_keys) = @_;
my $self = $class->new;
my $defaults = Slic3r::Config::Full->new;
if (@opt_keys) {
$self->set($_, $defaults->get($_))
for grep $defaults->has($_), @opt_keys;
$self->set($_, $Options->{$_}{default})
for grep exists $Options->{$_}{default}, @opt_keys;
} else {
$self->apply_static($defaults);
$self->apply_static(Slic3r::Config::Full->new);
}
return $self;
}

View File

@ -5,7 +5,9 @@ use utf8;
use File::Basename qw(basename);
use FindBin;
use List::Util qw(first);
use List::Util qw(first any);
use Slic3r::Geometry qw(X Y);
use Slic3r::GUI::2DBed;
use Slic3r::GUI::AboutDialog;
use Slic3r::GUI::BedShapeDialog;
@ -31,9 +33,10 @@ use Slic3r::GUI::ProgressStatusBar;
use Slic3r::GUI::Projector;
use Slic3r::GUI::OptionsGroup;
use Slic3r::GUI::OptionsGroup::Field;
use Slic3r::GUI::SimpleTab;
use Slic3r::GUI::Preset;
use Slic3r::GUI::PresetEditor;
use Slic3r::GUI::PresetEditorDialog;
use Slic3r::GUI::SLAPrintOptions;
use Slic3r::GUI::Tab;
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
our $have_LWP = eval "use LWP::UserAgent; 1";
@ -59,15 +62,12 @@ use constant AMF_MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(amf)};
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 $mode;
our $autosave;
our $threads;
our @cb;
our $Settings = {
_ => {
mode => 'simple',
version_check => 1,
autocenter => 1,
invert_zoom => 0,
@ -100,6 +100,7 @@ sub OnInit {
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
$self->{notifier} = Slic3r::GUI::Notifier->new;
$self->{presets} = { print => [], filament => [], printer => [] };
# locate or create data directory
# Unix: ~/.Slic3r
@ -131,18 +132,25 @@ sub OnInit {
for grep !exists $ini->{_}{$_}, keys %{$Settings->{_}};
$Settings = $ini;
}
delete $Settings->{_}{mode}; # handle legacy
}
$Settings->{_}{version} = $Slic3r::VERSION;
$Settings->{_}{threads} = $threads if $threads;
$self->save_settings;
if (-f "$enc_datadir/simple.ini") {
# The Simple Mode settings were already automatically duplicated to presets
# named "Simple Mode" in each group, so we already support retrocompatibility.
unlink "$enc_datadir/simple.ini";
}
$self->load_presets;
# application frame
Wx::Image::AddHandler(Wx::PNGHandler->new);
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
mode => $mode // $Settings->{_}{mode},
# 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_plater => $no_plater,
);
$self->SetTopWindow($frame);
@ -279,21 +287,64 @@ sub save_settings {
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
}
sub presets {
my ($self, $section) = @_;
sub presets { return $_[0]->{presets}; }
my %presets = ();
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
sub load_presets {
my ($self) = @_;
for my $group (qw(printer filament print)) {
my $presets = $self->{presets}{$group};
# keep external or dirty presets
@$presets = grep { ($_->external && $_->file_exists) || $_->dirty } @$presets;
my $dir = "$Slic3r::GUI::datadir/$group";
opendir my $dh, Slic3r::encode_path($dir)
or die "Failed to read directory $dir (errno: $!)\n";
foreach my $file (grep /\.ini$/i, readdir $dh) {
$file = Slic3r::decode_path($file);
my $name = basename($file);
$name =~ s/\.ini$//;
$presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
$name =~ s/\.ini$//i;
# skip if we already have it
next if any { $_->name eq $name } @$presets;
push @$presets, Slic3r::GUI::Preset->new(
group => $group,
name => $name,
file => "$dir/$file",
);
}
closedir $dh;
return %presets;
@$presets = sort { $a->name cmp $b->name } @$presets;
unshift @$presets, Slic3r::GUI::Preset->new(
group => $group,
default => 1,
name => '- default -',
);
}
}
sub add_external_preset {
my ($self, $file) = @_;
my $name = basename($file); # keep .ini suffix
for my $group (qw(printer filament print)) {
my $presets = $self->{presets}{$group};
# remove any existing preset with the same name
@$presets = grep { $_->name ne $name } @$presets;
push @$presets, Slic3r::GUI::Preset->new(
group => $group,
name => $name,
file => $file,
external => 1,
);
}
return $name;
}
sub have_version_check {
@ -377,4 +428,38 @@ sub scan_serial_ports {
return grep !/Bluetooth|FireFly/, @ports;
}
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));
}
}
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;
}
sub restore_window_pos {
my ($self, $window, $name) = @_;
if (defined $Settings->{_}{"${name}_pos"}) {
my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ];
$window->SetSize($size);
my $display = Wx::Display->new->GetClientArea();
my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ];
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
$window->Move($pos);
}
$window->Maximize(1) if $Settings->{_}{"${name}_maximized"};
}
}
1;

View File

@ -54,13 +54,13 @@ use constant HAS_VBO => eval { glGenBuffersARB_p(0); 1 };
# phi / theta angles to orient the camera.
use constant VIEW_ISO => [45.0,45.0];
use constant VIEW_LEFT => [90.0,90.0];
use constant VIEW_RIGHT => [-90.0,90.0];
use constant VIEW_TOP => [0.0,0.0];
use constant VIEW_BOTTOM => [0.0,180.0];
use constant VIEW_LEFT => [90.0,90.0];
use constant VIEW_RIGHT => [-90.0,90.0];
use constant VIEW_FRONT => [0.0,90.0];
use constant VIEW_REAR => [180.0,90.0];
use constant VIEW_BACK => [180.0,90.0];
use constant VIEW_DIAGONAL => [45.0,45.0];
use constant GIMBAL_LOCK_THETA_MAX => 170;
@ -338,20 +338,20 @@ sub select_view {
if (ref($direction)) {
$dirvec = $direction;
} else {
if ($direction eq 'iso') {
$dirvec = VIEW_ISO;
if ($direction eq 'top') {
$dirvec = VIEW_TOP;
} elsif ($direction eq 'bottom') {
$dirvec = VIEW_BOTTOM;
} elsif ($direction eq 'left') {
$dirvec = VIEW_LEFT;
} elsif ($direction eq 'right') {
$dirvec = VIEW_RIGHT;
} elsif ($direction eq 'top') {
$dirvec = VIEW_TOP;
} elsif ($direction eq 'bottom') {
$dirvec = VIEW_BOTTOM;
} elsif ($direction eq 'front') {
$dirvec = VIEW_FRONT;
} elsif ($direction eq 'rear') {
$dirvec = VIEW_REAR;
} elsif ($direction eq 'back') {
$dirvec = VIEW_BACK;
} elsif ($direction eq 'diagonal') {
$dirvec = VIEW_DIAGONAL;
}
}
$self->_sphi($dirvec->[0]);

View File

@ -7,6 +7,7 @@ use strict;
use warnings;
use utf8;
use List::Util qw(any);
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);
@ -39,14 +40,14 @@ sub new {
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
my %presets = wxTheApp->presets('printer');
my %presets = map { $_->name => $_ } wxTheApp->presets('printer');
# 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});
my $config = $presets{$preset_name}->dirty_config;
next if !$config->serial_port;
my $id = &Wx::NewId();
@ -100,8 +101,8 @@ sub OnActivate {
# get all available presets
my %presets = ();
{
my %all = wxTheApp->presets('printer');
my %configs = map { my $name = $_; $name => Slic3r::Config->load($all{$name}) } keys %all;
my %all = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
my %configs = map { my $name = $_; $name => $all{$name}->load_config } keys %all;
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
}
@ -177,6 +178,12 @@ sub print_panels {
map $_->GetWindow, $self->{sizer}->GetChildren;
}
sub printing {
my ($self) = @_;
return any { $_->printing } $self->print_panels;
}
sub update_presets {
my $self = shift;
my ($group, $presets, $selected, $is_dirty) = @_;

View File

@ -28,11 +28,8 @@ sub new {
}
# store input params
$self->{mode} = $params{mode};
$self->{mode} = 'expert' if $self->{mode} !~ /^(?:simple|expert)$/;
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
$self->{no_controller} = $params{no_controller};
$self->{no_plater} = $params{no_plater};
$self->{loaded} = 0;
# initialize tabpanel and menubar
@ -59,19 +56,8 @@ sub new {
$self->SetSizer($sizer);
$self->Fit;
$self->SetMinSize([760, 490]);
if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) {
my $size = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ];
$self->SetSize($size);
my $display = Wx::Display->new->GetClientArea();
my $pos = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ];
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
$self->Move($pos);
}
$self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized};
} else {
$self->SetSize($self->GetMinSize);
}
wxTheApp->restore_window_pos($self, "main_frame");
$self->Show;
$self->Layout;
}
@ -80,16 +66,24 @@ sub new {
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
if ($event->CanVeto && !$self->check_unsaved_changes) {
if ($event->CanVeto) {
if (!$self->{plater}->prompt_unsaved_changes) {
$event->Veto;
return;
}
if ($self->{controller} && $self->{controller}->printing) {
my $confirm = Wx::MessageDialog->new($self, "You are currently printing. Do you want to stop printing and continue anyway?",
'Unfinished Print', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
if ($confirm->ShowModal == wxID_NO) {
$event->Veto;
return;
}
}
}
# save window size
$Slic3r::GUI::Settings->{_}{main_frame_pos} = join ',', $self->GetScreenPositionXY;
$Slic3r::GUI::Settings->{_}{main_frame_size} = join ',', $self->GetSizeWH;
$Slic3r::GUI::Settings->{_}{main_frame_maximized} = $self->IsMaximized;
wxTheApp->save_settings;
wxTheApp->save_window_pos($self, "main_frame");
# propagate event
$event->Skip;
@ -107,71 +101,10 @@ sub _init_tabpanel {
$panel->OnActivate if $panel->can('OnActivate');
});
if (!$self->{no_plater}) {
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
}
if (!$self->{no_controller}) {
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
}
$self->{options_tabs} = {};
my $simple_config;
if ($self->{mode} eq 'simple') {
$simple_config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini")
if -e Slic3r::encode_path("$Slic3r::GUI::datadir/simple.ini");
}
my $class_prefix = $self->{mode} eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::";
for my $tab_name (qw(print filament printer)) {
my $tab;
$tab = $self->{options_tabs}{$tab_name} = ($class_prefix . ucfirst $tab_name)->new(
$panel,
no_controller => $self->{no_controller});
$tab->on_value_change(sub {
my ($opt_key, $value) = @_;
my $config = $tab->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';
}
if ($self->{loaded}) { # don't save while loading for the first time
if ($self->{mode} eq 'simple') {
# save config
$self->config->save("$Slic3r::GUI::datadir/simple.ini");
# save a copy into each preset section
# so that user gets the config when switching to expert mode
$config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $tab->name, 'Simple Mode');
$Slic3r::GUI::Settings->{presets}{$tab->name} = 'Simple Mode.ini';
wxTheApp->save_settings;
}
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave;
}
});
$tab->on_presets_changed(sub {
if ($self->{plater}) {
$self->{plater}->update_presets($tab_name, @_);
$self->{plater}->on_config_change($tab->config);
if ($self->{controller}) {
$self->{controller}->update_presets($tab_name, @_);
}
}
});
$tab->load_presets;
$panel->AddPage($tab, $tab->title);
$tab->load_config($simple_config) if $simple_config;
}
if ($self->{plater}) {
$self->{plater}->on_select_preset(sub {
my ($group, $i) = @_;
$self->{options_tabs}{$group}->select_preset($i);
});
# load initial config
$self->{plater}->on_config_change($self->config);
}
}
sub _init_menubar {
@ -239,10 +172,16 @@ sub _init_menubar {
}
# Plater menu
unless ($self->{no_plater}) {
{
my $plater = $self->{plater};
$self->{plater_menu} = Wx::Menu->new;
{
my $selectMenu = $self->{plater_select_menu} = Wx::Menu->new;
my $selectMenuItem = $self->{plater_menu}->AppendSubMenu($selectMenu, "Select", 'Select an object in the plater');
wxTheApp->set_menu_item_icon($selectMenuItem, 'brick.png');
}
$self->{plater_menu}->AppendSeparator();
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
$plater->export_gcode;
}, undef, 'cog_go.png');
@ -252,57 +191,35 @@ sub _init_menubar {
$self->_append_menu_item($self->{plater_menu}, "Export plate with modifiers as AMF...", 'Export current plate as AMF, including all modifier meshes', sub {
$plater->export_amf;
}, undef, 'brick_go.png');
$self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub {
$plater->pause_background_process;
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
$plater->resume_background_process;
}, undef, 'film.png');
$self->{object_menu} = $self->{plater}->object_menu;
$self->on_plater_selection_changed(0);
}
# Window menu
my $windowMenu = Wx::Menu->new;
# Settings menu
my $settingsMenu = Wx::Menu->new;
{
my $tab_offset = 0;
if (!$self->{no_plater}) {
$self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub {
$self->select_tab(0);
}, undef, 'application_view_tile.png');
$tab_offset += 1;
}
if (!$self->{no_controller}) {
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
$self->select_tab(1);
}, undef, 'printer_empty.png');
$tab_offset += 1;
}
if ($tab_offset > 0) {
$windowMenu->AppendSeparator();
}
$self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub {
$self->select_tab($tab_offset+0);
$self->_append_menu_item($settingsMenu, "P&rint Settings…\tCtrl+1", 'Show the print settings editor', sub {
$self->{plater}->show_preset_editor('print');
}, undef, 'cog.png');
$self->_append_menu_item($windowMenu, "Select &Filament Settings Tab\tCtrl+3", 'Show the filament settings', sub {
$self->select_tab($tab_offset+1);
$self->_append_menu_item($settingsMenu, "&Filament Settings…\tCtrl+2", 'Show the filament settings editor', sub {
$self->{plater}->show_preset_editor('filament');
}, undef, 'spool.png');
$self->_append_menu_item($windowMenu, "Select Print&er Settings Tab\tCtrl+4", 'Show the printer settings', sub {
$self->select_tab($tab_offset+2);
$self->_append_menu_item($settingsMenu, "Print&er Settings…\tCtrl+3", 'Show the printer settings editor', sub {
$self->{plater}->show_preset_editor('printer');
}, undef, 'printer_empty.png');
}
# View menu
if (!$self->{no_plater}) {
{
$self->{viewMenu} = Wx::Menu->new;
$self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); });
$self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, "Rear" , 'Rear View' , sub { $self->select_view('rear' ); });
$self->_append_menu_item($self->{viewMenu}, "Left" , 'Left View' , sub { $self->select_view('left' ); });
$self->_append_menu_item($self->{viewMenu}, "Right" , 'Right View' , sub { $self->select_view('right' ); });
$self->_append_menu_item($self->{viewMenu}, "Top\tCtrl+4" , 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, "Bottom\tCtrl+5" , 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, "Left\tCtrl+6" , 'Left View' , sub { $self->select_view('left' ); });
$self->_append_menu_item($self->{viewMenu}, "Right\tCtrl+7" , 'Right View' , sub { $self->select_view('right' ); });
$self->_append_menu_item($self->{viewMenu}, "Front\tCtrl+8" , 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, "Back\tCtrl+9" , 'Back View' , sub { $self->select_view('back' ); });
$self->_append_menu_item($self->{viewMenu}, "Diagonal\tCtrl+0", 'Diagonal View', sub { $self->select_view('diagonal'); });
$self->{viewMenu}->AppendSeparator();
$self->{color_toolpaths_by_role} = $self->_append_menu_item($self->{viewMenu},
"Color Toolpaths by Role",
@ -331,6 +248,22 @@ sub _init_menubar {
}
}
# Window menu
my $windowMenu = Wx::Menu->new;
{
$self->_append_menu_item($windowMenu, "&Plater\tCtrl+T", 'Show the plater', sub {
$self->select_tab(0);
}, undef, 'application_view_tile.png');
$self->_append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub {
$self->select_tab(1);
}, undef, 'printer_empty.png') if !$self->{no_controller};
$self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
$self->{plater}->pause_background_process;
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;
$self->{plater}->resume_background_process;
}, undef, 'film.png');
}
# Help menu
my $helpMenu = Wx::Menu->new;
{
@ -362,8 +295,9 @@ sub _init_menubar {
$menubar->Append($fileMenu, "&File");
$menubar->Append($self->{plater_menu}, "&Plater") if $self->{plater_menu};
$menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu};
$menubar->Append($windowMenu, "&Window");
$menubar->Append($settingsMenu, "&Settings");
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
$menubar->Append($windowMenu, "&Window");
$menubar->Append($helpMenu, "&Help");
$self->SetMenuBar($menubar);
}
@ -389,7 +323,7 @@ sub quick_slice {
my $progress_dialog;
eval {
# validate configuration
my $config = $self->config;
my $config = $self->{plater}->config;
$config->validate;
# select input file
@ -531,7 +465,7 @@ sub repair_stl {
sub export_config {
my $self = shift;
my $config = $self->config;
my $config = $self->{plater}->config;
eval {
# validate configuration
$config->validate;
@ -557,7 +491,6 @@ sub load_config_file {
my ($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",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
@ -568,9 +501,10 @@ sub load_config_file {
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings;
$last_config = $file;
for my $tab (values %{$self->{options_tabs}}) {
$tab->load_config_file($file);
}
my $name = wxTheApp->add_external_preset($file);
$self->{plater}->load_presets;
$self->{plater}->select_preset_by_name($name, $_) for qw(print filament printer);
}
sub export_configbundle {
@ -578,7 +512,7 @@ sub export_configbundle {
eval {
# validate current configuration in case it's dirty
$self->config->validate;
$self->{plater}->config->validate;
};
Slic3r::GUI::catch_error($self) and return;
@ -593,18 +527,14 @@ sub export_configbundle {
# 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 mode);
$ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter);
$ini->{presets} = $Slic3r::GUI::Settings->{presets};
if (-e "$Slic3r::GUI::datadir/simple.ini") {
my $config = Slic3r::Config->load("$Slic3r::GUI::datadir/simple.ini");
$ini->{simple} = $config->as_ini->{_};
}
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->{_};
my @presets = @{wxTheApp->presets->{$section}};
foreach my $preset (@presets) {
next if $preset->default || $preset->external;
$ini->{"$section:" . $preset->name} = $preset->load_config->as_ini->{_};
}
}
@ -639,15 +569,6 @@ sub load_configbundle {
$Slic3r::GUI::Settings->{presets} = $ini->{presets};
wxTheApp->save_settings;
}
if ($ini->{simple}) {
my $config = Slic3r::Config->load_ini_hash($ini->{simple});
$config->save("$Slic3r::GUI::datadir/simple.ini");
if ($self->{mode} eq 'simple') {
foreach my $tab (values %{$self->{options_tabs}}) {
$tab->load_config($config) for values %{$self->{options_tabs}};
}
}
}
my $imported = 0;
INI_BLOCK: foreach my $ini_category (sort keys %$ini) {
next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
@ -656,11 +577,11 @@ sub load_configbundle {
next if $skip_no_id && !$config->get($section . "_settings_id");
{
my %current_presets = Slic3r::GUI->presets($section);
my @current_presets = @{wxTheApp->presets->{$section}};
my %current_ids = map { $_ => 1 }
grep $_,
map Slic3r::Config->load($_)->get($section . "_settings_id"),
values %current_presets;
map $_->dirty_config->get($section . "_settings_id"),
@current_presets;
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
}
@ -668,148 +589,32 @@ sub load_configbundle {
Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name;
$imported++;
}
if ($self->{mode} eq 'expert') {
foreach my $tab (values %{$self->{options_tabs}}) {
$tab->load_presets;
}
}
$self->{plater}->load_presets;
return if !$imported;
my $message = sprintf "%d presets successfully imported.", $imported;
if ($self->{mode} eq 'simple' && $Slic3r::GUI::Settings->{_}{mode} eq 'expert') {
Slic3r::GUI::show_info($self, "$message You need to restart Slic3r to make the changes effective.");
} else {
Slic3r::GUI::show_info($self, $message);
}
}
sub load_config {
my $self = shift;
my ($config) = @_;
my ($self, $config) = @_;
foreach my $tab (values %{$self->{options_tabs}}) {
$tab->load_config($config);
}
if ($self->{plater}) {
$self->{plater}->on_config_change($config);
}
$self->{plater}->load_config($config);
}
sub config_wizard {
my $self = shift;
return unless $self->check_unsaved_changes;
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
if ($self->{mode} eq 'expert') {
for my $tab (values %{$self->{options_tabs}}) {
$tab->select_default_preset;
}
} else {
# TODO: select default settings in simple mode
}
$self->load_config($config);
if ($self->{mode} eq 'expert') {
for my $tab (values %{$self->{options_tabs}}) {
$tab->save_preset('My Settings');
foreach my $group (qw(print filament printer)) {
my $name = 'My Settings';
$config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $group, $name);
$Slic3r::GUI::Settings->{presets}{$group} = "$name.ini";
$self->{plater}->load_presets;
$self->{plater}->select_preset_by_name($name, $group);
}
}
}
}
=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 || $self->{mode} eq 'simple') {
$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);
}
if (!$filament_config) {
$filament_config = $config->clone;
next;
}
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);
}
}
}
my $config = Slic3r::Config->merge(
Slic3r::Config->new_from_defaults,
$self->{options_tabs}{print}->config,
$self->{options_tabs}{printer}->config,
$filament_config,
);
if ($self->{mode} eq 'simple') {
# set some sensible defaults
$config->set('first_layer_height', $config->nozzle_diameter->[0]);
$config->set('avoid_crossing_perimeters', 1);
$config->set('infill_every_layers', 10);
} else {
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) = @_;
if ($self->{mode} eq 'simple') {
return '';
}
return map $self->{options_tabs}{filament}->get_preset($_)->name,
$self->{plater}->filament_presets;
}
sub check_unsaved_changes {
my $self = shift;
my @dirty = ();
foreach my $tab (values %{$self->{options_tabs}}) {
push @dirty, $tab->title if $tab->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 1;
}
sub select_tab {
@ -820,9 +625,8 @@ sub select_tab {
# Set a camera direction, zoom to all objects.
sub select_view {
my ($self, $direction) = @_;
if (! $self->{no_plater}) {
$self->{plater}->select_view($direction);
}
}
sub _append_menu_item {
@ -830,19 +634,10 @@ sub _append_menu_item {
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description, $kind);
$self->_set_menu_item_icon($item, $icon);
wxTheApp->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));
}
}
1;

View File

@ -1,4 +1,4 @@
# A dialog group object. Used by the Tab, SimpleTab, Preferences dialog, ManualControlDialog etc.
# A dialog group object. Used by the PresetEditor, Preferences dialog, ManualControlDialog etc.
package Slic3r::GUI::OptionsGroup;
use Moo;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
# Included in ObjectSettingsDialog -> ObjectPartsPanel.
# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh.
# Maintains, displays, adds and removes overrides of slicing parameters.
package Slic3r::GUI::Plater::OverrideSettingsPanel;
use strict;
@ -16,13 +15,26 @@ use constant ICON_MATERIAL => 0;
use constant ICON_SOLIDMESH => 1;
use constant ICON_MODIFIERMESH => 2;
my %icons = (
'Advanced' => 'wand.png',
'Extruders' => 'funnel.png',
'Extrusion Width' => 'funnel.png',
'Infill' => 'infill.png',
'Layers and Perimeters' => 'layers.png',
'Skirt and brim' => 'box.png',
'Speed' => 'time.png',
'Speed > Acceleration' => 'time.png',
'Support material' => 'building.png',
);
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $params{size} // wxDefaultSize, wxTAB_TRAVERSAL);
$self->{default_config} = Slic3r::Config->new;
$self->{config} = Slic3r::Config->new;
$self->{on_change} = $params{on_change};
$self->{editable} = 1;
$self->{fixed_options} = {};
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
@ -35,15 +47,31 @@ sub new {
# create the button
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("Override one more option")
if $btn->can('SetToolTipString');
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
my $last_cat = '';
foreach my $opt_key (@{$self->{options}}) {
my $id = &Wx::NewId();
$menu->Append($id, $self->{option_labels}{$opt_key});
# add icon, if we have one for this category
my $icon;
if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) {
if ($last_cat && $cat ne $last_cat) {
$menu->AppendSeparator;
}
$last_cat = $cat;
$icon = $icons{$cat};
}
my $menuItem = $menu->Append($id, $self->{option_labels}{$opt_key});
wxTheApp->set_menu_item_icon($menuItem, $icon) if $icon;
EVT_MENU($menu, $id, sub {
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
$self->update_optgroup;
$self->{on_change}->() if $self->{on_change};
$self->{on_change}->($opt_key) if $self->{on_change};
});
}
$self->PopupMenu($menu, $btn->GetPosition);
@ -64,39 +92,59 @@ sub new {
return $self;
}
# Sets the config used to get the default values for user-added options.
sub set_default_config {
my ($self, $config) = @_;
$self->{default_config} = $config;
}
# Sets the target config, whose options will be displayed in the OptionsGroup.
sub set_config {
my ($self, $config) = @_;
$self->{config} = $config;
$self->update_optgroup;
}
# Sets the options listed in the Add button.
sub set_opt_keys {
my ($self, $opt_keys) = @_;
# sort options by category+label
$self->{option_labels} = {
map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) } @$opt_keys
$self->{option_labels} = {};
foreach my $opt_key (@$opt_keys) {
my $def = $Slic3r::Config::Options->{$opt_key} or next;
if (!$def->{category}) {
#printf "Skipping %s\n", $opt_key;
next;
}
$self->{option_labels}{$opt_key} = sprintf '%s > %s',
$def->{category},
$def->{full_label} // $def->{label};
};
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } keys %{$self->{option_labels}} ];
}
# Sets the options that user can't remove.
sub set_fixed_options {
my ($self, $opt_keys) = @_;
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
$self->update_optgroup;
}
sub fixed_options {
my ($self) = @_;
return keys %{$self->{fixed_options}};
}
sub update_optgroup {
my $self = shift;
$self->{options_sizer}->Clear(1);
return if !defined $self->{config};
$self->{btn_add}->Show($self->{editable});
my %categories = ();
foreach my $opt_key (@{$self->{config}->get_keys}) {
my $category = $Slic3r::Config::Options->{$opt_key}{category};
@ -104,7 +152,8 @@ sub update_optgroup {
push @{$categories{$category}}, $opt_key;
}
foreach my $category (sort keys %categories) {
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
my $optgroup;
$optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
parent => $self,
title => $category,
config => $self->{config},
@ -112,27 +161,33 @@ sub update_optgroup {
label_font => $Slic3r::GUI::small_font,
sidetext_font => $Slic3r::GUI::small_font,
label_width => 120,
on_change => sub { $self->{on_change}->() if $self->{on_change} },
on_change => sub {
my ($opt_key) = @_;
$self->{on_change}->($opt_key) if $self->{on_change};
},
extra_column => sub {
my ($line) = @_;
my $opt_key = $line->get_options->[0]->opt_id; # we assume that we have one option per line
my $opt_id = $line->get_options->[0]->opt_id; # we assume that we have one option per line
my ($opt_key, $opt_index) = @{ $optgroup->_opt_map->{$opt_id} };
# disallow deleting fixed options
return undef if $self->{fixed_options}{$opt_key};
return undef if $self->{fixed_options}{$opt_key} || !$self->{editable};
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);
$self->{on_change}->() if $self->{on_change};
$self->{on_change}->($opt_key) if $self->{on_change};
wxTheApp->CallAfter(sub { $self->update_optgroup });
});
return $btn;
},
);
foreach my $opt_key (sort @{$categories{$category}}) {
$optgroup->append_single_option_line($opt_key);
# For array options we override the first value.
my $opt_index = (ref($self->{config}->get($opt_key)) eq 'ARRAY') ? 0 : -1;
$optgroup->append_single_option_line($opt_key, $opt_index);
}
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0);
}
@ -154,4 +209,11 @@ sub disable {
$self->Disable;
}
# Shows or hides the Add button.
sub set_editable {
my ($self, $editable) = @_;
$self->{editable} = $editable;
}
1;

View File

@ -20,16 +20,6 @@ sub new {
},
label_width => 200,
);
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'mode',
type => 'select',
label => 'Mode',
tooltip => 'Choose between a simpler, basic mode and an expert mode with more options and more complicated interface.',
labels => ['Simple','Expert'],
values => ['simple','expert'],
default => $Slic3r::GUI::Settings->{_}{mode},
width => 100,
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'version_check',
type => 'bool',

210
lib/Slic3r/GUI/Preset.pm Normal file
View File

@ -0,0 +1,210 @@
package Slic3r::GUI::Preset;
use Moo;
use Unicode::Normalize;
use Wx qw(:dialog :icon :id wxTheApp);
has 'group' => (is => 'ro', required => 1);
has 'default' => (is => 'ro', default => sub { 0 });
has 'external' => (is => 'ro', default => sub { 0 });
has 'name' => (is => 'rw', required => 1);
has 'file' => (is => 'rw');
has '_config' => (is => 'rw', default => sub { Slic3r::Config->new });
has '_dirty_config' => (is => 'ro', default => sub { Slic3r::Config->new });
sub BUILD {
my ($self) = @_;
$self->name(Unicode::Normalize::NFC($self->name));
}
sub _loaded {
my ($self) = @_;
return !$self->_config->empty;
}
sub dirty_options {
my ($self) = @_;
my @dirty = ();
# Options present in both configs with different values:
push @dirty, @{$self->_config->diff($self->_dirty_config)};
# Overrides added to the dirty config:
my @extra = $self->_group_class->overriding_options;
push @dirty, grep { !$self->_config->has($_) && $self->_dirty_config->has($_) } @extra;
# Overrides removed from the dirty config:
push @dirty, grep { $self->_config->has($_) && !$self->_dirty_config->has($_) } @extra;
return @dirty;
}
sub dirty {
my ($self) = @_;
return !!$self->dirty_options;
}
sub dropdown_name {
my ($self) = @_;
my $name = $self->name;
$name .= " (modified)" if $self->dirty;
return $name;
}
sub file_exists {
my ($self) = @_;
die "Can't call file_exists() on a non-file preset" if !$self->file;
return -e Slic3r::encode_path($self->file);
}
sub rename {
my ($self, $name) = @_;
$self->name($name);
$self->file(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->group, $name);
}
sub prompt_unsaved_changes {
my ($self, $parent) = @_;
if ($self->dirty) {
my $name = $self->default ? 'Default preset' : "Preset \"" . $self->name . "\"";
my $opts = '';
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";
}
$opts .= "- $name\n";
}
my $msg = sprintf "%s has unsaved changes:\n%s\nDo you want to save them?", $name, $opts;
my $confirm = Wx::MessageDialog->new($parent, $msg,
'Unsaved Changes', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_QUESTION);
$confirm->SetYesNoCancelLabels('Save', 'Discard', 'Cancel');
my $res = $confirm->ShowModal;
if ($res == wxID_CANCEL) {
return 0;
} elsif ($res == wxID_YES) {
return $self->save($self->default ? undef : $self->name);
} elsif ($res == wxID_NO) {
$self->dismiss_changes;
return 1;
}
}
return 1;
}
sub save {
my ($self, $name, $parent) = @_;
if (!$name) {
my $default_name = $self->default ? 'Untitled' : $self->name;
$default_name =~ s/\.ini$//i;
my $dlg = Slic3r::GUI::SavePresetWindow->new($parent,
default => $default_name,
values => [ map $_->name, grep !$_->default && !$_->external, @{wxTheApp->presets->{$self->name}} ],
);
return 0 unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name;
}
$self->rename($name);
if (!$self->file) {
die "Calling save() without setting filename";
}
$self->_config->clear;
$self->_config->apply($self->_dirty_config);
$self->_config->save($self->file);
wxTheApp->load_presets;
return 1;
}
sub dismiss_changes {
my ($self) = @_;
$self->_dirty_config->clear;
$self->_dirty_config->apply($self->_config);
}
sub delete {
my ($self) = @_;
die "Default config can't be deleted" if $self->default;
die "External configs can't be deleted" if $self->external;
# Otherwise wxTheApp->load_presets() will keep it
$self->dismiss_changes;
if ($self->file) {
unlink Slic3r::encode_path($self->file) if $self->file_exists;
$self->file(undef);
}
}
# This returns the loaded config with the dirty options applied.
sub dirty_config {
my ($self) = @_;
$self->load_config if !$self->_loaded;
return $self->_dirty_config->clone;
}
sub load_config {
my ($self) = @_;
my @keys = $self->_group_class->options;
my @extra_keys = $self->_group_class->overriding_options;
if ($self->default) {
$self->_config(Slic3r::Config->new_from_defaults(@keys));
} elsif ($self->file) {
if (!$self->file_exists) {
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
return undef;
}
my $external_config = Slic3r::Config->load($self->file);
if (!@keys) {
$self->_config($external_config);
} else {
# apply preset values on top of defaults
my $config = Slic3r::Config->new_from_defaults(@keys);
$config->set($_, $external_config->get($_))
for grep $external_config->has($_), @keys;
# For extra_keys we don't populate defaults.
if (@extra_keys && !$self->external) {
$config->set($_, $external_config->get($_))
for grep $external_config->has($_), @extra_keys;
}
$self->_config($config);
}
}
$self->_dirty_config->apply($self->_config);
return $self->_config;
}
sub _group_class {
my ($self) = @_;
return "Slic3r::GUI::PresetEditor::".ucfirst $self->group;
}
1;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
package Slic3r::GUI::PresetEditorDialog;
use strict;
use warnings;
use Wx qw(:dialog :id :misc :sizer :button :icon wxTheApp WXK_ESCAPE);
use Wx::Event qw(EVT_CLOSE EVT_CHAR_HOOK);
use base qw(Wx::Dialog Class::Accessor);
use utf8;
__PACKAGE__->mk_accessors(qw(preset_editor));
sub new {
my ($class, $parent) = @_;
my $self = $class->SUPER::new($parent, -1, "Settings", wxDefaultPosition, [900,500],
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_EX_METAL);
$self->preset_editor($self->preset_editor_class->new($self));
$self->SetTitle($self->preset_editor->title);
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add($self->preset_editor, 1, wxEXPAND);
$self->SetSizer($sizer);
#$sizer->SetSizeHints($self);
if (0) {
my $buttons = $self->CreateStdDialogButtonSizer(wxCLOSE);
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
}
wxTheApp->restore_window_pos($self, "preset_editor");
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
# save window size
wxTheApp->save_window_pos($self, "preset_editor");
# propagate event
$event->Skip;
});
EVT_CHAR_HOOK($self, sub {
my (undef, $event) = @_;
if ($event->GetKeyCode == WXK_ESCAPE) {
$self->Close;
} else {
$event->Skip;
}
});
return $self;
}
package Slic3r::GUI::PresetEditorDialog::Printer;
use base qw(Slic3r::GUI::PresetEditorDialog);
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Printer" }
package Slic3r::GUI::PresetEditorDialog::Filament;
use base qw(Slic3r::GUI::PresetEditorDialog);
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Filament" }
package Slic3r::GUI::PresetEditorDialog::Print;
use base qw(Slic3r::GUI::PresetEditorDialog);
sub preset_editor_class { "Slic3r::GUI::PresetEditor::Print" }
1;

View File

@ -44,7 +44,7 @@ sub new {
$self->config(Slic3r::Config->new_from_defaults(
qw(serial_port serial_speed bed_shape start_gcode end_gcode z_offset)
));
$self->config->apply(wxTheApp->{mainframe}->config);
$self->config->apply(wxTheApp->{mainframe}->{plater}->config);
my @optgroups = ();
{
@ -520,9 +520,6 @@ sub _close {
}
wxTheApp->{mainframe}->Show;
my $printer_tab = wxTheApp->{mainframe}{options_tabs}{printer};
$printer_tab->load_config($self->config);
$self->EndModal(wxID_OK);
}
@ -555,7 +552,7 @@ sub BUILD {
# init print
{
my $print = Slic3r::SLAPrint->new(wxTheApp->{mainframe}->{plater}->{model});
$print->apply_config(wxTheApp->{mainframe}->config);
$print->apply_config(wxTheApp->{mainframe}->{plater}->config);
$self->_print($print);
$self->screen->print($print);

View File

@ -10,7 +10,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, "SLA/DLP Print", wxDefaultPosition, wxDefaultSize);
$self->config(Slic3r::Config::SLAPrint->new);
$self->config->apply_dynamic(wxTheApp->{mainframe}->config);
$self->config->apply_dynamic(wxTheApp->{mainframe}->{plater}->config);
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
my $new_optgroup = sub {
@ -103,8 +103,6 @@ sub _accept {
return;
}
wxTheApp->{mainframe}->load_config($self->config->dynamic);
$self->EndModal(wxID_OK);
$self->Close; # needed on Linux
@ -113,6 +111,9 @@ sub _accept {
# this double invocation is needed for properly hiding the MainFrame
$projector->Show;
$projector->ShowModal;
# TODO: diff the new config with the selected presets and prompt the user for
# applying the changes to them.
}
1;

View File

@ -1,351 +0,0 @@
# The "Simple" Print Settings tab.
# The "Simple" mode is enabled by File->Preferences dialog.
package Slic3r::GUI::SimpleTab;
use strict;
use warnings;
use utf8;
use File::Basename qw(basename);
use List::Util qw(first);
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :window :systemsettings);
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN);
use base 'Wx::ScrolledWindow';
sub new {
my $class = shift;
my ($parent, %params) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
$self->SetScrollbars(1, 1, 1, 1);
$self->{config} = Slic3r::Config->new;
$self->{optgroups} = [];
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->SetSizer($self->{vsizer});
$self->build;
$self->_update;
{
my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize);
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
$self->{vsizer}->Add($label, 0, wxEXPAND | wxALL, 10);
}
return $self;
}
sub init_config_options {
my ($self, @opt_keys) = @_;
$self->{config}->apply(Slic3r::Config->new_from_defaults(@opt_keys));
}
sub new_optgroup {
my ($self, $title, %params) = @_;
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
parent => $self,
title => $title,
config => $self->{config},
label_width => $params{label_width} // 200,
on_change => sub { $self->_on_value_change(@_) },
);
push @{$self->{optgroups}}, $optgroup;
$self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
return $optgroup;
}
sub load_config_file {
my $self = shift;
my ($file) = @_;
my $config = Slic3r::Config->load($file);
$self->load_config($config);
}
sub load_config {
my $self = shift;
my ($config) = @_;
foreach my $opt_key (@{$self->{config}->get_keys}) {
next unless $config->has($opt_key);
$self->{config}->set($opt_key, $config->get($opt_key));
}
$_->reload_config for @{$self->{optgroups}};
$self->_update;
}
sub load_presets {}
sub is_dirty { 0 }
sub config { $_[0]->{config}->clone }
sub _update {}
sub on_value_change {
my ($self, $cb) = @_;
$self->{on_value_change} = $cb;
}
sub on_presets_changed {}
# propagate event to the parent
sub _on_value_change {
my $self = shift;
$self->{on_value_change}->(@_) if $self->{on_value_change};
$self->_update;
}
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;
}
return undef;
}
package Slic3r::GUI::SimpleTab::Print;
use base 'Slic3r::GUI::SimpleTab';
use Wx qw(:sizer);
sub name { 'print' }
sub title { 'Print Settings' }
sub build {
my $self = shift;
$self->init_config_options(qw(
layer_height perimeters top_solid_layers bottom_solid_layers
fill_density fill_pattern top_infill_pattern bottom_infill_pattern
support_material support_material_spacing raft_layers
support_material_contact_distance dont_support_bridges
perimeter_speed infill_speed travel_speed
brim_width
xy_size_compensation
));
{
my $optgroup = $self->new_optgroup('General');
$optgroup->append_single_option_line('layer_height');
$optgroup->append_single_option_line('perimeters');
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Solid layers',
);
$line->append_option($optgroup->get_option('top_solid_layers'));
$line->append_option($optgroup->get_option('bottom_solid_layers'));
$optgroup->append_line($line);
}
{
my $optgroup = $self->new_optgroup('Infill');
$optgroup->append_single_option_line('fill_density');
$optgroup->append_single_option_line('fill_pattern');
$optgroup->append_single_option_line('top_infill_pattern');
$optgroup->append_single_option_line('bottom_infill_pattern');
}
{
my $optgroup = $self->new_optgroup('Support material');
$optgroup->append_single_option_line('support_material');
$optgroup->append_single_option_line('support_material_spacing');
$optgroup->append_single_option_line('support_material_contact_distance');
$optgroup->append_single_option_line('dont_support_bridges');
$optgroup->append_single_option_line('raft_layers');
}
{
my $optgroup = $self->new_optgroup('Speed');
$optgroup->append_single_option_line('perimeter_speed');
$optgroup->append_single_option_line('infill_speed');
$optgroup->append_single_option_line('travel_speed');
}
{
my $optgroup = $self->new_optgroup('Brim');
$optgroup->append_single_option_line('brim_width');
}
{
my $optgroup = $self->new_optgroup('Other');
$optgroup->append_single_option_line('xy_size_compensation');
}
}
sub _update {
my ($self) = @_;
my $config = $self->{config};
my $have_perimeters = $config->perimeters > 0;
$self->get_field($_)->toggle($have_perimeters)
for qw(perimeter_speed);
my $have_infill = $config->fill_density > 0;
my $have_solid_infill = $config->top_solid_layers > 0 || $config->bottom_solid_layers > 0;
$self->get_field($_)->toggle($have_infill)
for qw(fill_pattern);
$self->get_field($_)->toggle($have_solid_infill)
for qw(top_infill_pattern bottom_infill_pattern);
$self->get_field($_)->toggle($have_infill || $have_solid_infill)
for qw(infill_speed);
my $have_support_material = $config->support_material || $config->raft_layers > 0;
$self->get_field($_)->toggle($have_support_material)
for qw(support_material_spacing dont_support_bridges
support_material_contact_distance);
}
package Slic3r::GUI::SimpleTab::Filament;
use base 'Slic3r::GUI::SimpleTab';
sub name { 'filament' }
sub title { 'Filament Settings' }
sub build {
my $self = shift;
$self->init_config_options(qw(
filament_diameter extrusion_multiplier
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
));
{
my $optgroup = $self->new_optgroup('Filament');
$optgroup->append_single_option_line('filament_diameter', 0);
$optgroup->append_single_option_line('extrusion_multiplier', 0);
}
{
my $optgroup = $self->new_optgroup('Temperature (°C)');
{
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Extruder',
);
$line->append_option($optgroup->get_option('first_layer_temperature', 0));
$line->append_option($optgroup->get_option('temperature', 0));
$optgroup->append_line($line);
}
{
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Bed',
);
$line->append_option($optgroup->get_option('first_layer_bed_temperature'));
$line->append_option($optgroup->get_option('bed_temperature'));
$optgroup->append_line($line);
}
}
}
package Slic3r::GUI::SimpleTab::Printer;
use base 'Slic3r::GUI::SimpleTab';
use Wx qw(:sizer :button :bitmap :misc :id);
use Wx::Event qw(EVT_BUTTON);
sub name { 'printer' }
sub title { 'Printer Settings' }
sub build {
my $self = shift;
$self->init_config_options(qw(
bed_shape
z_offset
gcode_flavor
nozzle_diameter
retract_length retract_lift wipe
start_gcode
end_gcode
));
{
my $bed_shape_widget = sub {
my ($parent) = @_;
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$btn->SetFont($Slic3r::GUI::small_font);
if ($Slic3r::GUI::have_button_icons) {
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("cog.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->_on_value_change('bed_shape', $value);
}
});
return $sizer;
};
my $optgroup = $self->new_optgroup('Size and coordinates');
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Bed shape',
widget => $bed_shape_widget,
);
$optgroup->append_line($line);
$optgroup->append_single_option_line('z_offset');
}
{
my $optgroup = $self->new_optgroup('Firmware');
$optgroup->append_single_option_line('gcode_flavor');
}
{
my $optgroup = $self->new_optgroup('Extruder');
$optgroup->append_single_option_line('nozzle_diameter', 0);
}
{
my $optgroup = $self->new_optgroup('Retraction');
$optgroup->append_single_option_line('retract_length', 0);
$optgroup->append_single_option_line('retract_lift', 0);
$optgroup->append_single_option_line('wipe', 0);
}
{
my $optgroup = $self->new_optgroup('Start G-code',
label_width => 0,
);
my $option = $optgroup->get_option('start_gcode');
$option->full_width(1);
$option->height(150);
$optgroup->append_single_option_line($option);
}
{
my $optgroup = $self->new_optgroup('End G-code',
label_width => 0,
);
my $option = $optgroup->get_option('end_gcode');
$option->full_width(1);
$option->height(150);
$optgroup->append_single_option_line($option);
}
}
sub _update {
my ($self) = @_;
my $config = $self->{config};
my $have_retraction = $config->retract_length->[0] > 0;
$self->get_field($_, 0)->toggle($have_retraction)
for qw(retract_lift wipe);
}
1;

View File

@ -36,8 +36,6 @@ my %cli_options = ();
'autosave=s' => \$opt{autosave},
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
'no-controller' => \$opt{no_controller},
'no-plater' => \$opt{no_plater},
'gui-mode=s' => \$opt{gui_mode},
'datadir=s' => \$opt{datadir},
'export-svg' => \$opt{export_svg},
'merge|m' => \$opt{merge},
@ -78,28 +76,26 @@ if ($opt{load}) {
$opt{ignore_nonexistent_config} or die "Cannot find specified configuration file ($configfile).\n";
}
}
# expand shortcuts before applying, otherwise destination values would be already filled with defaults
$_->normalize for @external_configs;
}
# process command line options
my $cli_config = Slic3r::Config->new;
foreach my $c (@external_configs, Slic3r::Config->new_from_cli(%cli_options)) {
$c->normalize; # expand shortcuts before applying, otherwise destination values would be already filled with defaults
$cli_config->apply($c);
}
my $cli_config = Slic3r::Config->new_from_cli(%cli_options);
$cli_config->normalize; # expand shortcuts
# save configuration
if ($opt{save}) {
if (@{$cli_config->get_keys} > 0) {
$cli_config->save($opt{save});
my $config = $cli_config->clone;
$config->apply($_) for @external_configs;
if (@{$config->get_keys} > 0) {
$config->save($opt{save});
} else {
Slic3r::Config->new_from_defaults->save($opt{save});
}
}
# apply command line config on top of default config
my $config = Slic3r::Config->new_from_defaults;
$config->apply($cli_config);
# launch GUI
my $gui;
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
@ -107,9 +103,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
no warnings 'once';
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
$Slic3r::GUI::no_controller = $opt{no_controller};
$Slic3r::GUI::no_plater = $opt{no_plater};
$Slic3r::GUI::mode = $opt{gui_mode};
$Slic3r::GUI::autosave = $opt{autosave};
$Slic3r::GUI::autosave = Slic3r::decode_path($opt{autosave} // '');
$Slic3r::GUI::threads = $opt{threads};
}
$gui = Slic3r::GUI->new;
@ -119,7 +113,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
$gui->{mainframe}->load_config($cli_config);
foreach my $input_file (@ARGV) {
$input_file = Slic3r::decode_path($input_file);
$gui->{mainframe}{plater}->load_file($input_file) unless $opt{no_plater};
$gui->{mainframe}{plater}->load_file($input_file);
}
});
$gui->MainLoop;
@ -128,6 +122,9 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
die $@ if $@ && $opt{gui};
if (@ARGV) { # slicing from command line
# apply command line config on top of default config
my $config = Slic3r::Config->new_from_defaults;
$config->apply($cli_config);
$config->validate;
if ($opt{repair}) {
@ -330,7 +327,6 @@ $j
--gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab
--gui-mode Overrides the configured mode (simple/expert)
--autosave <file> Automatically export current configuration to the specified file
Output options:

BIN
var/wand.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

View File

@ -248,21 +248,24 @@ ConfigBase::has(const t_config_option_key &opt_key) {
void
ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) {
// get list of option keys to apply
t_config_option_keys opt_keys = other.keys();
// apply all options
this->apply_only(other, other.keys(), ignore_nonexistent);
}
void
ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent) {
// loop through options and apply them
for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) {
ConfigOption* my_opt = this->option(*it, true);
for (const t_config_option_key &opt_key : opt_keys) {
ConfigOption* my_opt = this->option(opt_key, true);
if (my_opt == NULL) {
if (ignore_nonexistent == false) throw "Attempt to apply non-existent option";
continue;
}
// not the most efficient way, but easier than casting pointers to subclasses
bool res = my_opt->deserialize( other.option(*it)->serialize() );
bool res = my_opt->deserialize( other.option(opt_key)->serialize() );
if (!res) {
std::string error = "Unexpected failure when deserializing serialized value for " + *it;
std::string error = "Unexpected failure when deserializing serialized value for " + opt_key;
CONFESS(error.c_str());
}
}
@ -524,6 +527,16 @@ DynamicConfig::erase(const t_config_option_key &opt_key) {
this->options.erase(opt_key);
}
void
DynamicConfig::clear() {
this->options.clear();
}
bool
DynamicConfig::empty() const {
return this->options.empty();
}
void
DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra)
{

View File

@ -650,6 +650,7 @@ class ConfigBase
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0;
virtual t_config_option_keys keys() const = 0;
void apply(const ConfigBase &other, bool ignore_nonexistent = false);
void apply_only(const ConfigBase &other, const t_config_option_keys &opt_keys, bool ignore_nonexistent = false);
bool equals(ConfigBase &other);
t_config_option_keys diff(ConfigBase &other);
std::string serialize(const t_config_option_key &opt_key) const;
@ -676,6 +677,8 @@ class DynamicConfig : public virtual ConfigBase
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false);
t_config_option_keys keys() const;
void erase(const t_config_option_key &opt_key);
void clear();
bool empty() const;
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra);
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);

View File

@ -25,6 +25,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("avoid_crossing_perimeters", coBool);
def->label = "Avoid crossing perimeters";
def->category = "Layers and Perimeters";
def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation.";
def->cli = "avoid-crossing-perimeters!";
def->default_value = new ConfigOptionBool(false);
@ -82,6 +83,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("bridge_acceleration", coFloat);
def->label = "Bridge";
def->category = "Speed > Acceleration";
def->tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges.";
def->sidetext = "mm/s²";
def->cli = "bridge-acceleration=f";
@ -120,6 +122,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("brim_connections_width", coFloat);
def->label = "Brim connections width";
def->category = "Skirt and brim";
def->tooltip = "If set to a positive value, straight connections will be built on the first layer between adjacent objects.";
def->sidetext = "mm";
def->cli = "brim-connections-width=f";
@ -128,14 +131,20 @@ PrintConfigDef::PrintConfigDef()
def = this->add("brim_width", coFloat);
def->label = "Exterior brim width";
def->category = "Skirt and brim";
def->tooltip = "Horizontal width of the brim that will be printed around each object on the first layer.";
def->sidetext = "mm";
def->cli = "brim-width=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(0);
def = this->add("compatible_printers", coStrings);
def->label = "Compatible printers";
def->default_value = new ConfigOptionStrings();
def = this->add("complete_objects", coBool);
def->label = "Complete individual objects";
def->category = "Advanced";
def->tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware.";
def->cli = "complete-objects!";
def->default_value = new ConfigOptionBool(false);
@ -148,6 +157,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("default_acceleration", coFloat);
def->label = "Default";
def->category = "Speed > Acceleration";
def->tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all.";
def->sidetext = "mm/s²";
def->cli = "default-acceleration=f";
@ -501,6 +511,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("first_layer_acceleration", coFloat);
def->label = "First layer";
def->category = "Speed > Acceleration";
def->tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer.";
def->sidetext = "mm/s²";
def->cli = "first-layer-acceleration=f";
@ -539,6 +550,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("first_layer_speed", coFloatOrPercent);
def->label = "First layer speed";
def->category = "Speed";
def->tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds.";
def->sidetext = "mm/s or %";
def->cli = "first-layer-speed=s";
@ -610,6 +622,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("infill_acceleration", coFloat);
def->label = "Infill";
def->category = "Speed > Acceleration";
def->tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill.";
def->sidetext = "mm/s²";
def->cli = "infill-acceleration=f";
@ -648,6 +661,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("infill_first", coBool);
def->label = "Infill before perimeters";
def->category = "Infill";
def->tooltip = "This option will switch the print order of perimeters and infill, making the latter first.";
def->cli = "infill-first!";
def->default_value = new ConfigOptionBool(false);
@ -684,6 +698,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("interior_brim_width", coFloat);
def->label = "Interior brim width";
def->category = "Skirt and brim";
def->tooltip = "Horizontal width of the brim that will be printed inside object holes on the first layer.";
def->sidetext = "mm";
def->cli = "interior-brim-width=f";
@ -726,6 +741,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("max_print_speed", coFloat);
def->label = "Max print speed";
def->category = "Speed";
def->tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow.";
def->sidetext = "mm/s";
def->cli = "max-print-speed=f";
@ -734,6 +750,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("max_volumetric_speed", coFloat);
def->label = "Max volumetric speed";
def->category = "Speed";
def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports.";
def->sidetext = "mm³/s";
def->cli = "max-volumetric-speed=f";
@ -759,6 +776,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("min_skirt_length", coFloat);
def->label = "Minimum extrusion length";
def->category = "Skirt and brim";
def->tooltip = "Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder.";
def->sidetext = "mm";
def->cli = "min-skirt-length=f";
@ -799,12 +817,15 @@ PrintConfigDef::PrintConfigDef()
def = this->add("only_retract_when_crossing_perimeters", coBool);
def->label = "Only retract when crossing perimeters";
def->category = "Layers and Perimeters";
def->tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible).";
def->cli = "only-retract-when-crossing-perimeters!";
def->default_value = new ConfigOptionBool(true);
def = this->add("ooze_prevention", coBool);
def->label = "Enable";
def->full_label = "Ooze Prevention";
def->category = "Extruders";
def->tooltip = "During multi-extruder prints, this option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures.";
def->cli = "ooze-prevention!";
def->default_value = new ConfigOptionBool(false);
@ -823,8 +844,17 @@ PrintConfigDef::PrintConfigDef()
def->cli = "overhangs|detect-bridging-perimeters!";
def->default_value = new ConfigOptionBool(true);
def = this->add("overridable", coStrings);
def->label = "Overridable options";
{
ConfigOptionStrings* opt = new ConfigOptionStrings();
opt->values.push_back("support_material");
def->default_value = opt;
}
def = this->add("perimeter_acceleration", coFloat);
def->label = "Perimeters";
def->category = "Speed > Acceleration";
def->tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters.";
def->sidetext = "mm/s²";
def->cli = "perimeter-acceleration=f";
@ -882,6 +912,7 @@ PrintConfigDef::PrintConfigDef()
def->multiline = true;
def->full_width = true;
def->height = 60;
def->default_value = new ConfigOptionStrings();
def = this->add("print_settings_id", coString);
def->default_value = new ConfigOptionString("");
@ -891,6 +922,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("pressure_advance", coFloat);
def->label = "Pressure advance";
def->category = "Extruder";
def->tooltip = "When set to a non-zero value, this experimental option enables pressure regulation. It's the K constant for the advance algorithm that pushes more or less filament upon speed changes. It's useful for Bowden-tube extruders. Reasonable values are in range 0-10.";
def->cli = "pressure-advance=f";
def->min = 0;
@ -924,6 +956,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_before_travel", coFloats);
def->label = "Minimum travel after retraction";
def->category = "Retraction";
def->tooltip = "Retraction is not triggered when travel moves are shorter than this length.";
def->sidetext = "mm";
def->cli = "retract-before-travel=f@";
@ -935,6 +968,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_layer_change", coBools);
def->label = "Retract on layer change";
def->category = "Retraction";
def->tooltip = "This flag enforces a retraction whenever a Z move is done.";
def->cli = "retract-layer-change!";
{
@ -945,6 +979,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_length", coFloats);
def->label = "Length";
def->category = "Retraction";
def->full_label = "Retraction Length";
def->tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder).";
def->sidetext = "mm (zero to disable)";
@ -969,6 +1004,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_lift", coFloats);
def->label = "Lift Z";
def->category = "Retraction";
def->tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered.";
def->sidetext = "mm";
def->cli = "retract-lift=f@";
@ -981,6 +1017,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_lift_above", coFloats);
def->label = "Above Z";
def->full_label = "Only lift Z above";
def->category = "Retraction";
def->tooltip = "If you set this to a positive value, Z lift will only take place above the specified absolute Z. You can tune this setting for skipping lift on the first layers.";
def->sidetext = "mm";
def->cli = "retract-lift-above=f@";
@ -993,6 +1030,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_lift_below", coFloats);
def->label = "Below Z";
def->full_label = "Only lift Z below";
def->category = "Retraction";
def->tooltip = "If you set this to a positive value, Z lift will only take place below the specified absolute Z. You can tune this setting for limiting lift to the first layers.";
def->sidetext = "mm";
def->cli = "retract-lift-below=f@";
@ -1004,6 +1042,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_restart_extra", coFloats);
def->label = "Extra length on restart";
def->category = "Retraction";
def->tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed.";
def->sidetext = "mm";
def->cli = "retract-restart-extra=f@";
@ -1026,7 +1065,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("retract_speed", coFloats);
def->label = "Speed";
def->full_label = "Retraction Speed";
def->category = "Retraction";
def->tooltip = "The speed for retractions (it only applies to the extruder motor). If you use the Firmware Retraction option, please note this value still affects the auto-speed pressure regulator.";
def->sidetext = "mm/s";
def->cli = "retract-speed=f@";
@ -1038,7 +1077,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("seam_position", coEnum);
def->label = "Seam position";
def->category = "Layers and perimeters";
def->category = "Layers and Perimeters";
def->tooltip = "Position of perimeters starting points.";
def->cli = "seam-position=s";
def->enum_keys_map = ConfigOptionEnum<SeamPosition>::get_enum_values();
@ -1076,6 +1115,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("skirt_distance", coFloat);
def->label = "Distance from object";
def->category = "Skirt and brim";
def->tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";
def->sidetext = "mm";
def->cli = "skirt-distance=f";
@ -1084,6 +1124,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("skirt_height", coInt);
def->label = "Skirt height";
def->category = "Skirt and brim";
def->tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts.";
def->sidetext = "layers";
def->cli = "skirt-height=i";
@ -1091,6 +1132,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("skirts", coInt);
def->label = "Loops (minimum)";
def->category = "Skirt and brim";
def->full_label = "Skirt Loops";
def->tooltip = "Number of loops for the skirt. If the Minimum Extrusion Length option is set, the number of loops might be greater than the one configured here. Set this to zero to disable skirt completely.";
def->cli = "skirts=i";
@ -1185,12 +1227,15 @@ PrintConfigDef::PrintConfigDef()
def = this->add("spiral_vase", coBool);
def->label = "Spiral vase";
def->category = "Layers and Perimeters";
def->tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object.";
def->cli = "spiral-vase!";
def->default_value = new ConfigOptionBool(false);
def = this->add("standby_temperature_delta", coInt);
def->label = "Temperature variation";
def->full_label = "Standby temperature delta";
def->category = "Extruders";
def->tooltip = "Temperature difference to be applied when an extruder is not active. Enables a full-height \"sacrificial\" skirt on which the nozzles are periodically wiped.";
def->sidetext = "∆°C";
def->cli = "standby-temperature-delta=i";
@ -1309,6 +1354,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("support_material_interface_speed", coFloatOrPercent);
def->label = "↳ interface";
def->label = "Interface Speed";
def->category = "Support material interface speed";
def->gui_type = "f_enum_open";
def->category = "Support material";
@ -1456,6 +1502,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("travel_speed", coFloat);
def->label = "Travel";
def->category = "Speed";
def->tooltip = "Speed for travel moves (jumps between distant extrusion points).";
def->sidetext = "mm/s";
def->cli = "travel-speed=f";
@ -1491,6 +1538,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("wipe", coBools);
def->label = "Wipe while retracting";
def->category = "Retraction";
def->tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders.";
def->cli = "wipe!";
{
@ -1647,7 +1695,7 @@ PrintConfigBase::_handle_legacy(t_config_option_key &opt_key, std::string &value
}
if (!this->def->has(opt_key)) {
printf("Unknown option %s\n", opt_key.c_str());
//printf("Unknown option %s\n", opt_key.c_str());
opt_key = "";
return;
}

View File

@ -27,14 +27,20 @@
double get_abs_value(t_config_option_key opt_key, double ratio_over);
void apply(DynamicPrintConfig* other)
%code{% THIS->apply(*other, true); %};
void apply_only(DynamicPrintConfig* other, std::vector<std::string> opt_keys)
%code{% THIS->apply_only(*other, opt_keys, true); %};
std::vector<std::string> diff(DynamicPrintConfig* other)
%code{% RETVAL = THIS->diff(*other); %};
std::vector<std::string> diff_static(StaticPrintConfig* other)
%code{% RETVAL = THIS->diff(*other); %};
bool equals(DynamicPrintConfig* other)
%code{% RETVAL = THIS->equals(*other); %};
void apply_static(StaticPrintConfig* other)
%code{% THIS->apply(*other, true); %};
%name{get_keys} std::vector<std::string> keys();
void erase(t_config_option_key opt_key);
void clear();
bool empty();
void normalize();
%name{setenv} void setenv_();
double min_object_distance();

View File

@ -257,6 +257,12 @@ _constant()
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config(DynamicPrintConfig* config)
%code%{ RETVAL = THIS->apply_config(*config); %};
bool apply_static_config(StaticPrintConfig* config)
%code%{
DynamicPrintConfig dpc;
dpc.apply(*config);
RETVAL = THIS->apply_config(dpc);
%};
bool has_infinite_skirt();
bool has_skirt();
std::string _validate()