mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-03 18:50:39 +08:00
Changes to the GUI. New preset editor dialog. Option overrides in plater and between configs. More.
This commit is contained in:
parent
c20d44c388
commit
5d77c160c6
@ -117,7 +117,6 @@ The author of the Silk icon set is Mark James.
|
||||
--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:
|
||||
|
@ -36,12 +36,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;
|
||||
}
|
||||
|
@ -31,9 +31,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";
|
||||
@ -57,21 +58,17 @@ use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj 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 @cb;
|
||||
|
||||
our $Settings = {
|
||||
_ => {
|
||||
mode => 'simple',
|
||||
version_check => 1,
|
||||
autocenter => 1,
|
||||
invert_zoom => 0,
|
||||
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,
|
||||
no_controller => 0,
|
||||
},
|
||||
};
|
||||
|
||||
@ -96,6 +93,7 @@ sub OnInit {
|
||||
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
||||
|
||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||
$self->{external_presets} = [];
|
||||
|
||||
# locate or create data directory
|
||||
# Unix: ~/.Slic3r
|
||||
@ -123,7 +121,7 @@ sub OnInit {
|
||||
my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") };
|
||||
$Settings = $ini if $ini;
|
||||
$last_version = $Settings->{_}{version};
|
||||
$Settings->{_}{mode} ||= 'expert';
|
||||
delete $Settings->{_}{mode}; # handle legacy
|
||||
$Settings->{_}{autocenter} //= 1;
|
||||
$Settings->{_}{invert_zoom} //= 0;
|
||||
$Settings->{_}{background_processing} //= 1;
|
||||
@ -133,13 +131,17 @@ sub OnInit {
|
||||
$Settings->{_}{version} = $Slic3r::VERSION;
|
||||
$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";
|
||||
}
|
||||
|
||||
# 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);
|
||||
|
||||
@ -277,20 +279,46 @@ sub save_settings {
|
||||
}
|
||||
|
||||
sub presets {
|
||||
my ($self, $section) = @_;
|
||||
my ($self, $section, $force_default) = @_;
|
||||
|
||||
my @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";
|
||||
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;
|
||||
push @presets, Slic3r::GUI::Preset->new(
|
||||
name => $name,
|
||||
file => "$Slic3r::GUI::datadir/$section/$file",
|
||||
);
|
||||
}
|
||||
closedir $dh;
|
||||
|
||||
return %presets;
|
||||
@presets = sort { $a->name cmp $b->name }
|
||||
@presets,
|
||||
(grep -e $_->file, @{$self->{external_presets}});
|
||||
|
||||
if ($force_default || !@presets) {
|
||||
unshift @presets, Slic3r::GUI::Preset->new(
|
||||
default => 1,
|
||||
name => '- default -',
|
||||
);
|
||||
}
|
||||
|
||||
return @presets;
|
||||
}
|
||||
|
||||
sub add_external_preset {
|
||||
my ($self, $file) = @_;
|
||||
|
||||
push @{$self->{external_presets}}, my $preset = Slic3r::GUI::Preset->new(
|
||||
name => basename($file), # keep .ini suffix
|
||||
file => $file,
|
||||
external => 1,
|
||||
);
|
||||
return $preset;
|
||||
}
|
||||
|
||||
sub have_version_check {
|
||||
|
@ -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}->load_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) = @_;
|
||||
|
@ -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
|
||||
@ -80,9 +77,17 @@ sub new {
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->CanVeto && !$self->check_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
if ($event->CanVeto) {
|
||||
my $veto = 0;
|
||||
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);
|
||||
$veto = 1 if $confirm->ShowModal == wxID_YES;
|
||||
}
|
||||
if ($veto) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# save window size
|
||||
@ -107,71 +112,10 @@ sub _init_tabpanel {
|
||||
$panel->OnActivate if $panel->can('OnActivate');
|
||||
});
|
||||
|
||||
if (!$self->{no_plater}) {
|
||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "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,7 +183,7 @@ sub _init_menubar {
|
||||
}
|
||||
|
||||
# Plater menu
|
||||
unless ($self->{no_plater}) {
|
||||
{
|
||||
my $plater = $self->{plater};
|
||||
|
||||
$self->{plater_menu} = Wx::Menu->new;
|
||||
@ -252,49 +196,27 @@ sub _init_menubar {
|
||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', 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' ); });
|
||||
@ -305,6 +227,22 @@ sub _init_menubar {
|
||||
$self->_append_menu_item($self->{viewMenu}, "Right" , 'Right View' , sub { $self->select_view('right' ); });
|
||||
}
|
||||
|
||||
# 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;
|
||||
{
|
||||
@ -336,8 +274,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);
|
||||
}
|
||||
@ -363,7 +302,7 @@ sub quick_slice {
|
||||
my $progress_dialog;
|
||||
eval {
|
||||
# validate configuration
|
||||
my $config = $self->config;
|
||||
my $config = $self->{plater}->config;
|
||||
$config->validate;
|
||||
|
||||
# select input file
|
||||
@ -509,7 +448,7 @@ sub repair_stl {
|
||||
sub export_config {
|
||||
my $self = shift;
|
||||
|
||||
my $config = $self->config;
|
||||
my $config = $self->{plater}->config;
|
||||
eval {
|
||||
# validate configuration
|
||||
$config->validate;
|
||||
@ -535,7 +474,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);
|
||||
@ -546,9 +484,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 $preset = wxTheApp->add_external_preset($file);
|
||||
$self->{plater}->load_presets;
|
||||
$self->{plater}->select_preset_by_name($preset->name, $_) for qw(print filament printer);
|
||||
}
|
||||
|
||||
sub export_configbundle {
|
||||
@ -556,7 +495,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;
|
||||
|
||||
@ -571,18 +510,13 @@ 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) {
|
||||
$ini->{"$section:" . $preset->name} = $preset->load_config->as_ini->{_};
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,15 +551,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):(.+)$/;
|
||||
@ -634,11 +559,11 @@ sub load_configbundle {
|
||||
next if $skip_no_id && !$config->get($section . "_settings_id");
|
||||
|
||||
{
|
||||
my %current_presets = Slic3r::GUI->presets($section);
|
||||
my @current_presets = Slic3r::GUI->presets($section);
|
||||
my %current_ids = map { $_ => 1 }
|
||||
grep $_,
|
||||
map Slic3r::Config->load($_)->get($section . "_settings_id"),
|
||||
values %current_presets;
|
||||
map $_->load_config->get($section . "_settings_id"),
|
||||
@current_presets;
|
||||
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
|
||||
}
|
||||
|
||||
@ -646,150 +571,34 @@ 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);
|
||||
}
|
||||
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 {
|
||||
my ($self, $tab) = @_;
|
||||
$self->{tabpanel}->SetSelection($tab);
|
||||
@ -798,9 +607,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);
|
||||
}
|
||||
|
||||
$self->{plater}->select_view($direction);
|
||||
}
|
||||
|
||||
sub _append_menu_item {
|
||||
|
@ -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;
|
||||
|
@ -6,16 +6,17 @@ use warnings;
|
||||
use utf8;
|
||||
|
||||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first max);
|
||||
use List::Util qw(sum first max none any);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg);
|
||||
use LWP::UserAgent;
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(presets));
|
||||
|
||||
use constant TB_ADD => &Wx::NewId;
|
||||
use constant TB_REMOVE => &Wx::NewId;
|
||||
@ -42,15 +43,13 @@ our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType;
|
||||
use constant FILAMENT_CHOOSERS_SPACING => 0;
|
||||
use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
|
||||
|
||||
my $PreventListEvents = 0;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
$self->{config} = Slic3r::Config->new_from_defaults(qw(
|
||||
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width
|
||||
serial_port serial_speed octoprint_host octoprint_apikey
|
||||
serial_port serial_speed octoprint_host octoprint_apikey overridable
|
||||
));
|
||||
$self->{model} = Slic3r::Model->new;
|
||||
$self->{print} = Slic3r::Print->new;
|
||||
@ -194,23 +193,6 @@ sub new {
|
||||
}
|
||||
}
|
||||
|
||||
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
||||
wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
|
||||
$self->{list}->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 145);
|
||||
$self->{list}->InsertColumn(1, "Copies", wxLIST_FORMAT_CENTER, 45);
|
||||
$self->{list}->InsertColumn(2, "Scale", wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER);
|
||||
EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected);
|
||||
EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected);
|
||||
EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated);
|
||||
EVT_KEY_DOWN($self->{list}, sub {
|
||||
my ($list, $event) = @_;
|
||||
if ($event->GetKeyCode == WXK_TAB) {
|
||||
$list->Navigate($event->ShiftDown ? &Wx::wxNavigateBackward : &Wx::wxNavigateForward);
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
});
|
||||
|
||||
# right pane buttons
|
||||
$self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||
$self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||
@ -314,7 +296,7 @@ sub new {
|
||||
|
||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||
for grep defined($_),
|
||||
$self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||
$self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D};
|
||||
|
||||
EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
@ -365,32 +347,52 @@ sub new {
|
||||
$self->update;
|
||||
|
||||
{
|
||||
my $presets;
|
||||
if ($self->GetFrame->{mode} eq 'expert') {
|
||||
$presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 2, 1, 2);
|
||||
$presets->AddGrowableCol(1, 1);
|
||||
$presets->SetFlexibleDirection(wxHORIZONTAL);
|
||||
my %group_labels = (
|
||||
print => 'Print settings',
|
||||
filament => 'Filament',
|
||||
printer => 'Printer',
|
||||
);
|
||||
$self->{preset_choosers} = {};
|
||||
for my $group (qw(print filament printer)) {
|
||||
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
||||
$self->{preset_choosers}{$group} = [$choice];
|
||||
# setup the listener
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset($group, $choice);
|
||||
});
|
||||
my $presets = $self->{presets_sizer} = Wx::FlexGridSizer->new(3, 3, 1, 2);
|
||||
$presets->AddGrowableCol(1, 1);
|
||||
$presets->SetFlexibleDirection(wxHORIZONTAL);
|
||||
my %group_labels = (
|
||||
print => 'Print settings',
|
||||
filament => 'Filament',
|
||||
printer => 'Printer',
|
||||
);
|
||||
$self->{preset_choosers} = {};
|
||||
for my $group (qw(print filament printer)) {
|
||||
# label
|
||||
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# dropdown control
|
||||
my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
||||
$self->{preset_choosers}{$group} = [$choice];
|
||||
# setup the listener
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset($group);
|
||||
});
|
||||
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
$presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 0);
|
||||
}
|
||||
});
|
||||
|
||||
# settings button
|
||||
my $settings_btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
EVT_BUTTON($self, $settings_btn, sub {
|
||||
$self->show_preset_editor($group, 0);
|
||||
});
|
||||
|
||||
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
$presets->Add($choice, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 0);
|
||||
$presets->Add($settings_btn, 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxLEFT, 3);
|
||||
}
|
||||
|
||||
{
|
||||
$self->{settings_override_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self,
|
||||
on_change => sub {
|
||||
$self->config_changed;
|
||||
});
|
||||
$self->{settings_override_panel}->set_editable(0);
|
||||
$self->{settings_override_config} = Slic3r::Config->new;
|
||||
$self->{settings_override_panel}->set_default_config($self->{settings_override_config});
|
||||
$self->{settings_override_panel}->set_config($self->{settings_override_config});
|
||||
}
|
||||
|
||||
my $object_info_sizer;
|
||||
@ -405,6 +407,8 @@ sub new {
|
||||
$object_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
||||
|
||||
my @info = (
|
||||
name => "Name",
|
||||
copies => "Copies",
|
||||
size => "Size",
|
||||
volume => "Volume",
|
||||
facets => "Facets",
|
||||
@ -473,7 +477,7 @@ sub new {
|
||||
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
|
||||
$right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5);
|
||||
$right_sizer->Add($self->{list}, 1, wxEXPAND, 5);
|
||||
$right_sizer->Add($self->{settings_override_panel}, 1, wxEXPAND, 5);
|
||||
$right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
|
||||
$right_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
|
||||
$right_sizer->Hide($print_info_sizer);
|
||||
@ -492,35 +496,83 @@ sub new {
|
||||
$self->SetSizer($sizer);
|
||||
}
|
||||
|
||||
$self->load_presets;
|
||||
$self->_on_select_preset($_) for qw(printer filament print);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# sets the callback
|
||||
sub on_select_preset {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_select_preset} = $cb;
|
||||
sub _on_select_preset {
|
||||
my ($self, $group) = @_;
|
||||
|
||||
my @presets = map $self->presets->{$group}[scalar $_->GetSelection],
|
||||
@{$self->{preset_choosers}{$group}};
|
||||
|
||||
$Slic3r::GUI::Settings->{presets}{$group} = $presets[0]->name;
|
||||
$Slic3r::GUI::Settings->{presets}{"${group}_${_}"} = $presets[$_]->name
|
||||
for 1..$#presets;
|
||||
|
||||
wxTheApp->save_settings;
|
||||
|
||||
my $config = $self->config;
|
||||
|
||||
$self->on_extruders_change(scalar @{$config->get('nozzle_diameter')});
|
||||
|
||||
foreach my $opt_key (@{$self->{config}->diff($config)}) {
|
||||
# Ignore overrides. No need to set them in our config; we'll use them directly below.
|
||||
next if $opt_key eq 'overrides';
|
||||
|
||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
||||
|
||||
if ($opt_key eq 'bed_shape') {
|
||||
$self->{canvas}->update_bed_size;
|
||||
$self->{canvas3D}->update_bed_size if $self->{canvas3D};
|
||||
$self->{preview3D}->set_bed_shape($self->{config}->bed_shape)
|
||||
if $self->{preview3D};
|
||||
$self->update;
|
||||
} elsif ($opt_key eq 'serial_port') {
|
||||
if ($config->get('serial_port')) {
|
||||
$self->{btn_print}->Show;
|
||||
} else {
|
||||
$self->{btn_print}->Hide;
|
||||
}
|
||||
$self->Layout;
|
||||
} elsif ($opt_key eq 'octoprint_host') {
|
||||
if ($config->get('octoprint_host')) {
|
||||
$self->{btn_send_gcode}->Show;
|
||||
} else {
|
||||
$self->{btn_send_gcode}->Hide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($group eq 'print') {
|
||||
$self->{settings_override_config}->clear;
|
||||
my $overridable = $config->get('overridable');
|
||||
if ($overridable) {
|
||||
$self->{settings_override_config}->set($_, $config->get($_))
|
||||
for @$overridable;
|
||||
}
|
||||
$self->{settings_override_panel}->update_optgroup;
|
||||
} elsif ($group eq 'printer') {
|
||||
# reload print and filament settings to honor their compatible_printer options
|
||||
$self->load_presets;
|
||||
}
|
||||
|
||||
return if !$self->GetFrame->is_loaded;
|
||||
|
||||
$self->config_changed;
|
||||
}
|
||||
|
||||
sub _on_select_preset {
|
||||
my $self = shift;
|
||||
my ($group, $choice) = @_;
|
||||
|
||||
# if user changed filament preset, don't propagate this to the tabs
|
||||
if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
|
||||
my @filament_presets = $self->filament_presets;
|
||||
$Slic3r::GUI::Settings->{presets}{filament} = $choice->GetString($filament_presets[0]) . ".ini";
|
||||
$Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_])
|
||||
for 1 .. $#filament_presets;
|
||||
wxTheApp->save_settings;
|
||||
return;
|
||||
}
|
||||
|
||||
# call GetSelection() in scalar context as it's context-aware
|
||||
$self->{on_select_preset}->($group, scalar $choice->GetSelection)
|
||||
if $self->{on_select_preset};
|
||||
|
||||
# get new config and generate on_config_change() event for updating plater and other things
|
||||
$self->on_config_change($self->GetFrame->config);
|
||||
sub load_config {
|
||||
my ($self, $config) = @_;
|
||||
|
||||
# This method is called with the CLI options.
|
||||
# We add them to the visible overrides.
|
||||
$self->{settings_override_config}->apply($config);
|
||||
$self->{settings_override_panel}->update_optgroup;
|
||||
|
||||
$self->config_changed;
|
||||
}
|
||||
|
||||
sub GetFrame {
|
||||
@ -528,60 +580,188 @@ sub GetFrame {
|
||||
return &Wx::GetTopLevelParent($self);
|
||||
}
|
||||
|
||||
sub update_presets {
|
||||
my $self = shift;
|
||||
my ($group, $presets, $selected, $is_dirty) = @_;
|
||||
sub load_presets {
|
||||
my ($self) = @_;
|
||||
|
||||
my @choosers = @{ $self->{preset_choosers}{$group} };
|
||||
foreach my $choice (@choosers) {
|
||||
if ($group eq 'filament' && @choosers > 1) {
|
||||
# if we have more than one filament chooser, keep our selection
|
||||
# instead of importing the one from the tab
|
||||
$selected = $choice->GetSelection;
|
||||
$is_dirty = 0;
|
||||
}
|
||||
$choice->Clear;
|
||||
foreach my $preset (@$presets) {
|
||||
my $bitmap;
|
||||
if ($group eq 'filament') {
|
||||
my $config = $preset->config(['filament_colour']);
|
||||
my $rgb_hex = $config->filament_colour->[0];
|
||||
if ($preset->default) {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG);
|
||||
} else {
|
||||
$rgb_hex =~ s/^#//;
|
||||
my @rgb = unpack 'C*', pack 'H*', $rgb_hex;
|
||||
my $image = Wx::Image->new(16,16);
|
||||
$image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb);
|
||||
$bitmap = Wx::Bitmap->new($image);
|
||||
$self->presets({});
|
||||
|
||||
my $selected_printer_name;
|
||||
foreach my $group (qw(printer filament print)) {
|
||||
my @presets = wxTheApp->presets($group);
|
||||
if ($group eq 'filament' || $group eq 'print') {
|
||||
my %printer_names = map { $_->name => 1 } @{ $self->presets->{printer} };
|
||||
# Skip presets not compatible with the selected printer, if they
|
||||
# have other compatible printers configured (and at least one of them exists).
|
||||
for (my $i = 0; $i <= $#presets; ++$i) {
|
||||
my $config = $presets[$i]->config;
|
||||
next if !$config->has('compatible_printers');
|
||||
my @compat = @{$config->compatible_printers};
|
||||
if (@compat
|
||||
&& (none { $_ eq $selected_printer_name } @compat)
|
||||
&& (any { $printer_names{$_} } @compat)) {
|
||||
splice @presets, $i, 1;
|
||||
--$i;
|
||||
}
|
||||
} elsif ($group eq 'print') {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG);
|
||||
} elsif ($group eq 'printer') {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
$choice->AppendString($preset->name, $bitmap);
|
||||
}
|
||||
if (!@presets) {
|
||||
unshift @presets, Slic3r::GUI::Preset->new(
|
||||
default => 1,
|
||||
name => '- default -',
|
||||
);
|
||||
}
|
||||
|
||||
if ($selected <= $#$presets) {
|
||||
my $preset_name = $choice->GetString($selected);
|
||||
if ($is_dirty) {
|
||||
$choice->SetString($selected, "$preset_name (modified)");
|
||||
$self->presets->{$group} = [@presets];
|
||||
|
||||
# get the wxChoice objects for this group
|
||||
my @choosers = @{ $self->{preset_choosers}{$group} };
|
||||
|
||||
# find the currently selected one(s) according to the saved file
|
||||
my @sel = ();
|
||||
if (my $current = $Slic3r::GUI::Settings->{presets}{$group}) {
|
||||
push @sel, grep defined, first { $presets[$_]->name eq $current } 0..$#presets;
|
||||
}
|
||||
for my $i (1..(@choosers-1)) {
|
||||
if (my $current = $Slic3r::GUI::Settings->{presets}{"${group}_$i"}) {
|
||||
push @sel, grep defined, first { $presets[$_]->name eq $current } 0..$#presets;
|
||||
}
|
||||
}
|
||||
@sel = (0) if !@sel;
|
||||
|
||||
# populate the wxChoice objects
|
||||
foreach my $choice (@choosers) {
|
||||
$choice->Clear;
|
||||
foreach my $preset (@presets) {
|
||||
# load/generate the proper icon
|
||||
my $bitmap;
|
||||
if ($group eq 'filament') {
|
||||
my $config = $preset->config;
|
||||
if ($preset->default || !$config->has('filament_colour')) {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG);
|
||||
} else {
|
||||
my $rgb_hex = $config->filament_colour->[0];
|
||||
|
||||
$rgb_hex =~ s/^#//;
|
||||
my @rgb = unpack 'C*', pack 'H*', $rgb_hex;
|
||||
my $image = Wx::Image->new(16,16);
|
||||
$image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb);
|
||||
$bitmap = Wx::Bitmap->new($image);
|
||||
}
|
||||
} elsif ($group eq 'print') {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG);
|
||||
} elsif ($group eq 'printer') {
|
||||
$bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
$choice->AppendString($preset->name, $bitmap);
|
||||
}
|
||||
|
||||
my $selected = shift @sel;
|
||||
if ($selected <= $#presets) {
|
||||
# call SetSelection() only after SetString() otherwise the new string
|
||||
# won't be picked up as the visible string
|
||||
$choice->SetSelection($selected);
|
||||
|
||||
my $preset_name = $choice->GetString($selected);
|
||||
$self->{print}->placeholder_parser->set("${group}_preset", $preset_name);
|
||||
$selected_printer_name = $preset_name if $group eq 'printer';
|
||||
}
|
||||
# call SetSelection() only after SetString() otherwise the new string
|
||||
# won't be picked up as the visible string
|
||||
$choice->SetSelection($selected);
|
||||
|
||||
$self->{print}->placeholder_parser->set("${group}_preset", $preset_name);
|
||||
}
|
||||
}
|
||||
|
||||
#$self->_on_select_preset($_) for qw(printer filament print);
|
||||
}
|
||||
|
||||
sub filament_presets {
|
||||
my $self = shift;
|
||||
sub select_preset_by_name {
|
||||
my ($self, $name, $group, $n) = @_;
|
||||
|
||||
# force scalar context for GetSelection() as it's context-aware
|
||||
return map scalar($_->GetSelection), @{ $self->{preset_choosers}{filament} };
|
||||
# $n is optional
|
||||
|
||||
my $presets = $self->presets->{$group};
|
||||
my $i = first { $presets->[$_]->name eq $name } 0..$#$presets;
|
||||
return if !defined $i;
|
||||
|
||||
my $choosers = $self->{preset_choosers}{$group};
|
||||
if (defined $n && $n <= $#$choosers) {
|
||||
$choosers->[$n]->SetSelection($i);
|
||||
} else {
|
||||
$_->SetSelection($i) for @$choosers;
|
||||
}
|
||||
$self->_on_select_preset($group);
|
||||
}
|
||||
|
||||
sub selected_presets {
|
||||
my ($self, $group) = @_;
|
||||
|
||||
my %presets;
|
||||
foreach my $group (qw(printer filament print)) {
|
||||
my @i = map scalar($_->GetSelection), @{ $self->{preset_choosers}{$group} };
|
||||
$presets{$group} = [ @{$self->presets->{$group}}[@i] ];
|
||||
}
|
||||
return $group ? @{$presets{$group}} : %presets;
|
||||
}
|
||||
|
||||
sub show_preset_editor {
|
||||
my ($self, $group, $i) = @_;
|
||||
|
||||
my $class = "Slic3r::GUI::PresetEditorDialog::" . ucfirst($group);
|
||||
my $dlg = $class->new($self);
|
||||
|
||||
my @presets = $self->selected_presets($group);
|
||||
$dlg->preset_editor->select_preset_by_name($presets[$i // 0]->name);
|
||||
$dlg->ShowModal;
|
||||
|
||||
# Re-load the presets as they might have changed.
|
||||
$self->load_presets;
|
||||
|
||||
# Select the preset that was last selected in the editor.
|
||||
$self->select_preset_by_name
|
||||
($dlg->preset_editor->get_current_preset->name, $group, $i, 1);
|
||||
}
|
||||
|
||||
# Returns the current config by merging the selected presets and the overrides.
|
||||
sub config {
|
||||
my ($self) = @_;
|
||||
|
||||
# use a DynamicConfig because FullPrintConfig is not enough
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
|
||||
# get defaults also for the values tracked by the Plater's config
|
||||
# (for example 'overridable')
|
||||
$config->apply(Slic3r::Config->new_from_defaults(@{$self->{config}->get_keys}));
|
||||
|
||||
my %classes = map { $_ => "Slic3r::GUI::PresetEditor::".ucfirst($_) }
|
||||
qw(print filament printer);
|
||||
|
||||
my %presets = $self->selected_presets;
|
||||
$config->apply($_->config([ $classes{printer}->options ]))
|
||||
for @{ $presets{printer} };
|
||||
if (@{ $presets{filament} }) {
|
||||
my @opt_keys = ($classes{filament}->options, $classes{filament}->overriding_options);
|
||||
my $filament_config = shift(@{ $presets{filament} })
|
||||
->config(\@opt_keys);
|
||||
|
||||
my $i = 1;
|
||||
for my $preset (@{ $presets{filament} }) {
|
||||
my $config = $preset->config(\@opt_keys);
|
||||
foreach my $opt_key (@{$config->get_keys}) {
|
||||
if ($filament_config->has($opt_key)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
|
||||
$config->apply($filament_config);
|
||||
}
|
||||
$config->apply($_->config([ $classes{print}->options ]))
|
||||
for @{ $presets{print} };
|
||||
|
||||
$config->apply($self->{settings_override_config});
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
sub add {
|
||||
@ -712,18 +892,7 @@ sub load_model_objects {
|
||||
);
|
||||
}
|
||||
|
||||
foreach my $obj_idx (@obj_idx) {
|
||||
my $object = $self->{objects}[$obj_idx];
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$self->{list}->InsertStringItem($obj_idx, $object->name);
|
||||
$self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL))
|
||||
if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont()
|
||||
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
$self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%");
|
||||
|
||||
$self->make_thumbnail($obj_idx);
|
||||
}
|
||||
$self->make_thumbnail($_) for @obj_idx;
|
||||
$self->arrange if $need_arrange;
|
||||
$self->update;
|
||||
|
||||
@ -731,8 +900,6 @@ sub load_model_objects {
|
||||
$self->{canvas3D}->zoom_to_volumes
|
||||
if $self->{canvas3D};
|
||||
|
||||
$self->{list}->Update;
|
||||
$self->{list}->Select($obj_idx[-1], 1);
|
||||
$self->object_list_changed;
|
||||
|
||||
$self->schedule_background_process;
|
||||
@ -766,7 +933,6 @@ sub remove {
|
||||
splice @{$self->{objects}}, $obj_idx, 1;
|
||||
$self->{model}->delete_object($obj_idx);
|
||||
$self->{print}->delete_object($obj_idx);
|
||||
$self->{list}->DeleteItem($obj_idx);
|
||||
$self->object_list_changed;
|
||||
|
||||
$self->select_object(undef);
|
||||
@ -786,7 +952,6 @@ sub reset {
|
||||
@{$self->{objects}} = ();
|
||||
$self->{model}->clear_objects;
|
||||
$self->{print}->clear_objects;
|
||||
$self->{list}->DeleteAllItems;
|
||||
$self->object_list_changed;
|
||||
|
||||
$self->select_object(undef);
|
||||
@ -808,7 +973,6 @@ sub increase {
|
||||
);
|
||||
$self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
|
||||
}
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
|
||||
# only autoarrange if user has autocentering enabled
|
||||
$self->stop_background_process;
|
||||
@ -833,15 +997,10 @@ sub decrease {
|
||||
$model_object->delete_last_instance;
|
||||
$self->{print}->objects->[$obj_idx]->delete_last_copy;
|
||||
}
|
||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||
} else {
|
||||
$self->remove;
|
||||
}
|
||||
|
||||
if ($self->{objects}[$obj_idx]) {
|
||||
$self->{list}->Select($obj_idx, 0);
|
||||
$self->{list}->Select($obj_idx, 1);
|
||||
}
|
||||
$self->update;
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
@ -1006,8 +1165,8 @@ sub changescale {
|
||||
$model_instance->scaling_factor*100, $self);
|
||||
return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0;
|
||||
}
|
||||
|
||||
$self->{list}->SetItem($obj_idx, 2, "$scale%");
|
||||
return if !$scale || $scale < 0;
|
||||
|
||||
$scale /= 100; # turn percent into factor
|
||||
|
||||
my $variation = $scale / $model_instance->scaling_factor;
|
||||
@ -1035,7 +1194,7 @@ sub arrange {
|
||||
$self->pause_background_process;
|
||||
|
||||
my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
|
||||
my $success = $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb);
|
||||
my $success = $self->{model}->arrange_objects($self->config->min_object_distance, $bb);
|
||||
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
|
||||
# when parts don't fit in print bed
|
||||
|
||||
@ -1086,6 +1245,17 @@ sub split_object {
|
||||
$self->load_model_objects(@model_objects);
|
||||
}
|
||||
|
||||
sub config_changed {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($Slic3r::GUI::autosave) {
|
||||
$self->config->save($Slic3r::GUI::autosave);
|
||||
}
|
||||
|
||||
# (re) start timer
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub schedule_background_process {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -1117,7 +1287,7 @@ sub async_apply_config {
|
||||
$self->pause_background_process;
|
||||
|
||||
# apply new config
|
||||
my $invalidated = $self->{print}->apply_config($self->GetFrame->config);
|
||||
my $invalidated = $self->{print}->apply_config($self->config);
|
||||
|
||||
return if !$Slic3r::GUI::Settings->{_}{background_processing};
|
||||
|
||||
@ -1151,7 +1321,7 @@ sub start_background_process {
|
||||
# don't start process thread if config is not valid
|
||||
eval {
|
||||
# this will throw errors if config is not valid
|
||||
$self->GetFrame->config->validate;
|
||||
$self->config->validate;
|
||||
$self->{print}->validate;
|
||||
};
|
||||
if ($@) {
|
||||
@ -1239,14 +1409,14 @@ sub export_gcode {
|
||||
# (we assume that if it is running, config is valid)
|
||||
eval {
|
||||
# this will throw errors if config is not valid
|
||||
$self->GetFrame->config->validate;
|
||||
$self->config->validate;
|
||||
$self->{print}->validate;
|
||||
};
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
|
||||
|
||||
# apply config and validate print
|
||||
my $config = $self->GetFrame->config;
|
||||
my $config = $self->config;
|
||||
eval {
|
||||
# this will throw errors if config is not valid
|
||||
$config->validate;
|
||||
@ -1410,15 +1580,15 @@ sub on_export_completed {
|
||||
sub do_print {
|
||||
my ($self) = @_;
|
||||
|
||||
my $printer_tab = $self->GetFrame->{options_tabs}{printer};
|
||||
my $printer_name = $printer_tab->get_current_preset->name;
|
||||
my $controller = $self->GetFrame->{controller} or return;
|
||||
|
||||
my $controller = $self->GetFrame->{controller};
|
||||
my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config);
|
||||
my %current_presets = $self->selected_presets;
|
||||
|
||||
my $printer_name = $current_presets{printer}->[0]->name;
|
||||
my $printer_panel = $controller->add_printer($printer_name, $self->config);
|
||||
|
||||
my $filament_stats = $self->{print}->filament_stats;
|
||||
my @filament_names = $self->GetFrame->filament_preset_names;
|
||||
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
||||
$filament_stats = { map { $current_presets{filament}[$_]->name => $filament_stats->{$_} } keys %$filament_stats };
|
||||
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
|
||||
|
||||
$self->GetFrame->select_tab(1);
|
||||
@ -1620,18 +1790,27 @@ sub on_extruders_change {
|
||||
# copy icons from first choice
|
||||
$choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
|
||||
|
||||
# insert new choice into sizer
|
||||
$self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0);
|
||||
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
|
||||
# settings button
|
||||
my $settings_btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
|
||||
# setup the listener
|
||||
# insert new row into sizer
|
||||
$self->{presets_sizer}->Insert(6 + ($#$choices-1)*3, 0, 0);
|
||||
$self->{presets_sizer}->Insert(7 + ($#$choices-1)*3, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
|
||||
$self->{presets_sizer}->Insert(8 + ($#$choices-1)*3, $settings_btn, 0, wxEXPAND | wxLEFT, 4);
|
||||
|
||||
# setup the listeners
|
||||
EVT_COMBOBOX($choice, $choice, sub {
|
||||
my ($choice) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_select_preset('filament', $choice);
|
||||
$self->_on_select_preset('filament');
|
||||
});
|
||||
});
|
||||
|
||||
EVT_BUTTON($self, $settings_btn, sub {
|
||||
$self->show_preset_editor('filament', $#$choices);
|
||||
});
|
||||
|
||||
# initialize selection
|
||||
my $i = first { $choice->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets;
|
||||
$choice->SetSelection($i || 0);
|
||||
@ -1639,79 +1818,21 @@ sub on_extruders_change {
|
||||
|
||||
# remove unused choices if any
|
||||
while (@$choices > $num_extruders) {
|
||||
$self->{presets_sizer}->Remove(4 + ($#$choices-1)*2); # label
|
||||
$self->{presets_sizer}->Remove(4 + ($#$choices-1)*2); # wxChoice
|
||||
my $i = 6 + ($#$choices-1)*3;
|
||||
|
||||
$self->{presets_sizer}->Remove($i); # label
|
||||
$self->{presets_sizer}->Remove($i); # wxChoice
|
||||
|
||||
my $settings_btn = $self->{presets_sizer}->GetItem($i)->GetWindow;
|
||||
$self->{presets_sizer}->Remove($i); # settings btn
|
||||
$settings_btn->Destroy;
|
||||
|
||||
$choices->[-1]->Destroy;
|
||||
pop @$choices;
|
||||
}
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
sub on_config_change {
|
||||
my $self = shift;
|
||||
my ($config) = @_;
|
||||
|
||||
foreach my $opt_key (@{$self->{config}->diff($config)}) {
|
||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
||||
if ($opt_key eq 'bed_shape') {
|
||||
$self->{canvas}->update_bed_size;
|
||||
$self->{canvas3D}->update_bed_size if $self->{canvas3D};
|
||||
$self->{preview3D}->set_bed_shape($self->{config}->bed_shape)
|
||||
if $self->{preview3D};
|
||||
$self->update;
|
||||
} elsif ($opt_key eq 'serial_port') {
|
||||
if ($config->get('serial_port')) {
|
||||
$self->{btn_print}->Show;
|
||||
} else {
|
||||
$self->{btn_print}->Hide;
|
||||
}
|
||||
$self->Layout;
|
||||
} elsif ($opt_key eq 'octoprint_host') {
|
||||
if ($config->get('octoprint_host')) {
|
||||
$self->{btn_send_gcode}->Show;
|
||||
} else {
|
||||
$self->{btn_send_gcode}->Hide;
|
||||
}
|
||||
$self->Layout;
|
||||
}
|
||||
}
|
||||
if ($self->{"right_sizer"}) {
|
||||
$self->{"right_sizer"}->Hide($self->{"sliced_info_box"});
|
||||
$self->{"right_sizer"}->Layout;
|
||||
}
|
||||
|
||||
return if !$self->GetFrame->is_loaded;
|
||||
|
||||
# (re)start timer
|
||||
$self->schedule_background_process;
|
||||
}
|
||||
|
||||
sub list_item_deselected {
|
||||
my ($self, $event) = @_;
|
||||
return if $PreventListEvents;
|
||||
|
||||
if ($self->{list}->GetFirstSelected == -1) {
|
||||
$self->select_object(undef);
|
||||
$self->refresh_canvases;
|
||||
}
|
||||
}
|
||||
|
||||
sub list_item_selected {
|
||||
my ($self, $event) = @_;
|
||||
return if $PreventListEvents;
|
||||
|
||||
my $obj_idx = $event->GetIndex;
|
||||
$self->select_object($obj_idx);
|
||||
$self->refresh_canvases;
|
||||
}
|
||||
|
||||
sub list_item_activated {
|
||||
my ($self, $event, $obj_idx) = @_;
|
||||
|
||||
$obj_idx //= $event->GetIndex;
|
||||
$self->object_settings_dialog($obj_idx);
|
||||
}
|
||||
|
||||
sub object_cut_dialog {
|
||||
my $self = shift;
|
||||
my ($obj_idx) = @_;
|
||||
@ -1818,9 +1939,17 @@ sub selection_changed {
|
||||
|
||||
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
||||
if ($have_sel) {
|
||||
$self->{object_info_name}->SetLabel($object->name);
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$self->{object_info_copies}->SetLabel($model_object->instances_count);
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
||||
{
|
||||
my $size_string = sprintf "%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size};
|
||||
if ($model_instance->scaling_factor != 1) {
|
||||
$size_string .= sprintf " (%s%%)", $model_instance->scaling_factor * 100;
|
||||
}
|
||||
$self->{object_info_size}->SetLabel($size_string);
|
||||
}
|
||||
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
||||
|
||||
my $raw_mesh = $model_object->raw_mesh;
|
||||
@ -1845,7 +1974,7 @@ sub selection_changed {
|
||||
$self->{object_info_facets}->SetLabel($object->facets);
|
||||
}
|
||||
} else {
|
||||
$self->{"object_info_$_"}->SetLabel("") for qw(size volume facets materials manifold);
|
||||
$self->{"object_info_$_"}->SetLabel("") for qw(name copies size volume facets materials manifold);
|
||||
$self->{object_info_manifold_warning_icon}->Hide;
|
||||
$self->{object_info_manifold}->SetToolTipString("");
|
||||
}
|
||||
@ -1862,15 +1991,6 @@ sub select_object {
|
||||
$_->selected(0) for @{ $self->{objects} };
|
||||
if (defined $obj_idx) {
|
||||
$self->{objects}->[$obj_idx]->selected(1);
|
||||
|
||||
# We use this flag to avoid circular event handling
|
||||
# Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows,
|
||||
# whose event handler calls this method again and again and again
|
||||
$PreventListEvents = 1;
|
||||
$self->{list}->Select($obj_idx, 1);
|
||||
$PreventListEvents = 0;
|
||||
} else {
|
||||
# TODO: deselect all in list
|
||||
}
|
||||
$self->selection_changed(1);
|
||||
}
|
||||
@ -1895,7 +2015,7 @@ sub validate_config {
|
||||
my $self = shift;
|
||||
|
||||
eval {
|
||||
$self->GetFrame->config->validate;
|
||||
$self->config->validate;
|
||||
};
|
||||
return 0 if Slic3r::GUI::catch_error($self);
|
||||
return 1;
|
||||
|
@ -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;
|
||||
@ -19,10 +18,11 @@ use constant ICON_MODIFIERMESH => 2;
|
||||
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);
|
||||
@ -64,17 +64,20 @@ 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) = @_;
|
||||
|
||||
@ -85,6 +88,7 @@ sub set_opt_keys {
|
||||
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
|
||||
}
|
||||
|
||||
# Sets the options that user can't remove.
|
||||
sub set_fixed_options {
|
||||
my ($self, $opt_keys) = @_;
|
||||
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
|
||||
@ -97,6 +101,8 @@ sub update_optgroup {
|
||||
$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 +110,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},
|
||||
@ -116,10 +123,11 @@ sub update_optgroup {
|
||||
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);
|
||||
@ -132,7 +140,9 @@ sub update_optgroup {
|
||||
},
|
||||
);
|
||||
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 +164,11 @@ sub disable {
|
||||
$self->Disable;
|
||||
}
|
||||
|
||||
# Shows or hides the Add button.
|
||||
sub set_editable {
|
||||
my ($self, $editable) = @_;
|
||||
|
||||
$self->{editable} = $editable;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -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',
|
||||
|
52
lib/Slic3r/GUI/Preset.pm
Normal file
52
lib/Slic3r/GUI/Preset.pm
Normal file
@ -0,0 +1,52 @@
|
||||
package Slic3r::GUI::Preset;
|
||||
use Moo;
|
||||
use Unicode::Normalize;
|
||||
|
||||
has 'default' => (is => 'ro', default => sub { 0 });
|
||||
has 'external' => (is => 'ro', default => sub { 0 });
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'file' => (is => 'rw');
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->name(Unicode::Normalize::NFC($self->name));
|
||||
}
|
||||
|
||||
sub config {
|
||||
my ($self, $keys, $extra_keys) = @_;
|
||||
|
||||
if ($self->default) {
|
||||
return Slic3r::Config->new_from_defaults(@$keys);
|
||||
} else {
|
||||
if (!-e Slic3r::encode_path($self->file)) {
|
||||
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||
return undef;
|
||||
}
|
||||
my $external_config = $self->load_config;
|
||||
if (!$keys) {
|
||||
return $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) {
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @$extra_keys;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub load_config {
|
||||
my ($self) = @_;
|
||||
|
||||
return Slic3r::Config->load($self->file);
|
||||
}
|
||||
|
||||
1;
|
@ -1,7 +1,4 @@
|
||||
# The "Expert" tab at the right of the main tabbed window.
|
||||
# The "Expert" is enabled by File->Preferences dialog.
|
||||
|
||||
package Slic3r::GUI::Tab;
|
||||
package Slic3r::GUI::PresetEditor;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
@ -22,7 +19,7 @@ sub new {
|
||||
|
||||
# horizontal sizer
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
#$self->{sizer}->SetSizeHints($self);
|
||||
$self->SetSizer($self->{sizer});
|
||||
|
||||
# left vertical sizer
|
||||
@ -89,7 +86,6 @@ sub new {
|
||||
|
||||
EVT_CHOICE($parent, $self->{presets_choice}, sub {
|
||||
$self->on_select_preset;
|
||||
$self->_on_presets_changed;
|
||||
});
|
||||
|
||||
EVT_BUTTON($self, $self->{btn_save_preset}, sub { $self->save_preset });
|
||||
@ -106,12 +102,15 @@ sub new {
|
||||
$self->{presets_choice}->Delete($i);
|
||||
$self->current_preset(undef);
|
||||
$self->select_preset(0);
|
||||
$self->_on_presets_changed;
|
||||
});
|
||||
|
||||
# C++ instance DynamicPrintConfig
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
# Initialize the DynamicPrintConfig by default keys/values.
|
||||
$self->{config}->apply(Slic3r::Config->new_from_defaults($self->options));
|
||||
|
||||
|
||||
|
||||
# Possible %params keys: no_controller
|
||||
$self->build(%params);
|
||||
$self->update_tree;
|
||||
@ -128,6 +127,12 @@ sub get_current_preset {
|
||||
return $self->get_preset($self->current_preset);
|
||||
}
|
||||
|
||||
sub get_current_preset_config {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->get_preset_config($self->get_current_preset);
|
||||
}
|
||||
|
||||
sub get_preset {
|
||||
my ($self, $i) = @_;
|
||||
return $self->{presets}[$i];
|
||||
@ -151,14 +156,15 @@ sub save_preset {
|
||||
default => $default_name,
|
||||
values => [ map $_->name, grep !$_->default && !$_->external, @{$self->{presets}} ],
|
||||
);
|
||||
return unless $dlg->ShowModal == wxID_OK;
|
||||
return 0 unless $dlg->ShowModal == wxID_OK;
|
||||
$name = $dlg->get_name;
|
||||
}
|
||||
|
||||
$self->config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $self->name, $name);
|
||||
$self->load_presets;
|
||||
$self->select_preset_by_name($name);
|
||||
$self->_on_presets_changed;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub on_value_change {
|
||||
@ -166,11 +172,6 @@ sub on_value_change {
|
||||
$self->{on_value_change} = $cb;
|
||||
}
|
||||
|
||||
sub on_presets_changed {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_presets_changed} = $cb;
|
||||
}
|
||||
|
||||
# This method is supposed to be called whenever new values are loaded
|
||||
# or changed by user (so also when a preset is loaded).
|
||||
# propagate event to the parent
|
||||
@ -183,40 +184,27 @@ sub _on_value_change {
|
||||
|
||||
sub _update {}
|
||||
|
||||
sub _on_presets_changed {
|
||||
my $self = shift;
|
||||
|
||||
$self->{on_presets_changed}->(
|
||||
$self->{presets},
|
||||
scalar($self->{presets_choice}->GetSelection),
|
||||
$self->is_dirty,
|
||||
) if $self->{on_presets_changed};
|
||||
}
|
||||
|
||||
sub on_preset_loaded {}
|
||||
sub hidden_options {}
|
||||
sub config { $_[0]->{config}->clone }
|
||||
|
||||
sub select_default_preset {
|
||||
my $self = shift;
|
||||
$self->select_preset(0);
|
||||
}
|
||||
|
||||
sub select_preset {
|
||||
my $self = shift;
|
||||
$self->{presets_choice}->SetSelection($_[0]);
|
||||
$self->on_select_preset;
|
||||
my ($self, $i, $force) = @_;
|
||||
|
||||
$self->{presets_choice}->SetSelection($i);
|
||||
$self->on_select_preset($force);
|
||||
}
|
||||
|
||||
sub select_preset_by_name {
|
||||
my ($self, $name) = @_;
|
||||
my ($self, $name, $force) = @_;
|
||||
|
||||
$name = Unicode::Normalize::NFC($name);
|
||||
$self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}});
|
||||
my $i = first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}};
|
||||
return if !defined $i;
|
||||
$self->select_preset($i, $force);
|
||||
}
|
||||
|
||||
sub on_select_preset {
|
||||
my $self = shift;
|
||||
sub prompt_unsaved_changes {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->is_dirty) {
|
||||
my $old_preset = $self->get_current_preset;
|
||||
@ -233,27 +221,39 @@ sub on_select_preset {
|
||||
}
|
||||
|
||||
my $changes = join "\n", map "- $_", @option_names;
|
||||
my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?",
|
||||
'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
if ($confirm->ShowModal == wxID_NO) {
|
||||
$self->{presets_choice}->SetSelection($self->current_preset);
|
||||
|
||||
# trigger the on_presets_changed event so that we also restore the previous value
|
||||
# in the plater selector
|
||||
$self->_on_presets_changed;
|
||||
return;
|
||||
my $confirm = Wx::MessageDialog->new($self, "$name has unsaved changes:\n$changes\n\nDo you want to save them?",
|
||||
'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_preset($old_preset->default ? undef : $old_preset->name);
|
||||
}
|
||||
}
|
||||
|
||||
$self->current_preset($self->{presets_choice}->GetSelection);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub on_select_preset {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
my $i = $self->{presets_choice}->GetSelection;
|
||||
return if defined $self->current_preset && $i == $self->current_preset;
|
||||
|
||||
if (!$force && defined $self->current_preset && !$self->prompt_unsaved_changes) {
|
||||
$self->{presets_choice}->SetSelection($self->current_preset);
|
||||
return;
|
||||
}
|
||||
|
||||
$self->current_preset($i);
|
||||
my $preset = $self->get_current_preset;
|
||||
my $preset_config = $self->get_preset_config($preset);
|
||||
eval {
|
||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||
foreach my $opt_key (@{$self->{config}->get_keys}) {
|
||||
$self->{config}->set($opt_key, $preset_config->get($opt_key))
|
||||
if $preset_config->has($opt_key);
|
||||
}
|
||||
$self->{config}->clear;
|
||||
$self->{config}->apply($preset_config);
|
||||
($preset->default || $preset->external)
|
||||
? $self->{btn_delete_preset}->Disable
|
||||
: $self->{btn_delete_preset}->Enable;
|
||||
@ -261,12 +261,10 @@ sub on_select_preset {
|
||||
$self->_update;
|
||||
$self->on_preset_loaded;
|
||||
$self->reload_config;
|
||||
$Slic3r::GUI::Settings->{presets}{$self->name} = $preset->file ? basename($preset->file) : '';
|
||||
};
|
||||
if ($@) {
|
||||
$@ = "I was unable to load the selected config file: $@";
|
||||
Slic3r::GUI::catch_error($self);
|
||||
$self->select_default_preset;
|
||||
}
|
||||
|
||||
# use CallAfter because some field triggers schedule on_change calls using CallAfter,
|
||||
@ -274,16 +272,8 @@ sub on_select_preset {
|
||||
# preset dirty again
|
||||
# (not sure this is true anymore now that update_dirty is idempotent)
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_on_presets_changed;
|
||||
$self->update_dirty;
|
||||
});
|
||||
|
||||
wxTheApp->save_settings;
|
||||
}
|
||||
|
||||
sub init_config_options {
|
||||
my ($self, @opt_keys) = @_;
|
||||
$self->{config}->apply(Slic3r::Config->new_from_defaults(@opt_keys));
|
||||
}
|
||||
|
||||
sub add_options_page {
|
||||
@ -296,7 +286,7 @@ sub add_options_page {
|
||||
$self->{iconcount}++;
|
||||
}
|
||||
|
||||
my $page = Slic3r::GUI::Tab::Page->new($self, $title, $self->{iconcount});
|
||||
my $page = Slic3r::GUI::PresetEditor::Page->new($self, $title, $self->{iconcount});
|
||||
$page->Hide;
|
||||
$self->{sizer}->Add($page, 1, wxEXPAND | wxLEFT, 5);
|
||||
push @{$self->{pages}}, $page;
|
||||
@ -345,7 +335,6 @@ sub update_dirty {
|
||||
}
|
||||
}
|
||||
$self->{presets_choice}->SetSelection($self->current_preset); # http://trac.wxwidgets.org/ticket/13769
|
||||
$self->_on_presets_changed;
|
||||
}
|
||||
|
||||
sub is_dirty {
|
||||
@ -357,36 +346,23 @@ sub dirty_options {
|
||||
my $self = shift;
|
||||
|
||||
return [] if !defined $self->current_preset; # happens during initialization
|
||||
return $self->get_preset_config($self->get_current_preset)->diff($self->{config});
|
||||
|
||||
my $preset_config = $self->get_current_preset_config;
|
||||
return [
|
||||
(grep !$preset_config->has($_), @{$self->{config}->get_keys}),
|
||||
(grep !$self->{config}->has($_), @{$preset_config->get_keys}),
|
||||
@{ $self->{config}->diff($self->get_current_preset_config) },
|
||||
];
|
||||
}
|
||||
|
||||
sub load_presets {
|
||||
my $self = shift;
|
||||
|
||||
$self->{presets} = [
|
||||
Slic3r::GUI::Tab::Preset->new(
|
||||
default => 1,
|
||||
name => '- default -',
|
||||
),
|
||||
];
|
||||
|
||||
my %presets = wxTheApp->presets($self->name);
|
||||
foreach my $preset_name (sort keys %presets) {
|
||||
push @{$self->{presets}}, Slic3r::GUI::Tab::Preset->new(
|
||||
name => $preset_name,
|
||||
file => $presets{$preset_name},
|
||||
);
|
||||
}
|
||||
|
||||
$self->{presets} = [ wxTheApp->presets($self->name, 1) ];
|
||||
$self->current_preset(undef);
|
||||
$self->{presets_choice}->Clear;
|
||||
$self->{presets_choice}->Append($_->name) for @{$self->{presets}};
|
||||
{
|
||||
# load last used preset
|
||||
my $i = first { basename($self->{presets}[$_]->file) eq ($Slic3r::GUI::Settings->{presets}{$self->name} || '') } 1 .. $#{$self->{presets}};
|
||||
$self->select_preset($i || 0);
|
||||
}
|
||||
$self->_on_presets_changed;
|
||||
$self->select_preset(0);
|
||||
}
|
||||
|
||||
sub load_config_file {
|
||||
@ -397,7 +373,7 @@ sub load_config_file {
|
||||
my $i = first { $self->{presets}[$_]{file} eq $file && $self->{presets}[$_]{external} } 1..$#{$self->{presets}};
|
||||
if (!$i) {
|
||||
my $preset_name = basename($file); # keep the .ini suffix
|
||||
push @{$self->{presets}}, Slic3r::GUI::Tab::Preset->new(
|
||||
push @{$self->{presets}}, Slic3r::GUI::Preset->new(
|
||||
file => $file,
|
||||
name => $preset_name,
|
||||
external => 1,
|
||||
@ -407,7 +383,6 @@ sub load_config_file {
|
||||
}
|
||||
$self->{presets_choice}->SetSelection($i);
|
||||
$self->on_select_preset;
|
||||
$self->_on_presets_changed;
|
||||
}
|
||||
|
||||
sub load_config {
|
||||
@ -424,7 +399,11 @@ sub load_config {
|
||||
|
||||
sub get_preset_config {
|
||||
my ($self, $preset) = @_;
|
||||
return $preset->config($self->{config}->get_keys);
|
||||
|
||||
return $preset->config(
|
||||
[ $self->options ],
|
||||
[ $self->overriding_options ],
|
||||
);
|
||||
}
|
||||
|
||||
sub get_field {
|
||||
@ -448,19 +427,64 @@ sub set_value {
|
||||
return $changed;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Tab::Print;
|
||||
use base 'Slic3r::GUI::Tab';
|
||||
sub _compatible_printers_widget {
|
||||
my ($self) = @_;
|
||||
|
||||
return sub {
|
||||
my ($parent) = @_;
|
||||
|
||||
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize,
|
||||
wxBU_LEFT | wxBU_EXACTFIT);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
if ($Slic3r::GUI::have_button_icons) {
|
||||
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($btn);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
my @presets = map $_->name, grep !$_->default && !$_->external,
|
||||
wxTheApp->presets('printer');
|
||||
|
||||
my $dlg = Wx::MultiChoiceDialog->new($self,
|
||||
"Select the printers this profile is compatible with.\nIf none are selected, it will be considered compatible with all of them.",
|
||||
"Compatible printers", \@presets);
|
||||
|
||||
my @selections = ();
|
||||
foreach my $preset_name (@{ $self->{config}->get('compatible_printers') }) {
|
||||
push @selections, first { $presets[$_] eq $preset_name } 0..$#presets;
|
||||
}
|
||||
$dlg->SetSelections(@selections);
|
||||
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = [ @presets[$dlg->GetSelections] ];
|
||||
$self->{config}->set('compatible_printers', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('compatible_printers', $value);
|
||||
}
|
||||
});
|
||||
|
||||
return $sizer;
|
||||
};
|
||||
}
|
||||
|
||||
sub options { die "Unimplemented options()"; }
|
||||
sub overridable_options { () }
|
||||
sub overriding_options { () }
|
||||
|
||||
package Slic3r::GUI::PresetEditor::Print;
|
||||
use base 'Slic3r::GUI::PresetEditor';
|
||||
|
||||
use List::Util qw(first any);
|
||||
use Wx qw(:icon :dialog :id);
|
||||
use Wx qw(:icon :dialog :id :misc :button :sizer);
|
||||
use Wx::Event qw(EVT_BUTTON);
|
||||
|
||||
sub name { 'print' }
|
||||
sub title { 'Print Settings' }
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
|
||||
$self->init_config_options(qw(
|
||||
sub options {
|
||||
return qw(
|
||||
layer_height first_layer_height
|
||||
perimeters spiral_vase
|
||||
top_solid_layers bottom_solid_layers
|
||||
@ -497,9 +521,54 @@ sub build {
|
||||
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
|
||||
top_infill_extrusion_width support_material_extrusion_width
|
||||
infill_overlap bridge_flow_ratio
|
||||
xy_size_compensation threads resolution
|
||||
));
|
||||
$self->{config}->set('print_settings_id', '');
|
||||
xy_size_compensation threads resolution overridable compatible_printers
|
||||
print_settings_id
|
||||
)
|
||||
}
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
|
||||
my $overridable_widget = sub {
|
||||
my ($parent) = @_;
|
||||
|
||||
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize,
|
||||
wxBU_LEFT | wxBU_EXACTFIT);
|
||||
$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 %options = (
|
||||
map { $_ => sprintf('%s > %s', $Slic3r::Config::Options->{$_}{category}, $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label}) }
|
||||
map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new
|
||||
);
|
||||
my @opt_keys = sort { $options{$a} cmp $options{$b} } keys %options;
|
||||
|
||||
my $dlg = Wx::MultiChoiceDialog->new($self, "Selected options will be displayed in the plater screen for quick changes.",
|
||||
"Overridable options",
|
||||
[ map $options{$_}, @opt_keys ]);
|
||||
|
||||
my @selections = ();
|
||||
foreach my $opt_key (@{ $self->{config}->get('overridable') }) {
|
||||
push @selections, first { $opt_keys[$_] eq $opt_key } 0..$#opt_keys;
|
||||
}
|
||||
$dlg->SetSelections(@selections);
|
||||
|
||||
if ($dlg->ShowModal == wxID_OK) {
|
||||
my $value = [ @opt_keys[$dlg->GetSelections] ];
|
||||
$self->{config}->set('overridable', $value);
|
||||
$self->update_dirty;
|
||||
$self->_on_value_change('overridable', $value);
|
||||
}
|
||||
});
|
||||
|
||||
return $sizer;
|
||||
};
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Layers and perimeters', 'layers.png');
|
||||
@ -643,7 +712,7 @@ sub build {
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Multiple Extruders', 'funnel.png');
|
||||
my $page = $self->add_options_page('Multiple extruders', 'funnel.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Extruders');
|
||||
$optgroup->append_single_option_line('perimeter_extruder');
|
||||
@ -728,7 +797,7 @@ sub build {
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Notes', 'note.png');
|
||||
my $page = $self->add_options_page('Notes and preferences', 'note.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Notes',
|
||||
label_width => 0,
|
||||
@ -738,6 +807,23 @@ sub build {
|
||||
$option->height(250);
|
||||
$optgroup->append_single_option_line($option);
|
||||
}
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Profile preferences');
|
||||
{
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => 'Overridable settings',
|
||||
widget => $overridable_widget,
|
||||
);
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
{
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => 'Compatible printers',
|
||||
widget => $self->_compatible_printers_widget,
|
||||
);
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -890,24 +976,34 @@ sub _update {
|
||||
|
||||
sub hidden_options { !$Slic3r::have_threads ? qw(threads) : () }
|
||||
|
||||
package Slic3r::GUI::Tab::Filament;
|
||||
use base 'Slic3r::GUI::Tab';
|
||||
package Slic3r::GUI::PresetEditor::Filament;
|
||||
use base 'Slic3r::GUI::PresetEditor';
|
||||
|
||||
use Wx qw(wxTheApp);
|
||||
|
||||
sub name { 'filament' }
|
||||
sub title { 'Filament Settings' }
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
|
||||
$self->init_config_options(qw(
|
||||
sub options {
|
||||
return qw(
|
||||
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost
|
||||
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
|
||||
fan_always_on cooling
|
||||
fan_always_on cooling compatible_printers
|
||||
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
|
||||
fan_below_layer_time slowdown_below_layer_time min_print_speed
|
||||
start_filament_gcode end_filament_gcode
|
||||
));
|
||||
$self->{config}->set('filament_settings_id', '');
|
||||
filament_settings_id
|
||||
);
|
||||
}
|
||||
|
||||
sub overriding_options {
|
||||
return (
|
||||
Slic3r::GUI::PresetEditor::Printer->overridable_options,
|
||||
);
|
||||
}
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Filament', 'spool.png');
|
||||
@ -987,26 +1083,6 @@ sub build {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Advanced', 'wrench.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Print speed override');
|
||||
$optgroup->append_single_option_line('filament_max_volumetric_speed', 0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Notes', 'note.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Notes',
|
||||
label_width => 0,
|
||||
);
|
||||
my $option = $optgroup->get_option('filament_notes', 0);
|
||||
$option->full_width(1);
|
||||
$option->height(250);
|
||||
$optgroup->append_single_option_line($option);
|
||||
}
|
||||
}
|
||||
{
|
||||
my $page = $self->add_options_page('Custom G-code', 'cog.png');
|
||||
{
|
||||
@ -1028,6 +1104,81 @@ sub build {
|
||||
$optgroup->append_single_option_line($option);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Overrides', 'wrench.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Profile preferences');
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => 'Compatible printers',
|
||||
widget => $self->_compatible_printers_widget,
|
||||
);
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Overrides');
|
||||
$optgroup->append_single_option_line('filament_max_volumetric_speed', 0);
|
||||
|
||||
# Populate the overrides config.
|
||||
my @overridable = $self->overriding_options;
|
||||
$self->{overrides_config} = Slic3r::Config->new;
|
||||
|
||||
# Populate the defaults with the current preset.
|
||||
$self->{overrides_default_config} = Slic3r::Config->new;
|
||||
$self->{overrides_default_config}->apply_only
|
||||
(wxTheApp->{mainframe}->{plater}->config, \@overridable);
|
||||
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => '',
|
||||
full_width => 1,
|
||||
widget => sub {
|
||||
my ($parent) = @_;
|
||||
|
||||
$self->{overrides_panel} = my $panel = Slic3r::GUI::Plater::OverrideSettingsPanel->new($parent,
|
||||
size => [-1, 300],
|
||||
on_change => sub {
|
||||
$self->{config}->erase($_) for @overridable;
|
||||
$self->{config}->apply($self->{overrides_config});
|
||||
$self->update_dirty;
|
||||
});
|
||||
$panel->set_editable(1);
|
||||
$panel->set_default_config($self->{overrides_default_config});
|
||||
$panel->set_config($self->{overrides_config});
|
||||
$panel->set_opt_keys([@overridable]);
|
||||
|
||||
return $panel;
|
||||
},
|
||||
);
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $page = $self->add_options_page('Notes', 'note.png');
|
||||
{
|
||||
my $optgroup = $page->new_optgroup('Notes',
|
||||
label_width => 0,
|
||||
);
|
||||
my $option = $optgroup->get_option('filament_notes', 0);
|
||||
$option->full_width(1);
|
||||
$option->height(250);
|
||||
$optgroup->append_single_option_line($option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub reload_config {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{overrides_config}->clear;
|
||||
foreach my $opt_key (@{$self->{overrides_default_config}->get_keys}) {
|
||||
if ($self->{config}->has($opt_key)) {
|
||||
$self->{overrides_config}->set($opt_key, $self->{config}->get($opt_key));
|
||||
}
|
||||
}
|
||||
$self->{overrides_panel}->update_optgroup;
|
||||
|
||||
$self->SUPER::reload_config;
|
||||
}
|
||||
|
||||
sub _update {
|
||||
@ -1071,19 +1222,16 @@ sub _update_description {
|
||||
$self->{description_line}->SetText($msg);
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Tab::Printer;
|
||||
use base 'Slic3r::GUI::Tab';
|
||||
package Slic3r::GUI::PresetEditor::Printer;
|
||||
use base 'Slic3r::GUI::PresetEditor';
|
||||
use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog);
|
||||
use Wx::Event qw(EVT_BUTTON);
|
||||
|
||||
sub name { 'printer' }
|
||||
sub title { 'Printer Settings' }
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
my (%params) = @_;
|
||||
|
||||
$self->init_config_options(qw(
|
||||
sub options {
|
||||
return qw(
|
||||
bed_shape z_offset z_steps_per_mm has_heatbed
|
||||
gcode_flavor use_relative_e_distances
|
||||
serial_port serial_speed
|
||||
@ -1093,9 +1241,22 @@ sub build {
|
||||
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode
|
||||
nozzle_diameter extruder_offset
|
||||
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
|
||||
retract_length_toolchange retract_restart_extra_toolchange
|
||||
));
|
||||
$self->{config}->set('printer_settings_id', '');
|
||||
retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below
|
||||
printer_settings_id
|
||||
);
|
||||
}
|
||||
|
||||
sub overridable_options {
|
||||
return qw(
|
||||
pressure_advance
|
||||
retract_length retract_lift retract_speed retract_restart_extra
|
||||
retract_before_travel retract_layer_change wipe
|
||||
);
|
||||
}
|
||||
|
||||
sub build {
|
||||
my $self = shift;
|
||||
my (%params) = @_;
|
||||
|
||||
$self->{extruders_count} = 1;
|
||||
|
||||
@ -1484,7 +1645,7 @@ sub load_config_file {
|
||||
if @{ $self->{config}->nozzle_diameter } > 1;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Tab::Page;
|
||||
package Slic3r::GUI::PresetEditor::Page;
|
||||
use Wx qw(wxTheApp :misc :panel :sizer);
|
||||
use base 'Wx::ScrolledWindow';
|
||||
|
||||
@ -1603,33 +1764,4 @@ sub get_name {
|
||||
return $self->{chosen_name};
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Tab::Preset;
|
||||
use Moo;
|
||||
|
||||
has 'default' => (is => 'ro', default => sub { 0 });
|
||||
has 'external' => (is => 'ro', default => sub { 0 });
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'file' => (is => 'rw');
|
||||
|
||||
sub config {
|
||||
my ($self, $keys) = @_;
|
||||
|
||||
if ($self->default) {
|
||||
return Slic3r::Config->new_from_defaults(@$keys);
|
||||
} else {
|
||||
if (!-e Slic3r::encode_path($self->file)) {
|
||||
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||
return undef;
|
||||
}
|
||||
|
||||
# apply preset values on top of defaults
|
||||
my $config = Slic3r::Config->new_from_defaults(@$keys);
|
||||
my $external_config = Slic3r::Config->load($self->file);
|
||||
$config->set($_, $external_config->get($_))
|
||||
for grep $external_config->has($_), @$keys;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
65
lib/Slic3r/GUI/PresetEditorDialog.pm
Normal file
65
lib/Slic3r/GUI/PresetEditorDialog.pm
Normal file
@ -0,0 +1,65 @@
|
||||
package Slic3r::GUI::PresetEditorDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Wx qw(:dialog :id :misc :sizer :button :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_CLOSE);
|
||||
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, [800,500],
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxDIALOG_EX_METAL);
|
||||
|
||||
$self->preset_editor($self->preset_editor_class->new($self));
|
||||
$self->SetTitle($self->preset_editor->title);
|
||||
$self->preset_editor->load_presets;
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($self->preset_editor, 1, wxEXPAND);
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
#$sizer->SetSizeHints($self);
|
||||
|
||||
if (0) {
|
||||
# This does not call the EVT_CLOSE below
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxCLOSE);
|
||||
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
}
|
||||
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->CanVeto && !$self->preset_editor->prompt_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
|
||||
# propagate event
|
||||
$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;
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
32
slic3r.pl
32
slic3r.pl
@ -35,8 +35,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},
|
||||
@ -77,28 +75,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") {
|
||||
@ -106,9 +102,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} // '');
|
||||
}
|
||||
$gui = Slic3r::GUI->new;
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
@ -117,7 +111,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;
|
||||
@ -126,6 +120,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}) {
|
||||
@ -327,7 +324,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:
|
||||
|
@ -241,21 +241,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());
|
||||
}
|
||||
}
|
||||
@ -500,6 +503,11 @@ DynamicConfig::erase(const t_config_option_key &opt_key) {
|
||||
this->options.erase(opt_key);
|
||||
}
|
||||
|
||||
void
|
||||
DynamicConfig::clear() {
|
||||
this->options.clear();
|
||||
}
|
||||
|
||||
void
|
||||
DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* extra)
|
||||
{
|
||||
|
@ -665,6 +665,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;
|
||||
@ -691,6 +692,7 @@ 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();
|
||||
void read_cli(const int argc, const char **argv, t_config_option_keys* extra);
|
||||
|
||||
private:
|
||||
|
@ -131,6 +131,10 @@ PrintConfigDef::PrintConfigDef()
|
||||
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";
|
||||
@ -784,7 +788,7 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain the API Key required for authentication.";
|
||||
def->cli = "octoprint-apikey=s";
|
||||
def->default_value = new ConfigOptionString("");
|
||||
|
||||
|
||||
def = this->add("octoprint_host", coString);
|
||||
def->label = "Host or IP";
|
||||
def->tooltip = "Slic3r can upload G-code files to OctoPrint. This field should contain the hostname or IP address of the OctoPrint instance.";
|
||||
@ -816,6 +820,14 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan.";
|
||||
def->cli = "overhangs!";
|
||||
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";
|
||||
@ -876,6 +888,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("");
|
||||
@ -885,6 +898,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;
|
||||
@ -918,6 +932,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@";
|
||||
@ -929,6 +944,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!";
|
||||
{
|
||||
@ -939,6 +955,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)";
|
||||
@ -963,6 +980,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@";
|
||||
@ -975,6 +993,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@";
|
||||
@ -987,6 +1006,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@";
|
||||
@ -998,6 +1018,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@";
|
||||
@ -1020,7 +1041,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@";
|
||||
@ -1479,6 +1500,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!";
|
||||
{
|
||||
|
@ -27,14 +27,19 @@
|
||||
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();
|
||||
void normalize();
|
||||
%name{setenv} void setenv_();
|
||||
double min_object_distance();
|
||||
|
@ -256,6 +256,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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user