2016-09-14 11:22:41 +02:00
# The "Expert" tab at the right of the main tabbed window.
# The "Expert" is enabled by File->Preferences dialog.
2016-10-24 16:07:36 +02:00
# This file implements following packages:
# Slic3r::GUI::Tab;
# Slic3r::GUI::Tab::Print;
# Slic3r::GUI::Tab::Filament;
# Slic3r::GUI::Tab::Printer;
# Slic3r::GUI::Tab::Page
# - Option page: For example, the Slic3r::GUI::Tab::Print has option pages "Layers and perimeters", "Infill", "Skirt and brim" ...
# Slic3r::GUI::SavePresetWindow
# - Dialog to select a new preset name to store the configuration.
# Slic3r::GUI::Tab::Preset;
# - Single preset item: name, file is default or external.
2012-06-17 22:27:05 +02:00
package Slic3r::GUI::Tab ;
use strict ;
use warnings ;
use utf8 ;
2012-06-19 14:47:02 +02:00
use File::Basename qw(basename) ;
2012-06-18 22:27:57 +02:00
use List::Util qw(first) ;
2014-06-14 19:59:59 +02:00
use Wx qw(:bookctrl :dialog :keycode :icon :id :misc :panel :sizer :treectrl :window
2015-12-25 17:52:10 +01:00
:button wxTheApp) ;
use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_KEY_DOWN EVT_TREE_SEL_CHANGED) ;
2014-12-25 17:35:31 +01:00
use base qw(Wx::Panel Class::Accessor) ;
2016-10-24 16:07:36 +02:00
# Index of the currently active preset.
2014-12-25 17:35:31 +01:00
__PACKAGE__ -> mk_accessors ( qw(current_preset) );
2012-06-17 22:27:05 +02:00
sub new {
my $class = shift ;
2012-07-18 20:36:34 +02:00
my ( $parent , %params ) = @_ ;
2012-07-24 12:42:58 +02:00
my $self = $class -> SUPER:: new ( $parent , - 1 , wxDefaultPosition , wxDefaultSize , wxBK_LEFT | wxTAB_TRAVERSAL );
2012-06-19 17:23:10 +02:00
2012-06-18 22:27:57 +02:00
# horizontal sizer
2012-07-01 19:24:06 +02:00
$self -> { sizer } = Wx::BoxSizer -> new ( wxHORIZONTAL );
2012-06-18 16:46:43 +02:00
$self -> { sizer } -> SetSizeHints ( $self );
$self -> SetSizer ( $self -> { sizer });
2015-12-25 17:52:10 +01:00
# left vertical sizer
2015-12-18 20:00:22 +01:00
my $left_sizer = Wx::BoxSizer -> new ( wxVERTICAL );
2015-12-25 17:52:10 +01:00
$self -> { sizer } -> Add ( $left_sizer , 0 , wxEXPAND | wxLEFT | wxTOP | wxBOTTOM , 3 );
my $left_col_width = 150 ;
2016-10-24 16:07:36 +02:00
2013-03-09 16:27:18 +01:00
# preset chooser
{
2012-06-18 22:27:57 +02:00
2013-03-09 16:27:18 +01:00
# choice menu
2015-12-25 17:52:10 +01:00
$self -> { presets_choice } = Wx::Choice -> new ( $self , - 1 , wxDefaultPosition , [ $left_col_width , - 1 ], [] );
2013-03-09 16:27:18 +01:00
$self -> { presets_choice } -> SetFont ( $ Slic3r::GUI:: small_font );
2012-06-18 22:27:57 +02:00
2013-03-09 16:27:18 +01:00
# buttons
2016-04-09 19:10:57 +02:00
$self -> { btn_save_preset } = Wx::BitmapButton -> new ( $self , - 1 , Wx::Bitmap -> new ( $ Slic3r:: var -> ( "disk.png" ), wxBITMAP_TYPE_PNG ),
2015-02-09 11:04:28 +01:00
wxDefaultPosition , wxDefaultSize , wxBORDER_NONE );
2016-04-09 19:10:57 +02:00
$self -> { btn_delete_preset } = Wx::BitmapButton -> new ( $self , - 1 , Wx::Bitmap -> new ( $ Slic3r:: var -> ( "delete.png" ), wxBITMAP_TYPE_PNG ),
2015-02-09 11:04:28 +01:00
wxDefaultPosition , wxDefaultSize , wxBORDER_NONE );
2013-03-09 16:27:18 +01:00
$self -> { btn_save_preset } -> SetToolTipString ( "Save current " . lc ( $self -> title ));
$self -> { btn_delete_preset } -> SetToolTipString ( "Delete this preset" );
$self -> { btn_delete_preset } -> Disable ;
2012-06-18 22:27:57 +02:00
2013-03-09 16:27:18 +01:00
### These cause GTK warnings:
2015-12-25 17:52:10 +01:00
###my $box = Wx::StaticBox->new($self, -1, "Presets:", wxDefaultPosition, [$left_col_width, 50]);
2013-03-09 16:27:18 +01:00
###my $hsizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL);
2012-06-20 17:08:38 +02:00
2013-03-09 16:27:18 +01:00
my $hsizer = Wx::BoxSizer -> new ( wxHORIZONTAL );
2012-06-20 17:08:38 +02:00
2013-03-09 16:27:18 +01:00
$left_sizer -> Add ( $hsizer , 0 , wxEXPAND | wxBOTTOM , 5 );
$hsizer -> Add ( $self -> { presets_choice }, 1 , wxRIGHT | wxALIGN_CENTER_VERTICAL , 3 );
$hsizer -> Add ( $self -> { btn_save_preset }, 0 , wxALIGN_CENTER_VERTICAL );
$hsizer -> Add ( $self -> { btn_delete_preset }, 0 , wxALIGN_CENTER_VERTICAL );
2012-06-18 22:27:57 +02:00
}
2013-03-09 16:27:18 +01:00
# tree
2015-12-25 17:52:10 +01:00
$self -> { treectrl } = Wx::TreeCtrl -> new ( $self , - 1 , wxDefaultPosition , [ $left_col_width , - 1 ], wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS );
2013-03-09 16:27:18 +01:00
$left_sizer -> Add ( $self -> { treectrl }, 1 , wxEXPAND );
$self -> { icons } = Wx::ImageList -> new ( 16 , 16 , 1 );
$self -> { treectrl } -> AssignImageList ( $self -> { icons });
$self -> { iconcount } = - 1 ;
$self -> { treectrl } -> AddRoot ( "root" );
$self -> { pages } = [] ;
$self -> { treectrl } -> SetIndent ( 0 );
2015-06-01 23:11:27 +02:00
$self -> { disable_tree_sel_changed_event } = 0 ;
2013-03-09 16:27:18 +01:00
EVT_TREE_SEL_CHANGED ( $parent , $self -> { treectrl }, sub {
2015-06-01 23:11:27 +02:00
return if $self -> { disable_tree_sel_changed_event };
2013-03-09 16:27:18 +01:00
my $page = first { $_ -> { title } eq $self -> { treectrl } -> GetItemText ( $self -> { treectrl } -> GetSelection ) } @ { $self -> { pages }}
or return ;
$_ -> Hide for @ { $self -> { pages }};
$page -> Show ;
$self -> { sizer } -> Layout ;
$self -> Refresh ;
});
EVT_KEY_DOWN ( $self -> { treectrl }, sub {
my ( $treectrl , $event ) = @_ ;
if ( $event -> GetKeyCode == WXK_TAB ) {
$treectrl -> Navigate ( $event -> ShiftDown ? & Wx:: wxNavigateBackward : & Wx:: wxNavigateForward );
} else {
$event -> Skip ;
2012-07-24 12:42:58 +02:00
}
2013-03-09 16:27:18 +01:00
});
EVT_CHOICE ( $parent , $self -> { presets_choice }, sub {
$self -> on_select_preset ;
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
2013-03-09 16:27:18 +01:00
});
2013-11-11 21:45:58 +01:00
EVT_BUTTON ( $self , $self -> { btn_save_preset }, sub { $self -> save_preset });
2013-03-09 16:27:18 +01:00
EVT_BUTTON ( $self , $self -> { btn_delete_preset }, sub {
2014-12-25 17:35:31 +01:00
my $i = $self -> current_preset ;
2016-10-24 16:07:36 +02:00
# Don't let the user delete the '- default -' configuration.
# This shall not happen as the 'delete' button is disabled for the '- default -' entry,
# but better be safe than sorry.
return if ( $i == 0 && $self -> get_current_preset -> { default });
2013-03-09 16:27:18 +01:00
my $res = Wx::MessageDialog -> new ( $self , "Are you sure you want to delete the selected preset?" , 'Delete Preset' , wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION ) -> ShowModal ;
return unless $res == wxID_YES ;
2016-10-24 16:07:36 +02:00
# Delete the file.
my $path = Slic3r:: encode_path ( $self -> { presets }[ $i ] -> file );
if ( - e $path && ! unlink $path ) {
# Cannot delete the file, therefore the item will not be removed from the selection.
Slic3r::GUI:: show_error ( $self , "Cannot delete file $path : $!" );
return ;
2013-03-09 16:27:18 +01:00
}
2016-10-24 16:07:36 +02:00
# Delete the preset.
2013-03-09 16:27:18 +01:00
splice @ { $self -> { presets }}, $i , 1 ;
2016-10-24 16:07:36 +02:00
# Delete the item from the UI component.
$self -> { presets_choice } -> Delete ( $i - $self -> { default_suppressed });
2014-12-25 17:35:31 +01:00
$self -> current_preset ( undef );
2016-10-24 16:07:36 +02:00
if ( $self -> { default_suppressed } && scalar ( @ { $self -> { presets }}) == 1 ) {
# Empty selection. Add the '- default -' item into the drop down selection.
$self -> { presets_choice } -> Append ( $self -> { presets } -> [ 0 ] -> name );
# and remember that the '- default -' is shown.
$self -> { default_suppressed } = 0 ;
}
# Select the 0th item. If default is suppressed, select the first valid.
$self -> select_preset ( $self -> { default_suppressed });
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
2013-03-09 16:27:18 +01:00
});
2016-11-01 13:41:24 +01:00
# C++ instance DynamicPrintConfig
2013-03-09 16:27:18 +01:00
$self -> { config } = Slic3r::Config -> new ;
2016-11-01 13:41:24 +01:00
# Initialize the DynamicPrintConfig by default keys/values.
# Possible %params keys: no_controller
2016-05-16 23:40:24 +02:00
$self -> build ( %params );
2015-06-01 14:57:07 +02:00
$self -> update_tree ;
2014-07-01 18:18:23 +02:00
$self -> _update ;
2013-03-09 16:27:18 +01:00
if ( $self -> hidden_options ) {
$self -> { config } -> apply ( Slic3r::Config -> new_from_defaults ( $self -> hidden_options ));
2012-07-27 21:13:03 +02:00
}
2012-07-18 20:36:34 +02:00
2012-06-17 22:27:05 +02:00
return $self ;
}
2016-10-24 16:07:36 +02:00
# Are the '- default -' selections suppressed by the Slic3r GUI preferences?
sub no_defaults {
return $ Slic3r::GUI:: Settings -> { _ }{ no_defaults } ? 1 : 0 ;
}
# Get a currently active preset (Perl class Slic3r::GUI::Tab::Preset).
2014-12-25 17:35:31 +01:00
sub get_current_preset {
2012-07-15 17:54:57 +02:00
my $self = shift ;
2014-12-25 17:35:31 +01:00
return $self -> get_preset ( $self -> current_preset );
2012-07-15 17:54:57 +02:00
}
2016-10-24 16:07:36 +02:00
# Get a preset (Perl class Slic3r::GUI::Tab::Preset) with an index $i.
2012-08-07 18:44:47 +02:00
sub get_preset {
2014-12-25 17:35:31 +01:00
my ( $self , $i ) = @_ ;
return $self -> { presets }[ $i ];
2012-08-07 18:44:47 +02:00
}
2013-11-11 21:45:58 +01:00
sub save_preset {
my ( $self , $name ) = @_ ;
# since buttons (and choices too) don't get focus on Mac, we set focus manually
# to the treectrl so that the EVT_* events are fired for the input field having
# focus currently. is there anything better than this?
$self -> { treectrl } -> SetFocus ;
if ( ! defined $name ) {
2014-12-25 17:35:31 +01:00
my $preset = $self -> get_current_preset ;
my $default_name = $preset -> default ? 'Untitled' : $preset -> name ;
2013-11-11 21:45:58 +01:00
$default_name =~ s/\.ini$//i ;
my $dlg = Slic3r::GUI::SavePresetWindow -> new ( $self ,
title => lc ( $self -> title ),
default => $default_name ,
2014-12-25 17:35:31 +01:00
values => [ map $_ -> name , grep ! $_ -> default && ! $_ -> external , @ { $self -> { presets }} ],
2013-11-11 21:45:58 +01:00
);
return unless $dlg -> ShowModal == wxID_OK ;
$name = $dlg -> get_name ;
}
$self -> config -> save ( sprintf "$Slic3r::GUI::datadir/%s/%s.ini" , $self -> name , $name );
$self -> load_presets ;
2014-12-25 17:35:31 +01:00
$self -> select_preset_by_name ( $name );
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
2013-11-11 21:45:58 +01:00
}
2012-07-27 21:13:03 +02:00
sub on_value_change {
2014-07-01 16:40:56 +02:00
my ( $self , $cb ) = @_ ;
$self -> { on_value_change } = $cb ;
2012-07-27 21:13:03 +02:00
}
2013-03-09 16:43:09 +01:00
sub on_presets_changed {
2014-07-01 16:40:56 +02:00
my ( $self , $cb ) = @_ ;
$self -> { on_presets_changed } = $cb ;
}
2017-02-07 18:41:09 +01:00
# This method is called whenever an option field is changed by the user.
# Propagate event to the parent through the 'on_value_changed' callback
# and call _update.
2014-07-01 16:40:56 +02:00
sub _on_value_change {
2017-02-07 18:41:09 +01:00
my ( $self , $key , $value ) = @_ ;
$self -> { on_value_change } -> ( $key , $value ) if $self -> { on_value_change };
$self -> _update ({ $key => 1 });
2014-07-01 16:40:56 +02:00
}
2017-02-07 18:41:09 +01:00
# Override this to capture changes of configuration caused either by loading or switching a preset,
# or by a user changing an option field.
2014-07-01 18:18:23 +02:00
sub _update {}
2014-07-01 16:40:56 +02:00
sub _on_presets_changed {
my $self = shift ;
2015-06-01 23:34:04 +02:00
$self -> { on_presets_changed } -> (
$self -> { presets },
2016-10-24 16:07:36 +02:00
$self -> { default_suppressed },
scalar ( $self -> { presets_choice } -> GetSelection ) + $self -> { default_suppressed },
2015-06-01 23:34:04 +02:00
$self -> is_dirty ,
) if $self -> { on_presets_changed };
2013-03-09 16:43:09 +01:00
}
2012-07-18 20:36:34 +02:00
sub on_preset_loaded {}
2012-07-27 21:13:03 +02:00
sub hidden_options {}
sub config { $_ [ 0 ] -> { config } -> clone }
2012-07-18 20:36:34 +02:00
2012-07-28 11:34:12 +02:00
sub select_default_preset {
my $self = shift ;
2014-12-25 17:35:31 +01:00
$self -> select_preset ( 0 );
2012-07-28 11:34:12 +02:00
}
2012-08-07 18:44:47 +02:00
sub select_preset {
2016-10-24 16:07:36 +02:00
my ( $self , $i ) = @_ ;
if ( $self -> { default_suppressed } && $i == 0 ) {
# Selecting the '- default -'. Add it to the combo box.
$self -> { default_suppressed } = 0 ;
$self -> { presets_choice } -> Insert ( $self -> { presets } -> [ 0 ] -> name , 0 );
} elsif ( $self -> no_defaults && ! $self -> { default_suppressed } && $i > 0 ) {
# The user wants to hide the '- default -' items and a non-default item has been added to the presets.
# Hide the '- default -' item.
$self -> { presets_choice } -> Delete ( 0 );
$self -> { default_suppressed } = 1 ;
}
$self -> { presets_choice } -> SetSelection ( $i - $self -> { default_suppressed });
2012-08-07 18:44:47 +02:00
$self -> on_select_preset ;
}
2014-12-25 17:35:31 +01:00
sub select_preset_by_name {
my ( $self , $name ) = @_ ;
2015-06-02 16:10:15 +02:00
$name = Unicode::Normalize:: NFC ( $name );
2014-12-25 17:35:31 +01:00
$self -> select_preset ( first { $self -> { presets }[ $_ ] -> name eq $name } 0 .. $# { $self -> { presets }});
}
2012-06-19 17:23:10 +02:00
sub on_select_preset {
my $self = shift ;
2014-12-25 17:35:31 +01:00
if ( $self -> is_dirty ) {
2016-10-24 16:07:36 +02:00
# Display a dialog showing the dirty options in a human readable form.
2014-12-25 17:35:31 +01:00
my $old_preset = $self -> get_current_preset ;
my $name = $old_preset -> default ? 'Default preset' : "Preset \"" . $old_preset -> name . "\"" ;
2014-12-27 14:38:06 +01:00
my @option_names = ();
foreach my $opt_key ( @ { $self -> dirty_options }) {
my $opt = $ Slic3r::Config:: Options -> { $opt_key };
my $name = $opt -> { full_label } // $opt -> { label };
if ( $opt -> { category }) {
$name = $opt -> { category } . " > $name" ;
}
push @option_names , $name ;
}
my $changes = join "\n" , map "- $_" , @option_names ;
2014-12-25 17:35:31 +01:00
my $confirm = Wx::MessageDialog -> new ( $self , "$name has unsaved changes:\n$changes\n\nDiscard changes and continue anyway?" ,
2012-07-27 13:57:58 +02:00
'Unsaved Changes' , wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION );
if ( $confirm -> ShowModal == wxID_NO ) {
2016-10-24 16:07:36 +02:00
$self -> { presets_choice } -> SetSelection ( $self -> current_preset - $self -> { default_suppressed });
2015-06-01 23:52:15 +02:00
# trigger the on_presets_changed event so that we also restore the previous value
# in the plater selector
$self -> _on_presets_changed ;
2012-07-27 13:57:58 +02:00
return ;
}
2012-06-19 17:23:10 +02:00
}
2016-10-24 16:07:36 +02:00
$self -> current_preset ( $self -> { presets_choice } -> GetSelection + $self -> { default_suppressed });
2014-12-25 17:35:31 +01:00
my $preset = $self -> get_current_preset ;
2012-08-07 18:44:47 +02:00
my $preset_config = $self -> get_preset_config ( $preset );
eval {
local $SIG { __WARN__ } = Slic3r::GUI:: warning_catcher ( $self );
2017-02-07 18:41:09 +01:00
my %keys_modified = ();
2014-07-01 16:40:56 +02:00
foreach my $opt_key ( @ { $self -> { config } -> get_keys }) {
2017-02-07 18:41:09 +01:00
if ( $preset_config -> has ( $opt_key )) {
if ( $self -> { config } -> serialize ( $opt_key ) ne $preset_config -> serialize ( $opt_key )) {
$self -> { config } -> set ( $opt_key , $preset_config -> get ( $opt_key ));
$keys_modified { $opt_key } = 1 ;
}
}
2012-06-19 17:23:10 +02:00
}
2014-12-25 17:35:31 +01:00
( $preset -> default || $preset -> external )
2013-03-30 00:30:21 +01:00
? $self -> { btn_delete_preset } -> Disable
: $self -> { btn_delete_preset } -> Enable ;
2017-02-07 18:41:09 +01:00
$self -> _update ( \ %keys_modified );
2013-03-30 00:30:21 +01:00
$self -> on_preset_loaded ;
2014-07-01 16:40:56 +02:00
$self -> reload_config ;
2014-12-25 17:35:31 +01:00
$ Slic3r::GUI:: Settings -> { presets }{ $self -> name } = $preset -> file ? basename ( $preset -> file ) : '' ;
2012-08-07 18:44:47 +02:00
};
2013-03-30 00:30:21 +01:00
if ( $@ ) {
$@ = "I was unable to load the selected config file: $@" ;
Slic3r::GUI:: catch_error ( $self );
$self -> select_default_preset ;
}
2014-12-25 17:35:31 +01:00
# use CallAfter because some field triggers schedule on_change calls using CallAfter,
# and we don't want them to be called after this update_dirty() as they would mark the
# preset dirty again
# (not sure this is true anymore now that update_dirty is idempotent)
wxTheApp -> CallAfter ( sub {
2014-12-25 18:52:27 +01:00
$self -> _on_presets_changed ;
2014-12-25 17:35:31 +01:00
$self -> update_dirty ;
});
2012-08-07 18:44:47 +02:00
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2012-06-19 17:23:10 +02:00
}
2014-07-01 16:40:56 +02:00
sub init_config_options {
my ( $self , @opt_keys ) = @_ ;
$self -> { config } -> apply ( Slic3r::Config -> new_from_defaults ( @opt_keys ));
}
2012-06-18 22:27:57 +02:00
sub add_options_page {
2012-06-17 22:27:05 +02:00
my $self = shift ;
2012-07-18 20:36:34 +02:00
my ( $title , $icon , %params ) = @_ ;
2012-06-17 22:54:08 +02:00
2013-03-09 16:27:18 +01:00
if ( $icon ) {
2016-04-09 19:10:57 +02:00
my $bitmap = Wx::Bitmap -> new ( $ Slic3r:: var -> ( $icon ), wxBITMAP_TYPE_PNG );
2012-06-18 16:46:43 +02:00
$self -> { icons } -> Add ( $bitmap );
$self -> { iconcount } ++ ;
2012-06-17 22:54:08 +02:00
}
2012-07-18 20:36:34 +02:00
2014-07-01 16:40:56 +02:00
my $page = Slic3r::GUI::Tab::Page -> new ( $self , $title , $self -> { iconcount });
2013-03-09 16:27:18 +01:00
$page -> Hide ;
2012-07-24 12:41:51 +02:00
$self -> { sizer } -> Add ( $page , 1 , wxEXPAND | wxLEFT , 5 );
2012-07-18 20:36:34 +02:00
push @ { $self -> { pages }}, $page ;
return $page ;
}
2014-07-01 16:40:56 +02:00
sub reload_config {
2012-07-24 15:30:50 +02:00
my $self = shift ;
2014-07-01 16:40:56 +02:00
$_ -> reload_config for @ { $self -> { pages }};
2012-07-24 15:30:50 +02:00
}
2012-07-18 20:36:34 +02:00
sub update_tree {
2015-01-25 11:43:34 +01:00
my ( $self ) = @_ ;
2012-07-18 20:36:34 +02:00
2015-01-25 11:43:34 +01:00
# get label of the currently selected item
my $selected = $self -> { treectrl } -> GetItemText ( $self -> { treectrl } -> GetSelection );
2012-07-18 20:36:34 +02:00
my $rootItem = $self -> { treectrl } -> GetRootItem ;
$self -> { treectrl } -> DeleteChildren ( $rootItem );
2015-01-25 11:43:34 +01:00
my $have_selection = 0 ;
2012-07-18 20:36:34 +02:00
foreach my $page ( @ { $self -> { pages }}) {
my $itemId = $self -> { treectrl } -> AppendItem ( $rootItem , $page -> { title }, $page -> { iconID });
2015-01-25 11:43:34 +01:00
if ( $page -> { title } eq $selected ) {
2015-06-01 23:11:27 +02:00
$self -> { disable_tree_sel_changed_event } = 1 ;
2015-01-25 11:43:34 +01:00
$self -> { treectrl } -> SelectItem ( $itemId );
2015-06-01 23:11:27 +02:00
$self -> { disable_tree_sel_changed_event } = 0 ;
2015-01-25 11:43:34 +01:00
$have_selection = 1 ;
}
}
if ( ! $have_selection ) {
2015-06-01 23:11:27 +02:00
# this is triggered on first load, so we don't disable the sel change event
2015-01-25 11:43:34 +01:00
$self -> { treectrl } -> SelectItem ( $self -> { treectrl } -> GetFirstChild ( $rootItem ));
2012-06-18 22:27:57 +02:00
}
}
2014-12-25 17:35:31 +01:00
sub update_dirty {
2012-06-18 22:27:57 +02:00
my $self = shift ;
2016-10-24 16:07:36 +02:00
foreach my $i ( $self -> { default_suppressed } .. $# { $self -> { presets }}) {
2014-12-25 17:35:31 +01:00
my $preset = $self -> get_preset ( $i );
2016-10-24 16:07:36 +02:00
$self -> { presets_choice } -> SetString (
$i - $self -> { default_suppressed },
( $i == $self -> current_preset && $self -> is_dirty ) ? $preset -> name . " (modified)" : $preset -> name );
2012-06-18 22:27:57 +02:00
}
2016-10-24 16:07:36 +02:00
$self -> { presets_choice } -> SetSelection ( $self -> current_preset - $self -> { default_suppressed }); # http://trac.wxwidgets.org/ticket/13769
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
2012-06-18 22:27:57 +02:00
}
2012-06-23 17:38:19 +02:00
sub is_dirty {
my $self = shift ;
2014-12-25 17:35:31 +01:00
return @ { $self -> dirty_options } > 0 ;
}
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 });
2012-06-23 17:38:19 +02:00
}
2012-06-18 22:27:57 +02:00
sub load_presets {
my $self = shift ;
2014-12-25 17:35:31 +01:00
$self -> { presets } = [
Slic3r::GUI::Tab::Preset -> new (
default => 1 ,
name => '- default -' ,
),
];
2012-06-18 22:27:57 +02:00
2014-06-14 19:59:59 +02:00
my %presets = wxTheApp -> presets ( $self -> name );
2014-04-06 22:45:40 +02:00
foreach my $preset_name ( sort keys %presets ) {
2014-12-25 17:35:31 +01:00
push @ { $self -> { presets }}, Slic3r::GUI::Tab::Preset -> new (
2014-03-25 14:04:01 +01:00
name => $preset_name ,
file => $presets { $preset_name },
2014-12-25 17:35:31 +01:00
);
2012-07-15 17:54:57 +02:00
}
2014-12-25 17:35:31 +01:00
$self -> current_preset ( undef );
2016-10-24 16:07:36 +02:00
$self -> { default_suppressed } = Slic3r::GUI::Tab -> no_defaults && scalar ( @ { $self -> { presets }}) > 1 ;
2012-06-18 22:27:57 +02:00
$self -> { presets_choice } -> Clear ;
2016-10-24 16:07:36 +02:00
foreach my $preset ( @ { $self -> { presets }}) {
next if ( $preset -> default && $self -> { default_suppressed });
$self -> { presets_choice } -> Append ( $preset -> name );
}
2012-06-19 18:11:51 +02:00
{
2012-07-15 17:54:57 +02:00
# load last used preset
2015-05-26 02:01:43 +02:00
my $i = first { basename ( $self -> { presets }[ $_ ] -> file ) eq ( $ Slic3r::GUI:: Settings -> { presets }{ $self -> name } || '' ) } 1 .. $# { $self -> { presets }};
2016-10-24 16:07:36 +02:00
$self -> select_preset ( $i || $self -> { default_suppressed });
2012-06-19 18:11:51 +02:00
}
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
2012-06-17 22:27:05 +02:00
}
2013-03-09 16:27:18 +01:00
sub load_config_file {
2012-06-19 14:47:02 +02:00
my $self = shift ;
my ( $file ) = @_ ;
2013-03-09 16:27:18 +01:00
# look for the loaded config among the existing menu items
my $i = first { $self -> { presets }[ $_ ]{ file } eq $file && $self -> { presets }[ $_ ]{ external } } 1 .. $# { $self -> { presets }};
if ( ! $i ) {
my $preset_name = basename ( $file ); # keep the .ini suffix
2014-12-25 17:35:31 +01:00
push @ { $self -> { presets }}, Slic3r::GUI::Tab::Preset -> new (
2013-03-09 16:27:18 +01:00
file => $file ,
name => $preset_name ,
external => 1 ,
2014-12-25 17:35:31 +01:00
);
2013-03-09 16:27:18 +01:00
$self -> { presets_choice } -> Append ( $preset_name );
$i = $# { $self -> { presets }};
2012-06-19 14:47:02 +02:00
}
2016-10-24 16:07:36 +02:00
$self -> { presets_choice } -> SetSelection ( $i - $self -> { default_suppressed });
2013-03-09 16:27:18 +01:00
$self -> on_select_preset ;
2014-07-01 16:40:56 +02:00
$self -> _on_presets_changed ;
}
sub load_config {
my $self = shift ;
my ( $config ) = @_ ;
2017-02-07 18:41:09 +01:00
my %keys_modified = ();
2014-07-01 16:40:56 +02:00
foreach my $opt_key ( @ { $self -> { config } -> diff ( $config )}) {
$self -> { config } -> set ( $opt_key , $config -> get ( $opt_key ));
2017-02-07 18:41:09 +01:00
$keys_modified { $opt_key } = 1 ;
2014-12-25 17:35:31 +01:00
$self -> update_dirty ;
2014-07-01 16:40:56 +02:00
}
2017-02-07 18:41:09 +01:00
# Initialize UI components with the config values.
2014-07-01 16:40:56 +02:00
$self -> reload_config ;
2017-02-07 18:41:09 +01:00
$self -> _update ( \ %keys_modified );
2012-06-19 14:47:02 +02:00
}
2014-12-25 17:35:31 +01:00
sub get_preset_config {
my ( $self , $preset ) = @_ ;
return $preset -> config ( $self -> { config } -> get_keys );
}
2014-07-01 18:18:23 +02:00
sub get_field {
my ( $self , $opt_key , $opt_index ) = @_ ;
foreach my $page ( @ { $self -> { pages } }) {
my $field = $page -> get_field ( $opt_key , $opt_index );
return $field if defined $field ;
}
return undef ;
}
2014-09-21 15:29:52 +02:00
sub set_value {
my $self = shift ;
my ( $opt_key , $value ) = @_ ;
my $changed = 0 ;
foreach my $page ( @ { $self -> { pages } }) {
$changed = 1 if $page -> set_value ( $opt_key , $value );
}
return $changed ;
}
2012-06-17 22:27:05 +02:00
package Slic3r::GUI::Tab::Print ;
use base 'Slic3r::GUI::Tab' ;
2015-05-28 18:56:35 +02:00
use List::Util qw(first) ;
2014-11-22 22:52:12 +01:00
use Wx qw(:icon :dialog :id) ;
2012-07-27 21:13:03 +02:00
sub name { 'print' }
2012-07-18 20:36:34 +02:00
sub title { 'Print Settings' }
sub build {
my $self = shift ;
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
$self -> init_config_options ( qw(
layer_height first_layer_height
perimeters spiral_vase
top_solid_layers bottom_solid_layers
2016-10-16 22:11:19 +02:00
extra_perimeters ensure_vertical_shell_thickness avoid_crossing_perimeters thin_walls overhangs
2014-07-01 16:40:56 +02:00
seam_position external_perimeters_first
2014-11-27 00:38:05 +01:00
fill_density fill_pattern external_fill_pattern
2014-07-01 16:40:56 +02:00
infill_every_layers infill_only_where_needed
solid_infill_every_layers fill_angle solid_infill_below_area
only_retract_when_crossing_perimeters infill_first
2016-09-13 15:02:28 +02:00
max_print_speed max_volumetric_speed
max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative
2014-07-01 16:40:56 +02:00
perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed
solid_infill_speed top_solid_infill_speed support_material_speed
2016-03-19 22:30:02 -05:00
support_material_xy_spacing
2014-07-01 16:40:56 +02:00
support_material_interface_speed bridge_speed gap_fill_speed
travel_speed
first_layer_speed
perimeter_acceleration infill_acceleration bridge_acceleration
first_layer_acceleration default_acceleration
skirts skirt_distance skirt_height min_skirt_length
brim_width
support_material support_material_threshold support_material_enforce_layers
raft_layers
2016-11-30 16:04:15 +01:00
support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle
2016-11-30 17:33:55 +01:00
support_material_interface_layers support_material_interface_spacing support_material_interface_contact_loops
2016-10-04 13:54:10 +02:00
support_material_contact_distance support_material_buildplate_only dont_support_bridges
2014-07-01 16:40:56 +02:00
notes
complete_objects extruder_clearance_radius extruder_clearance_height
gcode_comments output_filename_format
post_process
2014-12-17 00:34:00 +01:00
perimeter_extruder infill_extruder solid_infill_extruder
support_material_extruder support_material_interface_extruder
2014-07-01 16:40:56 +02:00
ooze_prevention standby_temperature_delta
interface_shells
extrusion_width first_layer_extrusion_width perimeter_extrusion_width
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
top_infill_extrusion_width support_material_extrusion_width
2015-02-01 12:08:25 +01:00
infill_overlap bridge_flow_ratio
2017-02-14 12:36:04 +01:00
clip_multipart_objects xy_size_compensation threads resolution
2014-07-01 16:40:56 +02:00
) );
2015-12-07 12:17:06 +01:00
$self -> { config } -> set ( 'print_settings_id' , '' );
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'Layers and perimeters' , 'layers.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Layer height' );
$optgroup -> append_single_option_line ( 'layer_height' );
$optgroup -> append_single_option_line ( 'first_layer_height' );
}
2013-07-05 21:55:01 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Vertical shells' );
$optgroup -> append_single_option_line ( 'perimeters' );
$optgroup -> append_single_option_line ( 'spiral_vase' );
}
2012-08-07 20:22:26 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Horizontal shells' );
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 = $page -> new_optgroup ( 'Quality (slower slicing)' );
$optgroup -> append_single_option_line ( 'extra_perimeters' );
2016-10-16 22:11:19 +02:00
$optgroup -> append_single_option_line ( 'ensure_vertical_shell_thickness' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'avoid_crossing_perimeters' );
$optgroup -> append_single_option_line ( 'thin_walls' );
$optgroup -> append_single_option_line ( 'overhangs' );
}
{
my $optgroup = $page -> new_optgroup ( 'Advanced' );
$optgroup -> append_single_option_line ( 'seam_position' );
$optgroup -> append_single_option_line ( 'external_perimeters_first' );
}
}
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
2016-02-20 16:18:48 +01:00
my $page = $self -> add_options_page ( 'Infill' , 'infill.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Infill' );
$optgroup -> append_single_option_line ( 'fill_density' );
$optgroup -> append_single_option_line ( 'fill_pattern' );
2014-11-27 00:38:05 +01:00
$optgroup -> append_single_option_line ( 'external_fill_pattern' );
2014-07-01 16:40:56 +02:00
}
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Reducing printing time' );
$optgroup -> append_single_option_line ( 'infill_every_layers' );
$optgroup -> append_single_option_line ( 'infill_only_where_needed' );
}
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Advanced' );
$optgroup -> append_single_option_line ( 'solid_infill_every_layers' );
$optgroup -> append_single_option_line ( 'fill_angle' );
$optgroup -> append_single_option_line ( 'solid_infill_below_area' );
$optgroup -> append_single_option_line ( 'only_retract_when_crossing_perimeters' );
$optgroup -> append_single_option_line ( 'infill_first' );
}
}
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'Skirt and brim' , 'box.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Skirt' );
$optgroup -> append_single_option_line ( 'skirts' );
$optgroup -> append_single_option_line ( 'skirt_distance' );
$optgroup -> append_single_option_line ( 'skirt_height' );
$optgroup -> append_single_option_line ( 'min_skirt_length' );
}
2013-01-28 14:12:01 +01:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Brim' );
$optgroup -> append_single_option_line ( 'brim_width' );
}
}
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'Support material' , 'building.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Support material' );
$optgroup -> append_single_option_line ( 'support_material' );
$optgroup -> append_single_option_line ( 'support_material_threshold' );
$optgroup -> append_single_option_line ( 'support_material_enforce_layers' );
}
{
my $optgroup = $page -> new_optgroup ( 'Raft' );
$optgroup -> append_single_option_line ( 'raft_layers' );
}
{
my $optgroup = $page -> new_optgroup ( 'Options for support material and raft' );
2015-01-19 09:52:24 +01:00
$optgroup -> append_single_option_line ( 'support_material_contact_distance' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'support_material_pattern' );
2016-10-04 14:38:13 +02:00
$optgroup -> append_single_option_line ( 'support_material_with_sheath' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'support_material_spacing' );
$optgroup -> append_single_option_line ( 'support_material_angle' );
$optgroup -> append_single_option_line ( 'support_material_interface_layers' );
$optgroup -> append_single_option_line ( 'support_material_interface_spacing' );
2016-11-30 17:33:55 +01:00
$optgroup -> append_single_option_line ( 'support_material_interface_contact_loops' );
2016-10-04 13:54:10 +02:00
$optgroup -> append_single_option_line ( 'support_material_buildplate_only' );
2016-03-19 22:30:02 -05:00
$optgroup -> append_single_option_line ( 'support_material_xy_spacing' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'dont_support_bridges' );
2016-11-30 16:04:15 +01:00
$optgroup -> append_single_option_line ( 'support_material_synchronize_layers' );
2014-07-01 16:40:56 +02:00
}
}
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
2014-12-23 20:47:11 +01:00
my $page = $self -> add_options_page ( 'Speed' , 'time.png' );
2012-06-17 22:27:05 +02:00
{
2014-12-23 20:47:11 +01:00
my $optgroup = $page -> new_optgroup ( 'Speed for print moves' );
$optgroup -> append_single_option_line ( 'perimeter_speed' );
$optgroup -> append_single_option_line ( 'small_perimeter_speed' );
$optgroup -> append_single_option_line ( 'external_perimeter_speed' );
$optgroup -> append_single_option_line ( 'infill_speed' );
$optgroup -> append_single_option_line ( 'solid_infill_speed' );
$optgroup -> append_single_option_line ( 'top_solid_infill_speed' );
$optgroup -> append_single_option_line ( 'support_material_speed' );
$optgroup -> append_single_option_line ( 'support_material_interface_speed' );
$optgroup -> append_single_option_line ( 'bridge_speed' );
$optgroup -> append_single_option_line ( 'gap_fill_speed' );
2014-07-01 16:40:56 +02:00
}
2013-09-18 17:45:02 +02:00
{
2014-12-23 20:47:11 +01:00
my $optgroup = $page -> new_optgroup ( 'Speed for non-print moves' );
$optgroup -> append_single_option_line ( 'travel_speed' );
2014-07-01 16:40:56 +02:00
}
2014-03-25 01:11:28 +01:00
{
2014-12-23 20:47:11 +01:00
my $optgroup = $page -> new_optgroup ( 'Modifiers' );
$optgroup -> append_single_option_line ( 'first_layer_speed' );
}
{
my $optgroup = $page -> new_optgroup ( 'Acceleration control (advanced)' );
$optgroup -> append_single_option_line ( 'perimeter_acceleration' );
$optgroup -> append_single_option_line ( 'infill_acceleration' );
$optgroup -> append_single_option_line ( 'bridge_acceleration' );
$optgroup -> append_single_option_line ( 'first_layer_acceleration' );
$optgroup -> append_single_option_line ( 'default_acceleration' );
2014-07-01 16:40:56 +02:00
}
2015-05-31 22:04:32 +02:00
{
my $optgroup = $page -> new_optgroup ( 'Autospeed (advanced)' );
$optgroup -> append_single_option_line ( 'max_print_speed' );
$optgroup -> append_single_option_line ( 'max_volumetric_speed' );
2016-09-13 15:02:28 +02:00
$optgroup -> append_single_option_line ( 'max_volumetric_extrusion_rate_slope_positive' );
$optgroup -> append_single_option_line ( 'max_volumetric_extrusion_rate_slope_negative' );
2015-05-31 22:04:32 +02:00
}
2014-07-01 16:40:56 +02:00
}
2012-08-07 20:14:28 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'Multiple Extruders' , 'funnel.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Extruders' );
$optgroup -> append_single_option_line ( 'perimeter_extruder' );
$optgroup -> append_single_option_line ( 'infill_extruder' );
2014-12-17 00:34:00 +01:00
$optgroup -> append_single_option_line ( 'solid_infill_extruder' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'support_material_extruder' );
$optgroup -> append_single_option_line ( 'support_material_interface_extruder' );
}
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Ooze prevention' );
$optgroup -> append_single_option_line ( 'ooze_prevention' );
$optgroup -> append_single_option_line ( 'standby_temperature_delta' );
}
2013-03-16 19:58:34 +01:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Advanced' );
$optgroup -> append_single_option_line ( 'interface_shells' );
}
}
{
my $page = $self -> add_options_page ( 'Advanced' , 'wrench.png' );
{
my $optgroup = $page -> new_optgroup ( 'Extrusion width' ,
label_width => 180 ,
);
$optgroup -> append_single_option_line ( 'extrusion_width' );
$optgroup -> append_single_option_line ( 'first_layer_extrusion_width' );
$optgroup -> append_single_option_line ( 'perimeter_extrusion_width' );
$optgroup -> append_single_option_line ( 'external_perimeter_extrusion_width' );
$optgroup -> append_single_option_line ( 'infill_extrusion_width' );
$optgroup -> append_single_option_line ( 'solid_infill_extrusion_width' );
$optgroup -> append_single_option_line ( 'top_infill_extrusion_width' );
$optgroup -> append_single_option_line ( 'support_material_extrusion_width' );
}
2015-02-01 12:08:25 +01:00
{
my $optgroup = $page -> new_optgroup ( 'Overlap' );
$optgroup -> append_single_option_line ( 'infill_overlap' );
}
2014-07-01 16:40:56 +02:00
{
my $optgroup = $page -> new_optgroup ( 'Flow' );
$optgroup -> append_single_option_line ( 'bridge_flow_ratio' );
}
{
my $optgroup = $page -> new_optgroup ( 'Other' );
2017-02-14 12:36:04 +01:00
$optgroup -> append_single_option_line ( 'clip_multipart_objects' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'xy_size_compensation' );
$optgroup -> append_single_option_line ( 'threads' ) if $ Slic3r:: have_threads ;
$optgroup -> append_single_option_line ( 'resolution' );
}
}
2014-12-23 20:47:11 +01:00
{
my $page = $self -> add_options_page ( 'Output options' , 'page_white_go.png' );
{
my $optgroup = $page -> new_optgroup ( 'Sequential printing' );
$optgroup -> append_single_option_line ( 'complete_objects' );
my $line = Slic3r::GUI::OptionsGroup::Line -> new (
label => 'Extruder clearance (mm)' ,
);
foreach my $opt_key ( qw(extruder_clearance_radius extruder_clearance_height) ) {
my $option = $optgroup -> get_option ( $opt_key );
$option -> width ( 60 );
$line -> append_option ( $option );
}
$optgroup -> append_line ( $line );
}
{
my $optgroup = $page -> new_optgroup ( 'Output file' );
$optgroup -> append_single_option_line ( 'gcode_comments' );
{
my $option = $optgroup -> get_option ( 'output_filename_format' );
$option -> full_width ( 1 );
$optgroup -> append_single_option_line ( $option );
}
}
{
my $optgroup = $page -> new_optgroup ( 'Post-processing scripts' ,
label_width => 0 ,
);
my $option = $optgroup -> get_option ( 'post_process' );
$option -> full_width ( 1 );
$option -> height ( 50 );
$optgroup -> append_single_option_line ( $option );
}
}
{
my $page = $self -> add_options_page ( 'Notes' , 'note.png' );
{
my $optgroup = $page -> new_optgroup ( 'Notes' ,
label_width => 0 ,
);
my $option = $optgroup -> get_option ( 'notes' );
$option -> full_width ( 1 );
$option -> height ( 250 );
$optgroup -> append_single_option_line ( $option );
}
}
2012-06-17 22:27:05 +02:00
}
2017-02-07 18:41:09 +01:00
# Slic3r::GUI::Tab::Print::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user.
2014-07-01 18:18:23 +02:00
sub _update {
2017-02-07 18:41:09 +01:00
# $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
my ( $self , $keys_modified ) = @_ ;
$keys_modified // = {};
2014-07-01 18:18:23 +02:00
my $config = $self -> { config };
2014-11-22 22:52:12 +01:00
if ( $config -> spiral_vase && ! ( $config -> perimeters == 1 && $config -> top_solid_layers == 0 && $config -> fill_density == 0 )) {
2015-01-18 22:21:50 +01:00
my $dialog = Wx::MessageDialog -> new ( $self ,
"The Spiral Vase mode requires:\n"
. "- one perimeter\n"
. "- no top solid layers\n"
. "- 0% fill density\n"
. "- no support material\n"
. "\nShall I adjust those settings in order to enable Spiral Vase?" ,
2014-11-22 22:52:12 +01:00
'Spiral Vase' , wxICON_WARNING | wxYES | wxNO );
if ( $dialog -> ShowModal () == wxID_YES ) {
my $new_conf = Slic3r::Config -> new ;
$new_conf -> set ( "perimeters" , 1 );
$new_conf -> set ( "top_solid_layers" , 0 );
$new_conf -> set ( "fill_density" , 0 );
2015-01-18 22:21:50 +01:00
$new_conf -> set ( "support_material" , 0 );
2014-11-22 22:52:12 +01:00
$self -> load_config ( $new_conf );
} else {
my $new_conf = Slic3r::Config -> new ;
$new_conf -> set ( "spiral_vase" , 0 );
$self -> load_config ( $new_conf );
}
}
2016-11-11 15:05:39 +01:00
2017-02-07 18:41:09 +01:00
if ( $keys_modified -> { 'layer_height' }) {
# If the user had set the variable layer height, reset it and let the user know.
}
2016-11-11 15:05:39 +01:00
if ( $config -> support_material ) {
# Ask only once.
if ( ! $self -> { support_material_overhangs_queried }) {
$self -> { support_material_overhangs_queried } = 1 ;
if ( $config -> overhangs != 1 ) {
my $dialog = Wx::MessageDialog -> new ( $self ,
"Supports work better, if the following feature is enabled:\n"
. "- Detect bridging perimeters\n"
. "\nShall I adjust those settings for supports?" ,
'Support Generator' , wxICON_WARNING | wxYES | wxNO | wxCANCEL );
my $answer = $dialog -> ShowModal ();
my $new_conf = Slic3r::Config -> new ;
if ( $answer == wxID_YES ) {
# Enable "detect bridging perimeters".
$new_conf -> set ( "overhangs" , 1 );
} elsif ( $answer == wxID_NO ) {
# Do nothing, leave supports on and "detect bridging perimeters" off.
} elsif ( $answer == wxID_CANCEL ) {
# Disable supports.
$new_conf -> set ( "support_material" , 0 );
$self -> { support_material_overhangs_queried } = 0 ;
}
$self -> load_config ( $new_conf );
}
}
} else {
$self -> { support_material_overhangs_queried } = 0 ;
}
2014-08-08 17:18:41 +02:00
2015-05-28 18:56:35 +02:00
if ( $config -> fill_density == 100
&& ! first { $_ eq $config -> fill_pattern } @ { $ Slic3r::Config:: Options -> { external_fill_pattern }{ values }}) {
my $dialog = Wx::MessageDialog -> new ( $self ,
"The " . $config -> fill_pattern . " infill pattern is not supposed to work at 100% density.\n"
. "\nShall I switch to rectilinear fill pattern?" ,
'Infill' , wxICON_WARNING | wxYES | wxNO );
my $new_conf = Slic3r::Config -> new ;
if ( $dialog -> ShowModal () == wxID_YES ) {
2016-09-26 13:58:47 +02:00
$new_conf -> set ( "fill_pattern" , 'rectilinear' );
$new_conf -> set ( "fill_density" , 100 );
2015-05-28 18:56:35 +02:00
} else {
$new_conf -> set ( "fill_density" , 40 );
}
$self -> load_config ( $new_conf );
}
2014-07-01 18:18:23 +02:00
my $have_perimeters = $config -> perimeters > 0 ;
$self -> get_field ( $_ ) -> toggle ( $have_perimeters )
2016-10-16 22:11:19 +02:00
for qw(extra_perimeters ensure_vertical_shell_thickness thin_walls overhangs seam_position external_perimeters_first
2014-12-23 20:47:11 +01:00
external_perimeter_extrusion_width
perimeter_speed small_perimeter_speed external_perimeter_speed) ;
2014-07-01 18:18:23 +02:00
my $have_infill = $config -> fill_density > 0 ;
2015-03-06 21:35:00 +01:00
# infill_extruder uses the same logic as in Print::extruders()
2014-07-01 18:18:23 +02:00
$self -> get_field ( $_ ) -> toggle ( $have_infill )
2014-12-23 20:47:11 +01:00
for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers
solid_infill_below_area infill_extruder) ;
my $have_solid_infill = ( $config -> top_solid_layers > 0 ) || ( $config -> bottom_solid_layers > 0 );
2015-03-06 21:35:00 +01:00
# solid_infill_extruder uses the same logic as in Print::extruders()
2014-12-23 20:47:11 +01:00
$self -> get_field ( $_ ) -> toggle ( $have_solid_infill )
for qw(external_fill_pattern infill_first solid_infill_extruder solid_infill_extrusion_width
solid_infill_speed) ;
$self -> get_field ( $_ ) -> toggle ( $have_infill || $have_solid_infill )
for qw(fill_angle infill_extrusion_width infill_speed bridge_speed) ;
$self -> get_field ( 'gap_fill_speed' ) -> toggle ( $have_perimeters && $have_infill );
my $have_top_solid_infill = $config -> top_solid_layers > 0 ;
$self -> get_field ( $_ ) -> toggle ( $have_top_solid_infill )
for qw(top_infill_extrusion_width top_solid_infill_speed) ;
2014-07-01 18:18:23 +02:00
my $have_default_acceleration = $config -> default_acceleration > 0 ;
$self -> get_field ( $_ ) -> toggle ( $have_default_acceleration )
for qw(perimeter_acceleration infill_acceleration bridge_acceleration first_layer_acceleration) ;
my $have_skirt = $config -> skirts > 0 || $config -> min_skirt_length > 0 ;
$self -> get_field ( $_ ) -> toggle ( $have_skirt )
for qw(skirt_distance skirt_height) ;
2014-12-23 20:47:11 +01:00
my $have_brim = $config -> brim_width > 0 ;
2015-03-06 21:35:00 +01:00
# perimeter_extruder uses the same logic as in Print::extruders()
2014-12-23 20:47:11 +01:00
$self -> get_field ( 'perimeter_extruder' ) -> toggle ( $have_perimeters || $have_brim );
2014-07-01 18:18:23 +02:00
my $have_support_material = $config -> support_material || $config -> raft_layers > 0 ;
my $have_support_interface = $config -> support_material_interface_layers > 0 ;
$self -> get_field ( $_ ) -> toggle ( $have_support_material )
2016-10-04 14:38:13 +02:00
for qw(support_material_threshold support_material_pattern support_material_with_sheath
2016-11-30 16:04:15 +01:00
support_material_spacing support_material_synchronize_layers support_material_angle
2014-07-01 18:18:23 +02:00
support_material_interface_layers dont_support_bridges
2016-04-09 12:54:32 -05:00
support_material_extrusion_width support_material_contact_distance support_material_xy_spacing) ;
2014-07-01 18:18:23 +02:00
$self -> get_field ( $_ ) -> toggle ( $have_support_material && $have_support_interface )
2014-12-23 20:47:11 +01:00
for qw(support_material_interface_spacing support_material_interface_extruder
2016-11-30 17:33:55 +01:00
support_material_interface_speed support_material_interface_contact_loops) ;
2014-12-23 20:47:11 +01:00
$self -> get_field ( 'perimeter_extrusion_width' ) -> toggle ( $have_perimeters || $have_skirt || $have_brim );
$self -> get_field ( 'support_material_extruder' ) -> toggle ( $have_support_material || $have_skirt );
$self -> get_field ( 'support_material_speed' ) -> toggle ( $have_support_material || $have_brim || $have_skirt );
2014-07-01 18:18:23 +02:00
my $have_sequential_printing = $config -> complete_objects ;
$self -> get_field ( $_ ) -> toggle ( $have_sequential_printing )
for qw(extruder_clearance_radius extruder_clearance_height) ;
my $have_ooze_prevention = $config -> ooze_prevention ;
$self -> get_field ( $_ ) -> toggle ( $have_ooze_prevention )
for qw(standby_temperature_delta) ;
}
2012-07-27 21:13:03 +02:00
sub hidden_options { ! $ Slic3r:: have_threads ? qw(threads) : () }
2012-06-18 14:29:47 +02:00
package Slic3r::GUI::Tab::Filament ;
use base 'Slic3r::GUI::Tab' ;
2012-07-27 21:13:03 +02:00
sub name { 'filament' }
2012-07-18 20:36:34 +02:00
sub title { 'Filament Settings' }
sub build {
my $self = shift ;
2012-06-18 14:29:47 +02:00
2014-07-01 16:40:56 +02:00
$self -> init_config_options ( qw(
2017-01-15 23:56:01 -06:00
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost
2014-07-01 16:40:56 +02:00
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
fan_always_on cooling
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
fan_below_layer_time slowdown_below_layer_time min_print_speed
) );
2015-12-07 12:17:06 +01:00
$self -> { config } -> set ( 'filament_settings_id' , '' );
2012-06-18 14:29:47 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'Filament' , 'spool.png' );
2012-06-18 14:29:47 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Filament' );
2015-05-26 02:01:43 +02:00
$optgroup -> append_single_option_line ( 'filament_colour' , 0 );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'filament_diameter' , 0 );
$optgroup -> append_single_option_line ( 'extrusion_multiplier' , 0 );
2017-01-15 23:56:01 -06:00
$optgroup -> append_single_option_line ( 'filament_density' , 0 );
$optgroup -> append_single_option_line ( 'filament_cost' , 0 );
2014-07-01 16:40:56 +02:00
}
{
my $optgroup = $page -> 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 );
}
}
}
{
my $page = $self -> add_options_page ( 'Cooling' , 'hourglass.png' );
{
my $optgroup = $page -> new_optgroup ( 'Enable' );
$optgroup -> append_single_option_line ( 'fan_always_on' );
$optgroup -> append_single_option_line ( 'cooling' );
my $line = Slic3r::GUI::OptionsGroup::Line -> new (
label => '' ,
full_width => 1 ,
widget => sub {
my ( $parent ) = @_ ;
return $self -> { description_line } = Slic3r::GUI::OptionsGroup::StaticText -> new ( $parent );
2012-10-28 12:43:41 +01:00
},
2014-07-01 16:40:56 +02:00
);
$optgroup -> append_line ( $line );
}
2012-06-18 14:29:47 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Fan settings' );
{
my $line = Slic3r::GUI::OptionsGroup::Line -> new (
label => 'Fan speed' ,
);
$line -> append_option ( $optgroup -> get_option ( 'min_fan_speed' ));
$line -> append_option ( $optgroup -> get_option ( 'max_fan_speed' ));
$optgroup -> append_line ( $line );
}
$optgroup -> append_single_option_line ( 'bridge_fan_speed' );
$optgroup -> append_single_option_line ( 'disable_fan_first_layers' );
}
2012-06-18 14:29:47 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Cooling thresholds' ,
label_width => 250 ,
);
$optgroup -> append_single_option_line ( 'fan_below_layer_time' );
$optgroup -> append_single_option_line ( 'slowdown_below_layer_time' );
$optgroup -> append_single_option_line ( 'min_print_speed' );
}
}
2016-11-01 13:41:24 +01:00
{
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 );
}
}
2014-07-01 18:18:23 +02:00
}
2017-02-07 18:41:09 +01:00
# Slic3r::GUI::Tab::Filament::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user.
2014-07-01 18:18:23 +02:00
sub _update {
2017-02-07 18:41:09 +01:00
# $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
my ( $self , $keys_modified ) = @_ ;
2014-07-01 16:40:56 +02:00
$self -> _update_description ;
2014-07-01 18:18:23 +02:00
my $cooling = $self -> { config } -> cooling ;
$self -> get_field ( $_ ) -> toggle ( $cooling )
2014-07-01 21:58:57 +02:00
for qw(max_fan_speed fan_below_layer_time slowdown_below_layer_time min_print_speed) ;
$self -> get_field ( $_ ) -> toggle ( $cooling || $self -> { config } -> fan_always_on )
for qw(min_fan_speed disable_fan_first_layers) ;
2012-06-18 14:29:47 +02:00
}
2012-10-28 12:43:41 +01:00
sub _update_description {
my $self = shift ;
my $config = $self -> config ;
my $msg = "" ;
2013-05-10 15:09:27 +02:00
my $fan_other_layers = $config -> fan_always_on
? sprintf "will always run at %d%%%s." , $config -> min_fan_speed ,
( $config -> disable_fan_first_layers > 1
? " except for the first " . $config -> disable_fan_first_layers . " layers"
: $config -> disable_fan_first_layers == 1
? " except for the first layer"
: "" )
: "will be turned off." ;
2012-10-28 12:43:41 +01:00
if ( $config -> cooling ) {
2013-09-16 10:09:39 +02:00
$msg = sprintf "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s)." ,
$config -> slowdown_below_layer_time , $config -> max_fan_speed , $config -> slowdown_below_layer_time , $config -> min_print_speed ;
2012-10-28 12:43:41 +01:00
if ( $config -> fan_below_layer_time > $config -> slowdown_below_layer_time ) {
$msg .= sprintf "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%." ,
$config -> fan_below_layer_time , $config -> max_fan_speed , $config -> min_fan_speed ;
}
2013-05-10 15:09:27 +02:00
$msg .= "\nDuring the other layers, fan $fan_other_layers"
} else {
$msg = "Fan $fan_other_layers" ;
2012-10-28 12:43:41 +01:00
}
2013-03-09 16:27:18 +01:00
$self -> { description_line } -> SetText ( $msg );
2012-10-28 12:43:41 +01:00
}
2012-06-17 22:27:05 +02:00
package Slic3r::GUI::Tab::Printer ;
use base 'Slic3r::GUI::Tab' ;
2015-11-04 19:43:23 +01:00
use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog) ;
2014-06-16 01:49:49 +02:00
use Wx::Event qw(EVT_BUTTON) ;
2012-06-17 22:27:05 +02:00
2012-07-27 21:13:03 +02:00
sub name { 'printer' }
2012-07-18 20:36:34 +02:00
sub title { 'Printer Settings' }
sub build {
my $self = shift ;
2016-05-16 23:40:24 +02:00
my ( %params ) = @_ ;
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
$self -> init_config_options ( qw(
bed_shape z_offset
gcode_flavor use_relative_e_distances
2015-01-03 23:25:55 +01:00
serial_port serial_speed
2014-12-28 01:30:05 +01:00
octoprint_host octoprint_apikey
2016-10-21 16:53:42 +02:00
use_firmware_retraction pressure_advance
2016-07-16 09:52:11 -05:00
use_volumetric_e variable_layer_height
2015-01-30 20:08:00 +01:00
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode
2014-07-01 16:40:56 +02:00
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
) );
2015-12-07 12:17:06 +01:00
$self -> { config } -> set ( 'printer_settings_id' , '' );
2012-07-24 15:30:50 +02:00
2014-06-16 01:49:49 +02:00
my $bed_shape_widget = sub {
my ( $parent ) = @_ ;
2015-11-02 01:35:28 +01:00
my $btn = Wx::Button -> new ( $parent , - 1 , "Set…" , wxDefaultPosition , wxDefaultSize ,
wxBU_LEFT | wxBU_EXACTFIT );
2014-06-16 01:49:49 +02:00
$btn -> SetFont ( $ Slic3r::GUI:: small_font );
if ( $ Slic3r::GUI:: have_button_icons ) {
2016-04-09 19:10:57 +02:00
$btn -> SetBitmap ( Wx::Bitmap -> new ( $ Slic3r:: var -> ( "cog.png" ), wxBITMAP_TYPE_PNG ));
2014-06-16 01:49:49 +02:00
}
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 );
2014-12-25 17:35:31 +01:00
$self -> update_dirty ;
2014-07-01 16:40:56 +02:00
$self -> _on_value_change ( 'bed_shape' , $value );
2014-06-16 01:49:49 +02:00
}
});
return $sizer ;
};
2014-07-01 16:40:56 +02:00
$self -> { extruders_count } = 1 ;
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
{
my $page = $self -> add_options_page ( 'General' , 'printer_empty.png' );
2012-06-17 22:27:05 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> 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' );
}
2012-06-17 23:24:10 +02:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Capabilities' );
{
my $option = Slic3r::GUI::OptionsGroup::Option -> new (
opt_id => 'extruders_count' ,
type => 'i' ,
default => 1 ,
label => 'Extruders' ,
tooltip => 'Number of extruders of the printer.' ,
min => 1 ,
);
$optgroup -> append_single_option_line ( $option );
}
$optgroup -> on_change ( sub {
my ( $opt_id ) = @_ ;
if ( $opt_id eq 'extruders_count' ) {
2015-06-01 14:57:07 +02:00
wxTheApp -> CallAfter ( sub {
$self -> _extruders_count_changed ( $optgroup -> get_value ( 'extruders_count' ));
});
2014-12-25 17:35:31 +01:00
$self -> update_dirty ;
2014-07-01 16:40:56 +02:00
}
});
}
2016-05-16 23:40:24 +02:00
if ( ! $params { no_controller })
2015-01-03 23:25:55 +01:00
{
my $optgroup = $page -> new_optgroup ( 'USB/Serial connection' );
2015-01-04 19:14:54 +01:00
my $line = Slic3r::GUI::OptionsGroup::Line -> new (
label => 'Serial port' ,
);
my $serial_port = $optgroup -> get_option ( 'serial_port' );
$serial_port -> side_widget ( sub {
my ( $parent ) = @_ ;
2016-04-09 19:10:57 +02:00
my $btn = Wx::BitmapButton -> new ( $parent , - 1 , Wx::Bitmap -> new ( $ Slic3r:: var -> ( "arrow_rotate_clockwise.png" ), wxBITMAP_TYPE_PNG ),
2015-01-04 19:14:54 +01:00
wxDefaultPosition , wxDefaultSize , & Wx:: wxBORDER_NONE );
$btn -> SetToolTipString ( "Rescan serial ports" )
if $btn -> can ( 'SetToolTipString' );
EVT_BUTTON ( $self , $btn , \& _update_serial_ports );
return $btn ;
});
2015-11-02 01:35:28 +01:00
my $serial_test = sub {
my ( $parent ) = @_ ;
my $btn = $self -> { serial_test_btn } = Wx::Button -> new ( $parent , - 1 ,
"Test" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT | wxBU_EXACTFIT );
$btn -> SetFont ( $ Slic3r::GUI:: small_font );
if ( $ Slic3r::GUI:: have_button_icons ) {
2016-04-09 19:10:57 +02:00
$btn -> SetBitmap ( Wx::Bitmap -> new ( $ Slic3r:: var -> ( "wrench.png" ), wxBITMAP_TYPE_PNG ));
2015-11-02 01:35:28 +01:00
}
EVT_BUTTON ( $self , $btn , sub {
my $sender = Slic3r::GCode::Sender -> new ;
my $res = $sender -> connect (
$self -> { config } -> serial_port ,
$self -> { config } -> serial_speed ,
);
2015-11-02 20:34:36 +01:00
if ( $res && $sender -> wait_connected ) {
2015-11-02 01:35:28 +01:00
Slic3r::GUI:: show_info ( $self , "Connection to printer works correctly." , "Success!" );
} else {
Slic3r::GUI:: show_error ( $self , "Connection failed." );
}
});
return $btn ;
};
2015-01-04 19:14:54 +01:00
$line -> append_option ( $serial_port );
$line -> append_option ( $optgroup -> get_option ( 'serial_speed' ));
2015-11-02 01:35:28 +01:00
$line -> append_widget ( $serial_test );
2015-01-04 19:14:54 +01:00
$optgroup -> append_line ( $line );
2015-01-03 23:25:55 +01:00
}
2014-12-28 01:30:05 +01:00
{
2014-12-29 12:49:32 +01:00
my $optgroup = $page -> new_optgroup ( 'OctoPrint upload' );
2014-12-28 18:49:13 +01:00
2015-01-08 21:34:51 +01:00
# append two buttons to the Host line
my $octoprint_host_browse = sub {
2014-12-28 18:49:13 +01:00
my ( $parent ) = @_ ;
my $btn = Wx::Button -> new ( $parent , - 1 , "Browse…" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT );
$btn -> SetFont ( $ Slic3r::GUI:: small_font );
if ( $ Slic3r::GUI:: have_button_icons ) {
2016-04-09 19:10:57 +02:00
$btn -> SetBitmap ( Wx::Bitmap -> new ( $ Slic3r:: var -> ( "zoom.png" ), wxBITMAP_TYPE_PNG ));
2014-12-28 18:49:13 +01:00
}
if ( ! eval "use Net::Bonjour; 1" ) {
$btn -> Disable ;
}
2015-01-08 21:34:51 +01:00
2014-12-28 18:49:13 +01:00
EVT_BUTTON ( $self , $btn , sub {
2016-06-03 09:36:23 +02:00
# look for devices
my $entries ;
{
my $res = Net::Bonjour -> new ( 'http' );
$res -> discover ;
$entries = [ $res -> entries ];
}
if ( @ { $entries }) {
my $dlg = Slic3r::GUI::BonjourBrowser -> new ( $self , $entries );
if ( $dlg -> ShowModal == wxID_OK ) {
my $value = $dlg -> GetValue . ":" . $dlg -> GetPort ;
$self -> { config } -> set ( 'octoprint_host' , $value );
$self -> update_dirty ;
$self -> _on_value_change ( 'octoprint_host' , $value );
$self -> reload_config ;
}
} else {
Wx::MessageDialog -> new ( $self , 'No Bonjour device found' , 'Device Browser' , wxOK | wxICON_INFORMATION ) -> ShowModal ;
2014-12-28 18:49:13 +01:00
}
});
2015-01-08 21:34:51 +01:00
return $btn ;
};
my $octoprint_host_test = sub {
my ( $parent ) = @_ ;
2015-11-02 01:35:28 +01:00
my $btn = $self -> { octoprint_host_test_btn } = Wx::Button -> new ( $parent , - 1 ,
"Test" , wxDefaultPosition , wxDefaultSize , wxBU_LEFT | wxBU_EXACTFIT );
2015-01-08 21:34:51 +01:00
$btn -> SetFont ( $ Slic3r::GUI:: small_font );
if ( $ Slic3r::GUI:: have_button_icons ) {
2016-04-09 19:10:57 +02:00
$btn -> SetBitmap ( Wx::Bitmap -> new ( $ Slic3r:: var -> ( "wrench.png" ), wxBITMAP_TYPE_PNG ));
2015-01-08 21:34:51 +01:00
}
EVT_BUTTON ( $self , $btn , sub {
my $ua = LWP::UserAgent -> new ;
$ua -> timeout ( 10 );
2015-01-12 11:56:54 -06:00
my $res = $ua -> get (
2015-01-08 21:34:51 +01:00
"http://" . $self -> { config } -> octoprint_host . "/api/version" ,
'X-Api-Key' => $self -> { config } -> octoprint_apikey ,
);
if ( $res -> is_success ) {
Slic3r::GUI:: show_info ( $self , "Connection to OctoPrint works correctly." , "Success!" );
} else {
Slic3r::GUI:: show_error ( $self ,
"I wasn't able to connect to OctoPrint (" . $res -> status_line . "). "
. "Check hostname and OctoPrint version (at least 1.1.0 is required)." );
}
});
return $btn ;
2014-12-28 18:49:13 +01:00
};
my $host_line = $optgroup -> create_single_option_line ( 'octoprint_host' );
2015-01-08 21:34:51 +01:00
$host_line -> append_widget ( $octoprint_host_browse );
$host_line -> append_widget ( $octoprint_host_test );
2014-12-28 18:49:13 +01:00
$optgroup -> append_line ( $host_line );
2014-12-28 01:30:05 +01:00
$optgroup -> append_single_option_line ( 'octoprint_apikey' );
}
2015-01-04 19:20:13 +01:00
{
my $optgroup = $page -> new_optgroup ( 'Firmware' );
$optgroup -> append_single_option_line ( 'gcode_flavor' );
}
2012-12-23 16:29:08 +01:00
{
2014-07-01 16:40:56 +02:00
my $optgroup = $page -> new_optgroup ( 'Advanced' );
2015-01-05 19:39:10 +01:00
$optgroup -> append_single_option_line ( 'use_relative_e_distances' );
2014-07-01 16:40:56 +02:00
$optgroup -> append_single_option_line ( 'use_firmware_retraction' );
2015-01-05 19:39:10 +01:00
$optgroup -> append_single_option_line ( 'use_volumetric_e' );
2014-11-24 18:22:39 +01:00
$optgroup -> append_single_option_line ( 'pressure_advance' );
2016-07-16 09:52:11 -05:00
$optgroup -> append_single_option_line ( 'variable_layer_height' );
2014-07-01 16:40:56 +02:00
}
}
{
my $page = $self -> add_options_page ( 'Custom G-code' , 'cog.png' );
{
my $optgroup = $page -> 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 = $page -> 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 );
}
{
2015-01-30 20:08:00 +01:00
my $optgroup = $page -> new_optgroup ( 'Before layer change G-code' ,
label_width => 0 ,
);
my $option = $optgroup -> get_option ( 'before_layer_gcode' );
$option -> full_width ( 1 );
$option -> height ( 150 );
$optgroup -> append_single_option_line ( $option );
}
{
my $optgroup = $page -> new_optgroup ( 'After layer change G-code' ,
2014-07-01 16:40:56 +02:00
label_width => 0 ,
);
my $option = $optgroup -> get_option ( 'layer_gcode' );
$option -> full_width ( 1 );
$option -> height ( 150 );
$optgroup -> append_single_option_line ( $option );
}
{
my $optgroup = $page -> new_optgroup ( 'Tool change G-code' ,
label_width => 0 ,
);
my $option = $optgroup -> get_option ( 'toolchange_gcode' );
$option -> full_width ( 1 );
$option -> height ( 150 );
$optgroup -> append_single_option_line ( $option );
}
}
2012-06-17 22:27:05 +02:00
2012-07-18 20:36:34 +02:00
$self -> { extruder_pages } = [] ;
2012-07-27 21:13:03 +02:00
$self -> _build_extruder_pages ;
2016-05-16 23:40:24 +02:00
$self -> _update_serial_ports if ( ! $params { no_controller });
2015-01-04 19:14:54 +01:00
}
sub _update_serial_ports {
my ( $self ) = @_ ;
$self -> get_field ( 'serial_port' ) -> set_values ([ wxTheApp -> scan_serial_ports ]);
2012-07-18 20:36:34 +02:00
}
2014-09-21 15:29:52 +02:00
sub _extruders_count_changed {
my ( $self , $extruders_count ) = @_ ;
$self -> { extruders_count } = $extruders_count ;
$self -> _build_extruder_pages ;
$self -> _on_value_change ( 'extruders_count' , $extruders_count );
}
2017-02-07 18:46:02 +01:00
sub _extruder_options { qw(nozzle_diameter min_layer_height max_layer_height extruder_offset retract_length retract_lift retract_lift_above retract_lift_below retract_speed retract_restart_extra retract_before_travel wipe
2013-03-09 20:05:43 +01:00
retract_layer_change retract_length_toolchange retract_restart_extra_toolchange) }
2012-07-18 20:36:34 +02:00
2012-07-27 21:13:03 +02:00
sub _build_extruder_pages {
2012-07-18 20:36:34 +02:00
my $self = shift ;
2014-04-19 16:38:28 +02:00
my $default_config = Slic3r::Config::Full -> new ;
foreach my $extruder_idx ( @ { $self -> { extruder_pages }} .. $self -> { extruders_count } - 1 ) {
# extend options
foreach my $opt_key ( $self -> _extruder_options ) {
my $values = $self -> { config } -> get ( $opt_key );
2014-05-20 23:37:17 +02:00
if ( ! defined $values ) {
$values = [ $default_config -> get_at ( $opt_key , 0 ) ];
} else {
# use last extruder's settings for the new one
my $last_value = $values -> [ - 1 ];
$values -> [ $extruder_idx ] // = $last_value ;
}
2014-04-19 16:38:28 +02:00
$self -> { config } -> set ( $opt_key , $values )
or die "Unable to extend $opt_key" ;
}
# build page
2014-07-01 16:40:56 +02:00
my $page = $self -> { extruder_pages }[ $extruder_idx ] = $self -> add_options_page ( "Extruder " . ( $extruder_idx + 1 ), 'funnel.png' );
{
my $optgroup = $page -> new_optgroup ( 'Size' );
$optgroup -> append_single_option_line ( 'nozzle_diameter' , $extruder_idx );
}
2017-02-07 18:46:02 +01:00
{
2017-02-09 16:21:48 +01:00
my $optgroup = $page -> new_optgroup ( 'Layer height limits' );
2017-02-07 18:46:02 +01:00
$optgroup -> append_single_option_line ( $_ , $extruder_idx )
for qw(min_layer_height max_layer_height) ;
}
2014-07-01 16:40:56 +02:00
{
my $optgroup = $page -> new_optgroup ( 'Position (for multi-extruder printers)' );
$optgroup -> append_single_option_line ( 'extruder_offset' , $extruder_idx );
}
{
my $optgroup = $page -> new_optgroup ( 'Retraction' );
$optgroup -> append_single_option_line ( $_ , $extruder_idx )
2015-12-18 18:36:39 +01:00
for qw(retract_length retract_lift) ;
{
my $line = Slic3r::GUI::OptionsGroup::Line -> new (
label => 'Only lift Z' ,
);
$line -> append_option ( $optgroup -> get_option ( 'retract_lift_above' , $extruder_idx ));
$line -> append_option ( $optgroup -> get_option ( 'retract_lift_below' , $extruder_idx ));
$optgroup -> append_line ( $line );
}
$optgroup -> append_single_option_line ( $_ , $extruder_idx )
for qw(retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe) ;
2014-07-01 16:40:56 +02:00
}
{
my $optgroup = $page -> new_optgroup ( 'Retraction when tool is disabled (advanced settings for multi-extruder setups)' );
$optgroup -> append_single_option_line ( $_ , $extruder_idx )
for qw(retract_length_toolchange retract_restart_extra_toolchange) ;
}
2012-07-18 20:36:34 +02:00
}
2014-04-19 16:38:28 +02:00
# remove extra pages
if ( $self -> { extruders_count } <= $# { $self -> { extruder_pages }}) {
2015-01-25 11:43:34 +01:00
$_ -> Destroy for @ { $self -> { extruder_pages }}[ $self -> { extruders_count } .. $# { $self -> { extruder_pages }}];
2014-04-19 16:38:28 +02:00
splice @ { $self -> { extruder_pages }}, $self -> { extruders_count };
}
# remove extra config values
foreach my $opt_key ( $self -> _extruder_options ) {
my $values = $self -> { config } -> get ( $opt_key );
splice @$values , $self -> { extruders_count } if $self -> { extruders_count } <= $#$values ;
$self -> { config } -> set ( $opt_key , $values )
or die "Unable to truncate $opt_key" ;
}
2012-07-18 20:36:34 +02:00
# rebuild page list
@ { $self -> { pages }} = (
( grep $_ -> { title } !~ /^Extruder \d+/ , @ { $self -> { pages }}),
2012-07-24 15:30:50 +02:00
@ { $self -> { extruder_pages }}[ 0 .. $self -> { extruders_count } - 1 ],
2012-07-18 20:36:34 +02:00
);
2015-01-25 11:43:34 +01:00
$self -> update_tree ;
2012-07-18 20:36:34 +02:00
}
2017-02-07 18:41:09 +01:00
# Slic3r::GUI::Tab::Printer::_update is called after a configuration preset is loaded or switched, or when a single option is modifed by the user.
2014-07-01 18:18:23 +02:00
sub _update {
2017-02-07 18:41:09 +01:00
# $keys_modified is a reference to hash with modified keys set to 1, unmodified keys missing.
my ( $self , $keys_modified ) = @_ ;
2014-07-01 18:18:23 +02:00
my $config = $self -> { config };
2016-05-16 23:40:24 +02:00
my $serial_speed = $self -> get_field ( 'serial_speed' );
if ( $serial_speed ) {
$self -> get_field ( 'serial_speed' ) -> toggle ( $config -> get ( 'serial_port' ));
if ( $config -> get ( 'serial_speed' ) && $config -> get ( 'serial_port' )) {
$self -> { serial_test_btn } -> Enable ;
} else {
$self -> { serial_test_btn } -> Disable ;
}
2015-11-02 01:35:28 +01:00
}
2015-01-08 21:37:00 +01:00
if ( $config -> get ( 'octoprint_host' ) && eval "use LWP::UserAgent; 1" ) {
2015-01-08 21:34:51 +01:00
$self -> { octoprint_host_test_btn } -> Enable ;
} else {
$self -> { octoprint_host_test_btn } -> Disable ;
}
2014-12-28 01:30:05 +01:00
$self -> get_field ( 'octoprint_apikey' ) -> toggle ( $config -> get ( 'octoprint_host' ));
2014-12-23 20:47:11 +01:00
my $have_multiple_extruders = $self -> { extruders_count } > 1 ;
$self -> get_field ( 'toolchange_gcode' ) -> toggle ( $have_multiple_extruders );
2014-07-01 18:18:23 +02:00
for my $i ( 0 .. ( $self -> { extruders_count } - 1 )) {
my $have_retract_length = $config -> get_at ( 'retract_length' , $i ) > 0 ;
# when using firmware retraction, firmware decides retraction length
$self -> get_field ( 'retract_length' , $i ) -> toggle ( ! $config -> use_firmware_retraction );
# user can customize travel length if we have retraction length or we're using
# firmware retraction
$self -> get_field ( 'retract_before_travel' , $i ) -> toggle ( $have_retract_length || $config -> use_firmware_retraction );
# user can customize other retraction options if retraction is enabled
my $retraction = ( $have_retract_length || $config -> use_firmware_retraction );
$self -> get_field ( $_ , $i ) -> toggle ( $retraction )
for qw(retract_lift retract_layer_change) ;
2015-12-18 18:36:39 +01:00
# retract lift above/below only applies if using retract lift
$self -> get_field ( $_ , $i ) -> toggle ( $retraction && $config -> get_at ( 'retract_lift' , $i ) > 0 )
for qw(retract_lift_above retract_lift_below) ;
2014-07-01 18:18:23 +02:00
# some options only apply when not using firmware retraction
$self -> get_field ( $_ , $i ) -> toggle ( $retraction && ! $config -> use_firmware_retraction )
for qw(retract_speed retract_restart_extra wipe) ;
2015-11-04 19:43:23 +01:00
if ( $config -> use_firmware_retraction && $config -> get_at ( 'wipe' , $i )) {
my $dialog = Wx::MessageDialog -> new ( $self ,
"The Wipe option is not available when using the Firmware Retraction mode.\n"
. "\nShall I disable it in order to enable Firmware Retraction?" ,
'Firmware Retraction' , wxICON_WARNING | wxYES | wxNO );
my $new_conf = Slic3r::Config -> new ;
if ( $dialog -> ShowModal () == wxID_YES ) {
my $wipe = $config -> wipe ;
$wipe -> [ $i ] = 0 ;
$new_conf -> set ( "wipe" , $wipe );
} else {
$new_conf -> set ( "use_firmware_retraction" , 0 );
}
$self -> load_config ( $new_conf );
}
2014-07-01 18:18:23 +02:00
2014-12-23 20:47:11 +01:00
$self -> get_field ( 'retract_length_toolchange' , $i ) -> toggle ( $have_multiple_extruders );
2014-07-01 18:18:23 +02:00
my $toolchange_retraction = $config -> get_at ( 'retract_length_toolchange' , $i ) > 0 ;
2014-12-23 20:47:11 +01:00
$self -> get_field ( 'retract_restart_extra_toolchange' , $i ) -> toggle
( $have_multiple_extruders && $toolchange_retraction );
2014-07-01 18:18:23 +02:00
}
}
2012-07-27 21:13:03 +02:00
# this gets executed after preset is loaded and before GUI fields are updated
2012-07-18 20:36:34 +02:00
sub on_preset_loaded {
my $self = shift ;
2014-09-21 15:29:52 +02:00
2012-07-18 20:36:34 +02:00
# update the extruders count field
2012-07-20 14:41:27 +02:00
{
2012-07-24 15:30:50 +02:00
# update the GUI field according to the number of nozzle diameters supplied
2014-09-21 15:29:52 +02:00
my $extruders_count = scalar @ { $self -> { config } -> nozzle_diameter };
$self -> set_value ( 'extruders_count' , $extruders_count );
$self -> _extruders_count_changed ( $extruders_count );
2012-07-18 20:36:34 +02:00
}
2012-06-17 22:27:05 +02:00
}
2013-03-09 16:27:18 +01:00
sub load_config_file {
2012-08-07 20:14:28 +02:00
my $self = shift ;
2013-03-09 16:27:18 +01:00
$self -> SUPER:: load_config_file ( @_ );
2012-08-07 20:14:28 +02:00
Slic3r::GUI:: warning_catcher ( $self ) -> (
"Your configuration was imported. However, Slic3r is currently only able to import settings "
. "for the first defined filament. We recommend you don't use exported configuration files "
. "for multi-extruder setups and rely on the built-in preset management system instead." )
if @ { $self -> { config } -> nozzle_diameter } > 1 ;
}
2012-06-17 22:27:05 +02:00
package Slic3r::GUI::Tab::Page ;
2015-05-28 18:56:35 +02:00
use Wx qw(wxTheApp :misc :panel :sizer) ;
2012-06-18 11:26:21 +02:00
use base 'Wx::ScrolledWindow' ;
2012-06-17 22:27:05 +02:00
sub new {
my $class = shift ;
2014-07-01 16:40:56 +02:00
my ( $parent , $title , $iconID ) = @_ ;
2012-07-24 12:42:58 +02:00
my $self = $class -> SUPER:: new ( $parent , - 1 , wxDefaultPosition , wxDefaultSize , wxTAB_TRAVERSAL );
2012-07-24 15:30:50 +02:00
$self -> { optgroups } = [] ;
2012-07-18 20:36:34 +02:00
$self -> { title } = $title ;
$self -> { iconID } = $iconID ;
2012-06-17 22:27:05 +02:00
2012-06-18 11:26:21 +02:00
$self -> SetScrollbars ( 1 , 1 , 1 , 1 );
2013-03-09 14:55:07 +01:00
$self -> { vsizer } = Wx::BoxSizer -> new ( wxVERTICAL );
$self -> SetSizer ( $self -> { vsizer });
2012-06-17 22:27:05 +02:00
return $self ;
}
2014-07-01 16:40:56 +02:00
sub new_optgroup {
my ( $self , $title , %params ) = @_ ;
2012-06-17 22:27:05 +02:00
2014-07-01 16:40:56 +02:00
my $optgroup = Slic3r::GUI::ConfigOptionsGroup -> new (
parent => $self ,
title => $title ,
config => $self -> GetParent -> { config },
label_width => $params { label_width } // 200 ,
2014-09-23 20:00:51 +02:00
on_change => sub {
2015-05-28 18:56:35 +02:00
my ( $opt_key , $value ) = @_ ;
wxTheApp -> CallAfter ( sub {
$self -> GetParent -> update_dirty ;
$self -> GetParent -> _on_value_change ( $opt_key , $value );
});
2014-09-23 20:00:51 +02:00
},
2012-07-27 21:13:03 +02:00
);
2014-07-01 16:40:56 +02:00
2012-07-24 15:30:50 +02:00
push @ { $self -> { optgroups }}, $optgroup ;
2014-07-01 16:40:56 +02:00
$self -> { vsizer } -> Add ( $optgroup -> sizer , 0 , wxEXPAND | wxALL , 10 );
return $optgroup ;
}
sub reload_config {
my ( $self ) = @_ ;
$_ -> reload_config for @ { $self -> { optgroups }};
2012-07-24 15:30:50 +02:00
}
2014-07-01 18:18:23 +02:00
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 ;
}
2012-07-24 15:30:50 +02:00
sub set_value {
my $self = shift ;
my ( $opt_key , $value ) = @_ ;
my $changed = 0 ;
foreach my $optgroup ( @ { $self -> { optgroups }}) {
$changed = 1 if $optgroup -> set_value ( $opt_key , $value );
}
return $changed ;
2012-06-17 22:27:05 +02:00
}
2012-06-18 22:27:57 +02:00
package Slic3r::GUI::SavePresetWindow ;
2012-07-03 01:20:30 +02:00
use Wx qw(:combobox :dialog :id :misc :sizer) ;
2012-06-21 13:01:59 +02:00
use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER) ;
2012-06-18 22:27:57 +02:00
use base 'Wx::Dialog' ;
sub new {
my $class = shift ;
my ( $parent , %params ) = @_ ;
2012-07-03 01:20:30 +02:00
my $self = $class -> SUPER:: new ( $parent , - 1 , "Save preset" , wxDefaultPosition , wxDefaultSize );
2012-06-18 22:27:57 +02:00
2014-12-25 17:35:31 +01:00
my @values = @ { $params { values }};
2014-03-23 21:40:45 +01:00
2012-07-03 01:20:30 +02:00
my $text = Wx::StaticText -> new ( $self , - 1 , "Save " . lc ( $params { title }) . " as:" , wxDefaultPosition , wxDefaultSize );
2014-03-23 21:40:45 +01:00
$self -> { combo } = Wx::ComboBox -> new ( $self , - 1 , $params { default }, wxDefaultPosition , wxDefaultSize , \ @values ,
2012-07-01 19:24:06 +02:00
wxTE_PROCESS_ENTER );
my $buttons = $self -> CreateStdDialogButtonSizer ( wxOK | wxCANCEL );
2012-06-18 22:27:57 +02:00
my $sizer = Wx::BoxSizer -> new ( wxVERTICAL );
$sizer -> Add ( $text , 0 , wxEXPAND | wxTOP | wxLEFT | wxRIGHT , 10 );
2012-06-21 13:01:59 +02:00
$sizer -> Add ( $self -> { combo }, 0 , wxEXPAND | wxLEFT | wxRIGHT , 10 );
2012-06-18 22:27:57 +02:00
$sizer -> Add ( $buttons , 0 , wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT , 10 );
2012-07-01 19:24:06 +02:00
EVT_BUTTON ( $self , wxID_OK , \& accept );
2012-06-21 13:01:59 +02:00
EVT_TEXT_ENTER ( $self , $self -> { combo }, \& accept );
2012-06-18 22:27:57 +02:00
$self -> SetSizer ( $sizer );
$sizer -> SetSizeHints ( $self );
return $self ;
}
2012-06-21 13:01:59 +02:00
sub accept {
my ( $self , $event ) = @_ ;
2012-07-15 18:55:01 +02:00
if (( $self -> { chosen_name } = $self -> { combo } -> GetValue )) {
2014-03-23 21:40:45 +01:00
if ( $self -> { chosen_name } !~ /^[^<>:\/\\|?*\"]+$/i ) {
2012-08-06 08:23:00 -04:00
Slic3r::GUI:: show_error ( $self , "The supplied name is not valid; the following characters are not allowed: <>:/\|?*\"" );
2014-03-23 21:40:45 +01:00
} elsif ( $self -> { chosen_name } eq '- default -' ) {
Slic3r::GUI:: show_error ( $self , "The supplied name is not available." );
} else {
$self -> EndModal ( wxID_OK );
2012-07-15 18:55:01 +02:00
}
2012-06-21 13:01:59 +02:00
}
}
2012-06-18 22:27:57 +02:00
sub get_name {
my $self = shift ;
return $self -> { chosen_name };
}
2014-12-25 17:35:31 +01:00
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 ) {
2016-10-24 16:07:36 +02:00
# Perl class Slic3r::Config extends the C++ class Slic3r::DynamicPrintConfig
2014-12-25 17:35:31 +01:00
return Slic3r::Config -> new_from_defaults ( @$keys );
} else {
2015-06-02 16:10:15 +02:00
if ( !- e Slic3r:: encode_path ( $self -> file )) {
2015-01-13 23:19:19 +01:00
Slic3r::GUI:: show_error ( undef , "The selected preset does not exist anymore (" . $self -> file . ")." );
return undef ;
2014-12-25 17:35:31 +01:00
}
# apply preset values on top of defaults
2014-12-28 01:30:05 +01:00
my $config = Slic3r::Config -> new_from_defaults ( @$keys );
2014-12-25 17:35:31 +01:00
my $external_config = Slic3r::Config -> load ( $self -> file );
$config -> set ( $_ , $external_config -> get ( $_ ))
for grep $external_config -> has ( $_ ), @$keys ;
return $config ;
}
}
2012-06-17 22:27:05 +02:00
1 ;