2016-09-14 11:22:41 +02:00
# The main frame, the parent of all.
2014-06-14 19:11:04 +02:00
package Slic3r::GUI::MainFrame ;
use strict ;
use warnings ;
use utf8 ;
2014-06-27 17:21:41 +02:00
use File::Basename qw(basename dirname) ;
2014-06-14 19:54:18 +02:00
use List::Util qw(min) ;
2014-06-14 20:52:21 +02:00
use Slic3r::Geometry qw(X Y Z) ;
2014-06-14 19:54:18 +02:00
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
2014-06-14 19:59:59 +02:00
:font :icon wxTheApp) ;
2015-11-03 21:55:17 +01:00
use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED) ;
2014-06-14 19:11:04 +02:00
use base 'Wx::Frame' ;
2015-06-02 22:27:11 +02:00
our $qs_last_input_file ;
our $qs_last_output_file ;
2014-06-14 19:54:18 +02:00
our $last_config ;
2014-06-14 19:11:04 +02:00
sub new {
my ( $class , %params ) = @_ ;
2016-10-21 16:53:42 +02:00
my $self = $class -> SUPER:: new ( undef , - 1 , $ Slic3r:: FORK_NAME . ' - ' . $ Slic3r:: VERSION , wxDefaultPosition , wxDefaultSize , wxDEFAULT_FRAME_STYLE );
2016-10-24 18:05:26 +02:00
if ( $^O eq 'MSWin32' ) {
2016-11-20 14:22:26 +01:00
# Load the icon either from the exe, or fron the ico file.
my $iconfile = $ Slic3r:: var -> ( '..\slic3r.exe' );
$iconfile = $ Slic3r:: var -> ( "Slic3r.ico" ) unless - f $iconfile ;
$self -> SetIcon ( Wx::Icon -> new ( $iconfile , wxBITMAP_TYPE_ICO ));
2016-10-24 18:05:26 +02:00
} else {
$self -> SetIcon ( Wx::Icon -> new ( $ Slic3r:: var -> ( "Slic3r_128px.png" ), wxBITMAP_TYPE_PNG ));
}
2014-06-14 19:11:04 +02:00
2014-06-14 19:54:18 +02:00
# store input params
$self -> { mode } = $params { mode };
$self -> { mode } = 'expert' if $self -> { mode } !~ /^(?:simple|expert)$/ ;
2016-05-16 23:40:24 +02:00
# 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 };
2014-06-14 19:54:18 +02:00
$self -> { no_plater } = $params { no_plater };
$self -> { loaded } = 0 ;
# initialize tabpanel and menubar
$self -> _init_tabpanel ;
$self -> _init_menubar ;
2016-11-30 11:45:31 +01:00
# set default tooltip timer in msec
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
# (SetAutoPop is not available on GTK.)
eval { Wx::ToolTip:: SetAutoPop ( 32767 ) };
2014-06-14 19:54:18 +02:00
# initialize status bar
2014-06-14 19:11:04 +02:00
$self -> { statusbar } = Slic3r::GUI::ProgressStatusBar -> new ( $self , - 1 );
2016-10-21 16:53:42 +02:00
$self -> { statusbar } -> SetStatusText ( "Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases" );
2014-06-14 19:11:04 +02:00
$self -> SetStatusBar ( $self -> { statusbar });
2014-06-14 19:54:18 +02:00
$self -> { loaded } = 1 ;
# initialize layout
{
my $sizer = Wx::BoxSizer -> new ( wxVERTICAL );
$sizer -> Add ( $self -> { tabpanel }, 1 , wxEXPAND );
$sizer -> SetSizeHints ( $self );
$self -> SetSizer ( $sizer );
$self -> Fit ;
2014-07-04 10:32:32 +02:00
$self -> SetMinSize ([ 760 , 490 ]);
2014-12-08 22:05:26 +01:00
if ( defined $ Slic3r::GUI:: Settings -> { _ }{ main_frame_size }) {
2015-01-30 13:21:07 +01:00
my $size = [ split ',' , $ Slic3r::GUI:: Settings -> { _ }{ main_frame_size }, 2 ];
$self -> SetSize ( $size );
my $display = Wx::Display -> new -> GetClientArea ();
my $pos = [ split ',' , $ Slic3r::GUI:: Settings -> { _ }{ main_frame_pos }, 2 ];
if (( $pos -> [ X ] + $size -> [ X ] /2) < $display->GetRight && ($pos->[Y] + $size->[Y]/ 2 ) < $display -> GetBottom ) {
$self -> Move ( $pos );
}
2014-12-08 22:05:26 +01:00
$self -> Maximize ( 1 ) if $ Slic3r::GUI:: Settings -> { _ }{ main_frame_maximized };
} else {
$self -> SetSize ( $self -> GetMinSize );
}
2014-06-14 19:54:18 +02:00
$self -> Show ;
$self -> Layout ;
}
2014-12-08 22:05:26 +01:00
# declare events
EVT_CLOSE ( $self , sub {
my ( undef , $event ) = @_ ;
if ( $event -> CanVeto && ! $self -> check_unsaved_changes ) {
$event -> Veto ;
return ;
}
# save window size
$ Slic3r::GUI:: Settings -> { _ }{ main_frame_pos } = join ',' , $self -> GetScreenPositionXY ;
$ Slic3r::GUI:: Settings -> { _ }{ main_frame_size } = join ',' , $self -> GetSizeWH ;
$ Slic3r::GUI:: Settings -> { _ }{ main_frame_maximized } = $self -> IsMaximized ;
wxTheApp -> save_settings ;
# propagate event
$event -> Skip ;
});
2016-10-25 13:24:42 +02:00
$self -> update_ui_from_settings ;
2014-12-08 22:05:26 +01:00
2014-06-14 19:54:18 +02:00
return $self ;
}
sub _init_tabpanel {
my ( $self ) = @_ ;
$self -> { tabpanel } = my $panel = Wx::Notebook -> new ( $self , - 1 , wxDefaultPosition , wxDefaultSize , wxNB_TOP | wxTAB_TRAVERSAL );
2015-11-03 21:55:17 +01:00
EVT_NOTEBOOK_PAGE_CHANGED ( $self , $self -> { tabpanel }, sub {
my $panel = $self -> { tabpanel } -> GetCurrentPage ;
$panel -> OnActivate if $panel -> can ( 'OnActivate' );
});
2014-06-14 19:54:18 +02:00
2014-07-01 16:40:56 +02:00
if ( ! $self -> { no_plater }) {
$panel -> AddPage ( $self -> { plater } = Slic3r::GUI::Plater -> new ( $panel ), "Plater" );
2016-05-16 23:40:24 +02:00
if ( ! $self -> { no_controller }) {
$panel -> AddPage ( $self -> { controller } = Slic3r::GUI::Controller -> new ( $panel ), "Controller" );
}
2014-07-01 16:40:56 +02:00
}
2014-06-14 19:54:18 +02:00
$self -> { options_tabs } = {};
my $simple_config ;
if ( $self -> { mode } eq 'simple' ) {
$simple_config = Slic3r::Config -> load ( "$Slic3r::GUI::datadir/simple.ini" )
2014-11-22 21:55:45 +01:00
if - e Slic3r:: encode_path ( "$Slic3r::GUI::datadir/simple.ini" );
2014-06-14 19:54:18 +02:00
}
my $class_prefix = $self -> { mode } eq 'simple' ? "Slic3r::GUI::SimpleTab::" : "Slic3r::GUI::Tab::" ;
for my $tab_name ( qw(print filament printer) ) {
my $tab ;
2016-05-16 23:40:24 +02:00
$tab = $self -> { options_tabs }{ $tab_name } = ( $class_prefix . ucfirst $tab_name ) -> new (
$panel ,
no_controller => $self -> { no_controller });
2017-02-07 18:28:53 +01:00
# Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value.
2014-07-01 16:40:56 +02:00
$tab -> on_value_change ( sub {
2017-05-31 17:05:11 +02:00
my ( $opt_key , $value ) = @_ ;
2014-07-01 16:40:56 +02:00
my $config = $tab -> config ;
2014-09-21 15:29:52 +02:00
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' ;
}
2014-07-01 16:40:56 +02:00
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 ;
2014-06-14 19:54:18 +02:00
}
2014-07-01 16:40:56 +02:00
$self -> config -> save ( $ Slic3r::GUI:: autosave ) if $ Slic3r::GUI:: autosave ;
}
});
2016-10-24 16:07:36 +02:00
# Install a callback for the tab to update the platter and print controller presets, when
# a preset changes at Slic3r::GUI::Tab.
2014-07-01 16:40:56 +02:00
$tab -> on_presets_changed ( sub {
if ( $self -> { plater }) {
2017-02-07 18:28:53 +01:00
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
2014-07-01 16:40:56 +02:00
$self -> { plater } -> update_presets ( $tab_name , @_ );
$self -> { plater } -> on_config_change ( $tab -> config );
2016-05-16 23:40:24 +02:00
if ( $self -> { controller }) {
$self -> { controller } -> update_presets ( $tab_name , @_ );
}
2014-07-01 16:40:56 +02:00
}
});
$tab -> load_presets ;
2014-06-14 19:54:18 +02:00
$panel -> AddPage ( $tab , $tab -> title );
$tab -> load_config ( $simple_config ) if $simple_config ;
}
2014-07-01 16:40:56 +02:00
if ( $self -> { plater }) {
$self -> { plater } -> on_select_preset ( sub {
2015-05-26 02:01:43 +02:00
my ( $group , $i ) = @_ ;
$self -> { options_tabs }{ $group } -> select_preset ( $i );
2014-07-01 16:40:56 +02:00
});
# load initial config
$self -> { plater } -> on_config_change ( $self -> config );
}
2014-06-14 19:54:18 +02:00
}
sub _init_menubar {
my ( $self ) = @_ ;
2014-06-14 19:11:04 +02:00
# File menu
my $fileMenu = Wx::Menu -> new ;
{
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "&Load Config…\tCtrl+L" , 'Load exported configuration file' , sub {
$self -> load_config_file ;
2015-05-25 22:37:04 +02:00
}, undef , 'plugin_add.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "&Export Config…\tCtrl+E" , 'Export current configuration to file' , sub {
$self -> export_config ;
2015-05-25 22:37:04 +02:00
}, undef , 'plugin_go.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "&Load Config Bundle…" , 'Load presets from a bundle' , sub {
$self -> load_configbundle ;
2015-05-25 22:37:04 +02:00
}, undef , 'lorry_add.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "&Export Config Bundle…" , 'Export all presets to file' , sub {
$self -> export_configbundle ;
2015-05-25 22:37:04 +02:00
}, undef , 'lorry_go.png' );
2014-06-14 19:11:04 +02:00
$fileMenu -> AppendSeparator ();
2014-06-14 20:26:46 +02:00
my $repeat ;
$self -> _append_menu_item ( $fileMenu , "Q&uick Slice…\tCtrl+U" , 'Slice file' , sub {
2015-05-06 00:39:16 +02:00
wxTheApp -> CallAfter ( sub {
$self -> quick_slice ;
$repeat -> Enable ( defined $ Slic3r::GUI::MainFrame:: last_input_file );
});
2015-05-25 22:37:04 +02:00
}, undef , 'cog_go.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "Quick Slice and Save &As…\tCtrl+Alt+U" , 'Slice file and save as' , sub {
2015-05-06 00:39:16 +02:00
wxTheApp -> CallAfter ( sub {
$self -> quick_slice ( save_as => 1 );
$repeat -> Enable ( defined $ Slic3r::GUI::MainFrame:: last_input_file );
});
2015-05-25 22:37:04 +02:00
}, undef , 'cog_go.png' );
2014-06-14 20:26:46 +02:00
$repeat = $self -> _append_menu_item ( $fileMenu , "&Repeat Last Quick Slice\tCtrl+Shift+U" , 'Repeat last quick slice' , sub {
2015-05-06 00:39:16 +02:00
wxTheApp -> CallAfter ( sub {
$self -> quick_slice ( reslice => 1 );
});
2015-05-25 22:37:04 +02:00
}, undef , 'cog_go.png' );
2014-06-14 19:11:04 +02:00
$repeat -> Enable ( 0 );
$fileMenu -> AppendSeparator ();
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "Slice to SV&G…\tCtrl+G" , 'Slice file to SVG' , sub {
$self -> quick_slice ( save_as => 1 , export_svg => 1 );
2015-05-25 22:37:04 +02:00
}, undef , 'shape_handles.png' );
2016-10-25 13:24:42 +02:00
$self -> { menu_item_reslice_now } = $self -> _append_menu_item (
$fileMenu , "(&Re)Slice Now\tCtrl+S" , 'Start new slicing process' ,
sub { $self -> reslice_now ; }, undef , 'shape_handles.png' );
2014-06-14 19:11:04 +02:00
$fileMenu -> AppendSeparator ();
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "Repair STL file…" , 'Automatically repair an STL file' , sub {
$self -> repair_stl ;
2015-05-25 22:37:04 +02:00
}, undef , 'wrench.png' );
2014-06-14 19:11:04 +02:00
$fileMenu -> AppendSeparator ();
2015-05-31 22:35:52 +02:00
# Cmd+, is standard on OS X - what about other operating systems?
2015-05-31 22:33:46 +02:00
$self -> _append_menu_item ( $fileMenu , "Preferences…\tCtrl+," , 'Application preferences' , sub {
2014-06-14 20:26:46 +02:00
Slic3r::GUI::Preferences -> new ( $self ) -> ShowModal ;
2014-07-01 16:40:56 +02:00
}, wxID_PREFERENCES );
2014-06-14 19:11:04 +02:00
$fileMenu -> AppendSeparator ();
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $fileMenu , "&Quit" , 'Quit Slic3r' , sub {
$self -> Close ( 0 );
2014-07-01 16:40:56 +02:00
}, wxID_EXIT );
2014-06-14 19:11:04 +02:00
}
# Plater menu
2014-06-14 19:54:18 +02:00
unless ( $self -> { no_plater }) {
my $plater = $self -> { plater };
2014-06-14 19:11:04 +02:00
$self -> { plater_menu } = Wx::Menu -> new ;
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $self -> { plater_menu }, "Export G-code..." , 'Export current plate as G-code' , sub {
$plater -> export_gcode ;
2015-05-25 22:37:04 +02:00
}, undef , 'cog_go.png' );
2015-01-03 15:48:53 +01:00
$self -> _append_menu_item ( $self -> { plater_menu }, "Export plate as STL..." , 'Export current plate as STL' , sub {
2014-06-14 20:26:46 +02:00
$plater -> export_stl ;
2015-05-25 22:37:04 +02:00
}, undef , 'brick_go.png' );
2015-01-03 15:48:53 +01:00
$self -> _append_menu_item ( $self -> { plater_menu }, "Export plate as AMF..." , 'Export current plate as AMF' , sub {
2014-06-14 20:26:46 +02:00
$plater -> export_amf ;
2015-05-25 22:37:04 +02:00
}, undef , 'brick_go.png' );
2015-11-15 21:08:14 +01:00
$self -> _append_menu_item ( $self -> { plater_menu }, "Open DLP Projector…\tCtrl+L" , 'Open projector window for DLP printing' , sub {
my $projector = Slic3r::GUI::Projector -> new ( $self );
# this double invocation is needed for properly hiding the MainFrame
$projector -> Show ;
$projector -> ShowModal ;
}, undef , 'film.png' );
2014-06-14 19:11:04 +02:00
2014-06-14 21:36:28 +02:00
$self -> { object_menu } = $self -> { plater } -> object_menu ;
2014-06-14 19:11:04 +02:00
$self -> on_plater_selection_changed ( 0 );
}
# Window menu
my $windowMenu = Wx::Menu -> new ;
{
2015-01-04 23:18:23 +01:00
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 );
2015-05-28 18:05:36 +02:00
}, undef , 'application_view_tile.png' );
2017-03-23 18:02:35 +01:00
$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 ) {
2015-01-04 23:18:23 +01:00
$windowMenu -> AppendSeparator ();
}
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $windowMenu , "Select P&rint Settings Tab\tCtrl+2" , 'Show the print settings' , sub {
2015-01-04 23:18:23 +01:00
$self -> select_tab ( $tab_offset + 0 );
2015-05-25 22:37:04 +02:00
}, undef , 'cog.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $windowMenu , "Select &Filament Settings Tab\tCtrl+3" , 'Show the filament settings' , sub {
2015-01-04 23:18:23 +01:00
$self -> select_tab ( $tab_offset + 1 );
2015-05-25 22:37:04 +02:00
}, undef , 'spool.png' );
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $windowMenu , "Select Print&er Settings Tab\tCtrl+4" , 'Show the printer settings' , sub {
2015-01-04 23:18:23 +01:00
$self -> select_tab ( $tab_offset + 2 );
2015-05-25 22:37:04 +02:00
}, undef , 'printer_empty.png' );
2014-06-14 19:11:04 +02:00
}
2016-10-05 14:13:07 +02:00
# View menu
if ( ! $self -> { no_plater }) {
$self -> { viewMenu } = Wx::Menu -> new ;
2016-11-27 16:48:48 +01:00
$self -> _append_menu_item ( $self -> { viewMenu }, "Iso" , 'Iso View' , sub { $self -> select_view ( 'iso' ); });
2016-10-05 14:13:07 +02:00
$self -> _append_menu_item ( $self -> { viewMenu }, "Top" , 'Top View' , sub { $self -> select_view ( 'top' ); });
$self -> _append_menu_item ( $self -> { viewMenu }, "Bottom" , 'Bottom View' , sub { $self -> select_view ( 'bottom' ); });
$self -> _append_menu_item ( $self -> { viewMenu }, "Front" , 'Front View' , sub { $self -> select_view ( 'front' ); });
$self -> _append_menu_item ( $self -> { viewMenu }, "Rear" , 'Rear View' , sub { $self -> select_view ( 'rear' ); });
$self -> _append_menu_item ( $self -> { viewMenu }, "Left" , 'Left View' , sub { $self -> select_view ( 'left' ); });
$self -> _append_menu_item ( $self -> { viewMenu }, "Right" , 'Right View' , sub { $self -> select_view ( 'right' ); });
}
2014-06-14 19:11:04 +02:00
# Help menu
my $helpMenu = Wx::Menu -> new ;
{
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $helpMenu , "&Configuration $Slic3r::GUI::ConfigWizard::wizard…" , "Run Configuration $Slic3r::GUI::ConfigWizard::wizard" , sub {
$self -> config_wizard ;
});
2014-06-14 19:11:04 +02:00
$helpMenu -> AppendSeparator ();
2016-10-21 16:53:42 +02:00
$self -> _append_menu_item ( $helpMenu , "Prusa 3D Drivers" , 'Open the Prusa3D drivers download page in your browser' , sub {
Wx:: LaunchDefaultBrowser ( 'http://www.prusa3d.com/drivers/' );
});
$self -> _append_menu_item ( $helpMenu , "Prusa Edition Releases" , 'Open the Prusa Edition releases page in your browser' , sub {
Wx:: LaunchDefaultBrowser ( 'http://github.com/prusa3d/slic3r/releases' );
});
# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
# wxTheApp->check_version(1);
# });
# $versioncheck->Enable(wxTheApp->have_version_check);
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $helpMenu , "Slic3r &Website" , 'Open the Slic3r website in your browser' , sub {
Wx:: LaunchDefaultBrowser ( 'http://slic3r.org/' );
});
$self -> _append_menu_item ( $helpMenu , "Slic3r &Manual" , 'Open the Slic3r manual in your browser' , sub {
Wx:: LaunchDefaultBrowser ( 'http://manual.slic3r.org/' );
});
2014-06-14 19:11:04 +02:00
$helpMenu -> AppendSeparator ();
2017-02-19 16:04:57 +01:00
$self -> _append_menu_item ( $helpMenu , "System Info" , 'Show system information' , sub {
wxTheApp -> system_info ;
});
$self -> _append_menu_item ( $helpMenu , "Report an Issue" , 'Report an issue on the Slic3r Prusa Edition' , sub {
Wx:: LaunchDefaultBrowser ( 'http://github.com/prusa3d/slic3r/issues/new' );
});
2014-06-14 20:26:46 +02:00
$self -> _append_menu_item ( $helpMenu , "&About Slic3r" , 'Show about dialog' , sub {
wxTheApp -> about ;
});
2014-06-14 19:11:04 +02:00
}
2016-09-12 13:26:17 +02:00
2014-06-14 19:11:04 +02:00
# menubar
# assign menubar to frame after appending items, otherwise special items
# will not be handled correctly
{
my $menubar = Wx::MenuBar -> new ;
$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" );
2016-10-05 14:13:07 +02:00
$menubar -> Append ( $self -> { viewMenu }, "&View" ) if $self -> { viewMenu };
2014-06-14 19:11:04 +02:00
$menubar -> Append ( $helpMenu , "&Help" );
$self -> SetMenuBar ( $menubar );
}
2014-06-14 19:54:18 +02:00
}
sub is_loaded {
my ( $self ) = @_ ;
return $self -> { loaded };
2014-06-14 19:11:04 +02:00
}
sub on_plater_selection_changed {
my ( $self , $have_selection ) = @_ ;
return if ! defined $self -> { object_menu };
$self -> { object_menu } -> Enable ( $_ -> GetId , $have_selection )
for $self -> { object_menu } -> GetMenuItems ;
}
2014-06-14 19:54:18 +02:00
sub quick_slice {
my $self = shift ;
my %params = @_ ;
my $progress_dialog ;
eval {
# validate configuration
my $config = $self -> config ;
$config -> validate ;
# select input file
my $input_file ;
my $dir = $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || '' ;
if ( ! $params { reslice }) {
2017-04-05 14:45:43 +02:00
my $dialog = Wx::FileDialog -> new ( $self , 'Choose a file to slice (STL/OBJ/AMF/PRUSA):' , $dir , "" , & Slic3r::GUI:: MODEL_WILDCARD , wxFD_OPEN | wxFD_FILE_MUST_EXIST );
2014-06-14 19:54:18 +02:00
if ( $dialog -> ShowModal != wxID_OK ) {
$dialog -> Destroy ;
return ;
}
2015-03-09 15:17:50 +01:00
$input_file = Slic3r:: decode_path ( $dialog -> GetPaths );
2014-06-14 19:54:18 +02:00
$dialog -> Destroy ;
2015-06-02 22:27:11 +02:00
$qs_last_input_file = $input_file unless $params { export_svg };
2014-06-14 19:54:18 +02:00
} else {
2015-06-02 22:27:11 +02:00
if ( ! defined $qs_last_input_file ) {
2014-06-14 19:54:18 +02:00
Wx::MessageDialog -> new ( $self , "No previously sliced file." ,
'Error' , wxICON_ERROR | wxOK ) -> ShowModal ();
return ;
}
2015-06-02 22:27:11 +02:00
if ( ! - e $qs_last_input_file ) {
Wx::MessageDialog -> new ( $self , "Previously sliced file ($qs_last_input_file) not found." ,
2014-06-14 19:54:18 +02:00
'File Not Found' , wxICON_ERROR | wxOK ) -> ShowModal ();
return ;
}
2015-06-02 22:27:11 +02:00
$input_file = $qs_last_input_file ;
2014-06-14 19:54:18 +02:00
}
my $input_file_basename = basename ( $input_file );
$ Slic3r::GUI:: Settings -> { recent }{ skein_directory } = dirname ( $input_file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
2014-12-21 23:29:31 +01:00
my $print_center ;
{
my $bed_shape = Slic3r::Polygon -> new_scale ( @ { $config -> bed_shape });
$print_center = Slic3r::Pointf -> new_unscale ( @ { $bed_shape -> bounding_box -> center });
}
2014-06-14 19:54:18 +02:00
my $sprint = Slic3r::Print::Simple -> new (
2014-12-21 23:29:31 +01:00
print_center => $print_center ,
2014-06-14 19:54:18 +02:00
status_cb => sub {
my ( $percent , $message ) = @_ ;
2016-10-24 16:07:36 +02:00
return if & Wx:: wxVERSION_STRING !~ m " 2\.(8\.|9\.[2-9])" ;
2014-06-14 19:54:18 +02:00
$progress_dialog -> Update ( $percent , "$message…" );
},
);
2014-06-27 17:21:41 +02:00
# keep model around
my $model = Slic3r::Model -> read_from_file ( $input_file );
2014-06-14 19:54:18 +02:00
$sprint -> apply_config ( $config );
2014-06-27 17:21:41 +02:00
$sprint -> set_model ( $model );
2014-06-14 19:54:18 +02:00
{
my $extra = $self -> extra_variables ;
$sprint -> placeholder_parser -> set ( $_ , $extra -> { $_ }) for keys %$extra ;
}
# select output file
my $output_file ;
if ( $params { reslice }) {
2015-06-02 22:27:11 +02:00
$output_file = $qs_last_output_file if defined $qs_last_output_file ;
2014-06-14 19:54:18 +02:00
} elsif ( $params { save_as }) {
2016-12-20 19:01:51 +01:00
$output_file = $sprint -> output_filepath ;
2014-06-14 19:54:18 +02:00
$output_file =~ s/\.gcode$/.svg/i if $params { export_svg };
my $dlg = Wx::FileDialog -> new ( $self , 'Save ' . ( $params { export_svg } ? 'SVG' : 'G-code' ) . ' file as:' ,
2014-06-14 19:59:59 +02:00
wxTheApp -> output_path ( dirname ( $output_file )),
2015-06-03 13:19:43 +02:00
basename ( $output_file ), $params { export_svg } ? & Slic3r::GUI::FILE_WILDCARDS -> { svg } : & Slic3r::GUI::FILE_WILDCARDS -> { gcode }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2014-06-14 19:54:18 +02:00
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return ;
}
2015-03-09 15:17:50 +01:00
$output_file = Slic3r:: decode_path ( $dlg -> GetPath );
2015-06-02 22:27:11 +02:00
$qs_last_output_file = $output_file unless $params { export_svg };
2014-06-14 19:54:18 +02:00
$ Slic3r::GUI:: Settings -> { _ }{ last_output_path } = dirname ( $output_file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
$dlg -> Destroy ;
}
# show processbar dialog
$progress_dialog = Wx::ProgressDialog -> new ( 'Slicing…' , "Processing $input_file_basename…" ,
100 , $self , 0 );
$progress_dialog -> Pulse ;
{
my @warnings = ();
local $SIG { __WARN__ } = sub { push @warnings , $_ [ 0 ] };
$sprint -> output_file ( $output_file );
if ( $params { export_svg }) {
$sprint -> export_svg ;
} else {
$sprint -> export_gcode ;
}
$sprint -> status_cb ( undef );
Slic3r::GUI:: warning_catcher ( $self ) -> ( $_ ) for @warnings ;
}
$progress_dialog -> Destroy ;
undef $progress_dialog ;
my $message = "$input_file_basename was successfully sliced." ;
2014-06-14 19:59:59 +02:00
wxTheApp -> notify ( $message );
2014-06-14 19:54:18 +02:00
Wx::MessageDialog -> new ( $self , $message , 'Slicing Done!' ,
wxOK | wxICON_INFORMATION ) -> ShowModal ;
};
Slic3r::GUI:: catch_error ( $self , sub { $progress_dialog -> Destroy if $progress_dialog });
}
2016-10-25 13:24:42 +02:00
sub reslice_now {
my ( $self ) = @_ ;
if ( $self -> { plater }) {
$self -> { plater } -> reslice ;
}
}
2014-06-14 19:54:18 +02:00
sub repair_stl {
my $self = shift ;
my $input_file ;
{
my $dir = $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || '' ;
my $dialog = Wx::FileDialog -> new ( $self , 'Select the STL file to repair:' , $dir , "" , & Slic3r::GUI::FILE_WILDCARDS -> { stl }, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if ( $dialog -> ShowModal != wxID_OK ) {
$dialog -> Destroy ;
return ;
}
2015-03-09 15:17:50 +01:00
$input_file = Slic3r:: decode_path ( $dialog -> GetPaths );
2014-06-14 19:54:18 +02:00
$dialog -> Destroy ;
}
my $output_file = $input_file ;
{
$output_file =~ s/\.stl$/_fixed.obj/i ;
my $dlg = Wx::FileDialog -> new ( $self , "Save OBJ file (less prone to coordinate errors than STL) as:" , dirname ( $output_file ),
basename ( $output_file ), & Slic3r::GUI::FILE_WILDCARDS -> { obj }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if ( $dlg -> ShowModal != wxID_OK ) {
$dlg -> Destroy ;
return undef ;
}
2015-03-09 15:17:50 +01:00
$output_file = Slic3r:: decode_path ( $dlg -> GetPath );
2014-06-14 19:54:18 +02:00
$dlg -> Destroy ;
}
my $tmesh = Slic3r::TriangleMesh -> new ;
$tmesh -> ReadSTLFile ( Slic3r:: encode_path ( $input_file ));
$tmesh -> repair ;
$tmesh -> WriteOBJFile ( Slic3r:: encode_path ( $output_file ));
Slic3r::GUI:: show_info ( $self , "Your file was repaired." , "Repair" );
}
sub extra_variables {
my $self = shift ;
my %extra_variables = ();
if ( $self -> { mode } eq 'expert' ) {
2014-12-25 18:18:41 +01:00
$extra_variables { "${_}_preset" } = $self -> { options_tabs }{ $_ } -> get_current_preset -> name
2014-06-14 19:54:18 +02:00
for qw(print filament printer) ;
}
return { %extra_variables };
}
sub export_config {
my $self = shift ;
my $config = $self -> config ;
eval {
# validate configuration
$config -> validate ;
};
Slic3r::GUI:: catch_error ( $self ) and return ;
my $dir = $last_config ? dirname ( $last_config ) : $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || '' ;
my $filename = $last_config ? basename ( $last_config ) : "config.ini" ;
my $dlg = Wx::FileDialog -> new ( $self , 'Save configuration as:' , $dir , $filename ,
& Slic3r::GUI::FILE_WILDCARDS -> { ini }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if ( $dlg -> ShowModal == wxID_OK ) {
2015-03-09 15:17:50 +01:00
my $file = Slic3r:: decode_path ( $dlg -> GetPath );
2014-06-14 19:54:18 +02:00
$ Slic3r::GUI:: Settings -> { recent }{ config_directory } = dirname ( $file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
$last_config = $file ;
$config -> save ( $file );
}
$dlg -> Destroy ;
}
sub load_config_file {
my $self = shift ;
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 );
return unless $dlg -> ShowModal == wxID_OK ;
2015-03-09 15:17:50 +01:00
$file = Slic3r:: decode_path ( $dlg -> GetPaths );
2014-06-14 19:54:18 +02:00
$dlg -> Destroy ;
}
$ Slic3r::GUI:: Settings -> { recent }{ config_directory } = dirname ( $file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
$last_config = $file ;
for my $tab ( values % { $self -> { options_tabs }}) {
$tab -> load_config_file ( $file );
}
}
sub export_configbundle {
my $self = shift ;
eval {
# validate current configuration in case it's dirty
$self -> config -> validate ;
};
Slic3r::GUI:: catch_error ( $self ) and return ;
my $dir = $last_config ? dirname ( $last_config ) : $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || '' ;
my $filename = "Slic3r_config_bundle.ini" ;
my $dlg = Wx::FileDialog -> new ( $self , 'Save presets bundle as:' , $dir , $filename ,
& Slic3r::GUI::FILE_WILDCARDS -> { ini }, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
if ( $dlg -> ShowModal == wxID_OK ) {
2015-03-09 15:17:50 +01:00
my $file = Slic3r:: decode_path ( $dlg -> GetPath );
2014-06-14 19:54:18 +02:00
$ Slic3r::GUI:: Settings -> { recent }{ config_directory } = dirname ( $file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
# 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 -> { 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) ) {
2014-06-14 19:59:59 +02:00
my %presets = wxTheApp -> presets ( $section );
2014-06-14 19:54:18 +02:00
foreach my $preset_name ( keys %presets ) {
my $config = Slic3r::Config -> load ( $presets { $preset_name });
$ini -> { "$section:$preset_name" } = $config -> as_ini -> { _ };
}
}
Slic3r::Config -> write_ini ( $file , $ini );
}
$dlg -> Destroy ;
}
sub load_configbundle {
2015-12-07 12:17:06 +01:00
my ( $self , $file , $skip_no_id ) = @_ ;
2014-06-14 19:54:18 +02:00
2015-12-07 12:17:06 +01:00
if ( ! $file ) {
my $dir = $last_config ? dirname ( $last_config ) : $ Slic3r::GUI:: Settings -> { recent }{ config_directory } || $ Slic3r::GUI:: Settings -> { recent }{ skein_directory } || '' ;
my $dlg = Wx::FileDialog -> new ( $self , 'Select configuration to load:' , $dir , "config.ini" ,
& Slic3r::GUI::FILE_WILDCARDS -> { ini }, wxFD_OPEN | wxFD_FILE_MUST_EXIST );
return unless $dlg -> ShowModal == wxID_OK ;
$file = Slic3r:: decode_path ( $dlg -> GetPaths );
$dlg -> Destroy ;
}
2014-06-14 19:54:18 +02:00
$ Slic3r::GUI:: Settings -> { recent }{ config_directory } = dirname ( $file );
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
# load .ini file
my $ini = Slic3r::Config -> read_ini ( $file );
if ( $ini -> { settings }) {
$ Slic3r::GUI:: Settings -> { _ }{ $_ } = $ini -> { settings }{ $_ } for keys % { $ini -> { settings }};
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
}
if ( $ini -> { presets }) {
$ Slic3r::GUI:: Settings -> { presets } = $ini -> { presets };
2014-06-14 19:59:59 +02:00
wxTheApp -> save_settings ;
2014-06-14 19:54:18 +02:00
}
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 ;
2015-12-07 12:17:06 +01:00
INI_BLOCK: foreach my $ini_category ( sort keys %$ini ) {
2014-06-14 19:54:18 +02:00
next unless $ini_category =~ /^(print|filament|printer):(.+)$/ ;
my ( $section , $preset_name ) = ( $1 , $2 );
my $config = Slic3r::Config -> load_ini_hash ( $ini -> { $ini_category });
2015-12-07 12:17:06 +01:00
next if $skip_no_id && ! $config -> get ( $section . "_settings_id" );
{
my %current_presets = Slic3r::GUI -> presets ( $section );
my %current_ids = map { $_ => 1 }
grep $_ ,
map Slic3r::Config -> load ( $_ ) -> get ( $section . "_settings_id" ),
values %current_presets ;
next INI_BLOCK if exists $current_ids { $config -> get ( $section . "_settings_id" )};
}
2014-06-14 19:54:18 +02:00
$config -> save ( sprintf "$Slic3r::GUI::datadir/%s/%s.ini" , $section , $preset_name );
2015-12-07 12:17:06 +01:00
Slic3r:: debugf "Imported %s preset %s\n" , $section , $preset_name ;
2014-06-14 19:54:18 +02:00
$imported ++ ;
}
if ( $self -> { mode } eq 'expert' ) {
foreach my $tab ( values % { $self -> { options_tabs }}) {
$tab -> load_presets ;
}
}
2015-12-17 18:48:50 +01:00
return if ! $imported ;
2014-06-14 19:54:18 +02:00
my $message = sprintf "%d presets successfully imported." , $imported ;
if ( $self -> { mode } eq 'simple' && $ Slic3r::GUI:: Settings -> { _ }{ mode } eq 'expert' ) {
Slic3r::GUI:: show_info ( $self , "$message You need to restart Slic3r to make the changes effective." );
} else {
Slic3r::GUI:: show_info ( $self , $message );
}
}
sub load_config {
my $self = shift ;
my ( $config ) = @_ ;
foreach my $tab ( values % { $self -> { options_tabs }}) {
2014-07-01 16:40:56 +02:00
$tab -> load_config ( $config );
2014-06-14 19:54:18 +02:00
}
2015-05-27 00:50:18 +02:00
if ( $self -> { plater }) {
$self -> { plater } -> on_config_change ( $config );
}
2014-06-14 19:54:18 +02:00
}
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 ;
}
2014-09-23 20:00:51 +02:00
} else {
# TODO: select default settings in simple mode
2014-06-14 19:54:18 +02:00
}
$self -> load_config ( $config );
if ( $self -> { mode } eq 'expert' ) {
for my $tab ( values % { $self -> { options_tabs }}) {
$tab -> save_preset ( 'My Settings' );
}
}
}
}
=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 ;
2014-07-01 16:40:56 +02:00
return Slic3r::Config -> new_from_defaults
if ! exists $self -> { options_tabs }{ print }
|| ! exists $self -> { options_tabs }{ filament }
|| ! exists $self -> { options_tabs }{ printer };
2014-06-14 19:54:18 +02:00
# 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 ++ ;
2015-03-23 21:48:31 +01:00
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 );
}
2014-06-14 19:54:18 +02:00
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 ))
2015-03-06 09:56:58 +01:00
for qw(perimeter infill solid_infill support_material support_material_interface) ;
2014-06-14 19:54:18 +02:00
}
return $config ;
}
2015-01-03 23:25:55 +01:00
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 ;
}
2014-06-14 19:54:18 +02:00
sub check_unsaved_changes {
my $self = shift ;
2014-12-28 16:19:55 +01:00
my @dirty = ();
foreach my $tab ( values % { $self -> { options_tabs }}) {
push @dirty , $tab -> title if $tab -> is_dirty ;
}
2014-06-14 19:54:18 +02:00
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 ) = @_ ;
2015-11-08 11:17:55 +01:00
$self -> { tabpanel } -> SetSelection ( $tab );
2014-06-14 19:54:18 +02:00
}
2016-10-05 14:13:07 +02:00
# Set a camera direction, zoom to all objects.
sub select_view {
my ( $self , $direction ) = @_ ;
if ( ! $self -> { no_plater }) {
$self -> { plater } -> select_view ( $direction );
}
}
2014-06-14 20:26:46 +02:00
sub _append_menu_item {
2015-05-25 22:37:04 +02:00
my ( $self , $menu , $string , $description , $cb , $id , $icon ) = @_ ;
2014-06-14 20:26:46 +02:00
2014-07-01 16:40:56 +02:00
$id // = & Wx:: NewId ();
2014-06-14 20:26:46 +02:00
my $item = $menu -> Append ( $id , $string , $description );
2015-05-25 22:37:04 +02:00
$self -> _set_menu_item_icon ( $item , $icon );
2014-06-14 20:26:46 +02:00
EVT_MENU ( $self , $id , $cb );
return $item ;
}
2015-05-25 22:37:04 +02:00
sub _set_menu_item_icon {
my ( $self , $menuItem , $icon ) = @_ ;
2015-05-26 11:27:07 +02:00
# SetBitmap was not available on OS X before Wx 0.9927
if ( $icon && $menuItem -> can ( 'SetBitmap' )) {
2016-04-09 19:10:57 +02:00
$menuItem -> SetBitmap ( Wx::Bitmap -> new ( $ Slic3r:: var -> ( $icon ), wxBITMAP_TYPE_PNG ));
2015-05-25 22:37:04 +02:00
}
}
2016-10-25 13:24:42 +02:00
# Called after the Preferences dialog is closed and the program settings are saved.
# Update the UI based on the current preferences.
sub update_ui_from_settings {
my ( $self ) = @_ ;
$self -> { menu_item_reslice_now } -> Enable ( ! $ Slic3r::GUI:: Settings -> { _ }{ background_processing });
$self -> { plater } -> update_ui_from_settings if ( $self -> { plater });
}
2014-06-14 19:11:04 +02:00
1 ;