mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge branch 'new-gui'
Conflicts: lib/Slic3r.pm lib/Slic3r/Config.pm lib/Slic3r/GUI/OptionsGroup.pm lib/Slic3r/GUI/SkeinPanel.pm lib/Slic3r/Print.pm
This commit is contained in:
		
						commit
						f173096a6c
					
				
					 32 changed files with 1854 additions and 450 deletions
				
			
		
							
								
								
									
										109
									
								
								lib/Slic3r/GUI/AboutDialog.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								lib/Slic3r/GUI/AboutDialog.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| package Slic3r::GUI::AboutDialog; | ||||
| use strict; | ||||
| use warnings; | ||||
| use utf8; | ||||
| 
 | ||||
| use Wx qw(:font :html :misc :sizer :systemsettings); | ||||
| use Wx::Event qw(EVT_HTML_LINK_CLICKED); | ||||
| use Wx::Print; | ||||
| use Wx::Html; | ||||
| use base 'Wx::Dialog'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1, 'About Slic3r', wxDefaultPosition, [600, 270]); | ||||
| 
 | ||||
|     $self->SetBackgroundColour(Wx::wxWHITE); | ||||
|     my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     $self->SetSizer($hsizer); | ||||
| 
 | ||||
|     # logo | ||||
|     my $logo = Slic3r::GUI::AboutDialog::Logo->new($self, -1, wxDefaultPosition, wxDefaultSize); | ||||
|     $logo->SetBackgroundColour(Wx::wxWHITE); | ||||
|     $hsizer->Add($logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30); | ||||
| 
 | ||||
|     my $vsizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $hsizer->Add($vsizer, 1, wxEXPAND, 0); | ||||
| 
 | ||||
|     # title | ||||
|     my $title = Wx::StaticText->new($self, -1, 'Slic3r', wxDefaultPosition, wxDefaultSize); | ||||
|     my $title_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|     $title_font->SetWeight(wxFONTWEIGHT_BOLD); | ||||
|     $title_font->SetFamily(wxFONTFAMILY_ROMAN); | ||||
|     $title_font->SetPointSize(24); | ||||
|     $title->SetFont($title_font); | ||||
|     $vsizer->Add($title, 0, wxALIGN_LEFT | wxTOP, 30); | ||||
| 
 | ||||
|     # version | ||||
|     my $version = Wx::StaticText->new($self, -1, "Version $Slic3r::VERSION", wxDefaultPosition, wxDefaultSize); | ||||
|     my $version_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|     $version_font->SetPointSize(&Wx::wxMSW ? 9 : 11); | ||||
|     $version->SetFont($version_font); | ||||
|     $vsizer->Add($version, 0, wxALIGN_LEFT | wxBOTTOM, 10); | ||||
| 
 | ||||
|     # text | ||||
|     my $text = | ||||
|         '<html>' . | ||||
|         '<body bgcolor="#ffffff" link="#808080">' . | ||||
|         '<font color="#808080">' . | ||||
|         'Copyright © 2011-2012 Alessandro Ranellucci. All rights reserved. ' . | ||||
|         '<a href="http://slic3r.org/">Slic3r</a> is licensed under the ' . | ||||
|         '<a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License, version 3</a>.' . | ||||
|         '<br /><br /><br />' . | ||||
|         'Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James. ' . | ||||
|         'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess and numerous others.' . | ||||
|         '</font>' . | ||||
|         '</body>' . | ||||
|         '</html>'; | ||||
|     my $html = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER); | ||||
|     my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|     my $size = &Wx::wxMSW ? 8 : 10; | ||||
|     $html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size, $size, $size, $size, $size, $size, $size]); | ||||
|     $html->SetBorders(2); | ||||
|     $html->SetPage($text); | ||||
|     $vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20); | ||||
|     EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub link_clicked { | ||||
|     my ($self, $event) = @_; | ||||
| 
 | ||||
|     Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref); | ||||
|     $event->Skip(0); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::AboutDialog::Logo; | ||||
| use Wx qw(:bitmap :dc); | ||||
| use Wx::Event qw(EVT_PAINT); | ||||
| use base 'Wx::Panel'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my $self = $class->SUPER::new(@_); | ||||
| 
 | ||||
|     $self->{logo} = Wx::Bitmap->new("$Slic3r::var/Slic3r_192px.png", wxBITMAP_TYPE_PNG); | ||||
|     $self->SetMinSize(Wx::Size->new($self->{logo}->GetWidth, $self->{logo}->GetHeight)); | ||||
| 
 | ||||
|     EVT_PAINT($self, \&repaint); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub repaint { | ||||
|     my ($self, $event) = @_; | ||||
| 
 | ||||
|     my $dc = Wx::PaintDC->new($self); | ||||
|     $dc->SetBackgroundMode(wxTRANSPARENT); | ||||
| 
 | ||||
|     my $size = $self->GetSize; | ||||
|     my $logo_w = $self->{logo}->GetWidth; | ||||
|     my $logo_h = $self->{logo}->GetHeight; | ||||
|     $dc->DrawBitmap($self->{logo}, ($size->GetWidth - $logo_w) / 2, ($size->GetHeight - $logo_h) / 2, 1); | ||||
| 
 | ||||
|     $event->Skip; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
							
								
								
									
										469
									
								
								lib/Slic3r/GUI/ConfigWizard.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								lib/Slic3r/GUI/ConfigWizard.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,469 @@ | |||
| package Slic3r::GUI::ConfigWizard; | ||||
| use strict; | ||||
| use warnings; | ||||
| use utf8; | ||||
| 
 | ||||
| use Wx; | ||||
| use base 'Wx::Wizard'; | ||||
| 
 | ||||
| # adhere to various human interface guidelines | ||||
| our $wizard = 'Wizard'; | ||||
| $wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1, "Configuration $wizard"); | ||||
| 
 | ||||
|     # Start from sane defaults | ||||
|     $self->{old} = Slic3r::Config->current; | ||||
|     Slic3r::Config->load_hash($Slic3r::Defaults, undef, 1); | ||||
| 
 | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Welcome->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Filament->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Temperature->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::BedTemperature->new($self)); | ||||
|     $self->add_page(Slic3r::GUI::ConfigWizard::Page::Finished->new($self)); | ||||
| 
 | ||||
|     $_->build_index for @{$self->{pages}}; | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub add_page { | ||||
|     my $self = shift; | ||||
|     my ($page) = @_; | ||||
| 
 | ||||
|     my $n = push @{$self->{pages}}, $page; | ||||
|     # add first page to the page area sizer | ||||
|     $self->GetPageAreaSizer->Add($page) if $n == 1; | ||||
|     # link pages | ||||
|     $self->{pages}[$n-2]->set_next_page($page) if $n >= 2; | ||||
|     $page->set_previous_page($self->{pages}[$n-2]) if $n >= 2; | ||||
| } | ||||
| 
 | ||||
| sub run { | ||||
|     my $self = shift; | ||||
| 
 | ||||
|     my $modified; | ||||
|     if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) { | ||||
|         $_->apply for @{$self->{pages}}; | ||||
|         $modified = 1; | ||||
|     } else { | ||||
|         Slic3r::Config->load_hash($self->{old}, undef, 1); | ||||
|         $modified = 0; | ||||
|     } | ||||
| 
 | ||||
|     $self->Destroy; | ||||
| 
 | ||||
|     return $modified; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Option; | ||||
| use Wx qw(:combobox :misc :sizer :textctrl); | ||||
| use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT); | ||||
| use base 'Wx::StaticBoxSizer'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $box = Wx::StaticBox->new($parent, -1, ''); | ||||
|     my $self = $class->SUPER::new($box, wxHORIZONTAL); | ||||
| 
 | ||||
|     my $label_width = 200; | ||||
| 
 | ||||
|     my $opt_key = $params{option}; | ||||
|     my $opt = $Slic3r::Config::Options->{$opt_key}; | ||||
| 
 | ||||
|     my $callback = $params{callback} || sub {}; | ||||
| 
 | ||||
|     # label | ||||
|     my $label = Wx::StaticText->new($parent, -1, "$opt->{label}:", wxDefaultPosition, wxDefaultSize); | ||||
|     $label->Wrap($label_width); | ||||
|     $self->Add($label, 1, wxEXPAND); | ||||
| 
 | ||||
|     # input field(s) and unit | ||||
|     my $field; | ||||
|     if ($opt->{type} =~ /^(i|f|s|s@)$/) { | ||||
|         my $style = $opt->{multiline} ? wxTE_MULTILINE : 0; | ||||
|         my $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); | ||||
| 
 | ||||
|         my ($get, $set) = $opt->{type} eq 's@' ? qw(serialize deserialize) : qw(get_raw set); | ||||
| 
 | ||||
|         if ($opt->{type} eq 'i') { | ||||
|             my $value = Slic3r::Config->$get($opt_key); | ||||
|             $field = Wx::SpinCtrl->new($parent, -1, $value, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 100, $value); | ||||
|             EVT_SPINCTRL($parent, $field, sub { $callback->($opt_key, $field->GetValue) }); | ||||
|         } else { | ||||
|             $field = Wx::TextCtrl->new($parent, -1, Slic3r::Config->$get($opt_key), wxDefaultPosition, $size, $style); | ||||
|             EVT_TEXT($parent, $field, sub { $callback->($opt_key, $field->GetValue) }); | ||||
|         } | ||||
|     } elsif ($opt->{type} eq 'bool') { | ||||
|         $field = Wx::CheckBox->new($parent, -1, ''); | ||||
|         $field->SetValue(Slic3r::Config->get_raw($opt_key)); | ||||
|         EVT_CHECKBOX($parent, $field, sub { $callback->($opt_key, $field->GetValue) }); | ||||
|     } elsif ($opt->{type} eq 'point') { | ||||
|         $field = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         my $field_size = Wx::Size->new(40, -1); | ||||
|         my $value = Slic3r::Config->get_raw($opt_key); | ||||
|         my @items = ( | ||||
|             Wx::StaticText->new($parent, -1, 'x:'), | ||||
|             my $x_field = Wx::TextCtrl->new($parent, -1, $value->[0], wxDefaultPosition, $field_size), | ||||
|             Wx::StaticText->new($parent, -1, '  y:'), | ||||
|             my $y_field = Wx::TextCtrl->new($parent, -1, $value->[1], wxDefaultPosition, $field_size), | ||||
|         ); | ||||
|         $field->Add($_) for @items; | ||||
|         EVT_TEXT($parent, $x_field, sub { $callback->($opt_key, [$x_field->GetValue, $y_field->GetValue]) }); | ||||
|         EVT_TEXT($parent, $y_field, sub { $callback->($opt_key, [$x_field->GetValue, $y_field->GetValue]) }); | ||||
|     } elsif ($opt->{type} eq 'select') { | ||||
|         $field = Wx::ComboBox->new($parent, -1, '', wxDefaultPosition, wxDefaultSize, $opt->{labels} || $opt->{values}, wxCB_READONLY); | ||||
|         my $value = Slic3r::Config->get_raw($opt_key); | ||||
|         $field->SetSelection(grep $opt->{values}[$_] eq $value, 0..$#{$opt->{values}}); | ||||
|         EVT_COMBOBOX($parent, $field, sub { $callback->($opt_key, $opt->{values}[$field->GetSelection]) }); | ||||
|     } else { | ||||
|         die 'Unsupported option type: ' . $opt->{type}; | ||||
|     } | ||||
|     if ($opt->{sidetext}) { | ||||
|         my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         $sizer->Add($field); | ||||
|         my $sidetext = Wx::StaticText->new($parent, -1, $opt->{sidetext}, wxDefaultPosition, wxDefaultSize); | ||||
|         $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); | ||||
|         $self->Add($sizer); | ||||
|     } else { | ||||
|         $self->Add($field, 0, $opt->{full_width} ? wxEXPAND : 0); | ||||
|     } | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Index; | ||||
| use Wx qw(:bitmap :dc :font :misc :sizer :systemsettings :window); | ||||
| use Wx::Event qw(EVT_ERASE_BACKGROUND EVT_PAINT); | ||||
| use base 'Wx::Panel'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $title) = @_; | ||||
|     my $self = $class->SUPER::new($parent); | ||||
| 
 | ||||
|     push @{$self->{titles}}, $title; | ||||
|     $self->{own_index} = 0; | ||||
| 
 | ||||
|     $self->{bullets}->{before} = Wx::Bitmap->new("$Slic3r::var/bullet_black.png", wxBITMAP_TYPE_PNG); | ||||
|     $self->{bullets}->{own}    = Wx::Bitmap->new("$Slic3r::var/bullet_blue.png",  wxBITMAP_TYPE_PNG); | ||||
|     $self->{bullets}->{after}  = Wx::Bitmap->new("$Slic3r::var/bullet_white.png", wxBITMAP_TYPE_PNG); | ||||
| 
 | ||||
|     $self->{background} = Wx::Bitmap->new("$Slic3r::var/Slic3r_192px_transparent.png", wxBITMAP_TYPE_PNG); | ||||
|     $self->SetMinSize(Wx::Size->new($self->{background}->GetWidth, $self->{background}->GetHeight)); | ||||
| 
 | ||||
|     EVT_PAINT($self, \&repaint); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub repaint { | ||||
|     my ($self, $event) = @_; | ||||
|     my $size = $self->GetClientSize; | ||||
|     my $gab = 5; | ||||
| 
 | ||||
|     my $dc = Wx::PaintDC->new($self); | ||||
|     $dc->SetBackgroundMode(wxTRANSPARENT); | ||||
|     $dc->SetFont($self->GetFont); | ||||
|     $dc->SetTextForeground($self->GetForegroundColour); | ||||
| 
 | ||||
|     my $background_h = $self->{background}->GetHeight; | ||||
|     my $background_w = $self->{background}->GetWidth; | ||||
|     $dc->DrawBitmap($self->{background}, ($size->GetWidth - $background_w) / 2, ($size->GetHeight - $background_h) / 2, 1); | ||||
| 
 | ||||
|     my $label_h = $self->{bullets}->{own}->GetHeight; | ||||
|     $label_h = $dc->GetCharHeight if $dc->GetCharHeight > $label_h; | ||||
|     my $label_w = $size->GetWidth; | ||||
| 
 | ||||
|     my $i = 0; | ||||
|     foreach (@{$self->{titles}}) { | ||||
|         my $bullet = $self->{bullets}->{own}; | ||||
|         $bullet = $self->{bullets}->{before} if $i < $self->{own_index}; | ||||
|         $bullet = $self->{bullets}->{after} if $i > $self->{own_index}; | ||||
| 
 | ||||
|         $dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index}; | ||||
|         $dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gab), $label_w, $label_h)); | ||||
|         $i++; | ||||
|     } | ||||
| 
 | ||||
|     $event->Skip; | ||||
| } | ||||
| 
 | ||||
| sub prepend_title { | ||||
|     my $self = shift; | ||||
|     my ($title) = @_; | ||||
| 
 | ||||
|     unshift @{$self->{titles}}, $title; | ||||
|     $self->{own_index}++; | ||||
|     $self->Refresh; | ||||
| } | ||||
| 
 | ||||
| sub append_title { | ||||
|     my $self = shift; | ||||
|     my ($title) = @_; | ||||
| 
 | ||||
|     push @{$self->{titles}}, $title; | ||||
|     $self->Refresh; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page; | ||||
| use Wx qw(:font :misc :sizer :staticline :systemsettings); | ||||
| use base 'Wx::WizardPage'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $title, $short_title) = @_; | ||||
|     my $self = $class->SUPER::new($parent); | ||||
| 
 | ||||
|     my $sizer = Wx::FlexGridSizer->new(0, 2, 10, 10); | ||||
|     $sizer->AddGrowableCol(1, 1); | ||||
|     $sizer->AddGrowableRow(1, 1); | ||||
|     $sizer->AddStretchSpacer(0); | ||||
|     $self->SetSizer($sizer); | ||||
| 
 | ||||
|     # title | ||||
|     my $text = Wx::StaticText->new($self, -1, $title, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); | ||||
|     my $bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|     $bold_font->SetWeight(wxFONTWEIGHT_BOLD); | ||||
|     $bold_font->SetPointSize(14); | ||||
|     $text->SetFont($bold_font); | ||||
|     $sizer->Add($text, 0, wxALIGN_LEFT, 0); | ||||
| 
 | ||||
|     # index | ||||
|     $self->{short_title} = $short_title ? $short_title : $title; | ||||
|     $self->{index} = Slic3r::GUI::ConfigWizard::Index->new($self, $self->{short_title}); | ||||
|     $sizer->Add($self->{index}, 1, wxEXPAND | wxTOP | wxRIGHT, 10); | ||||
| 
 | ||||
|     # contents | ||||
|     $self->{width} = 400; | ||||
|     $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $sizer->Add($self->{vsizer}, 1, wxEXPAND, 0); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub append_text { | ||||
|     my $self = shift; | ||||
|     my ($text) = @_; | ||||
| 
 | ||||
|     my $para = Wx::StaticText->new($self, -1, $text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); | ||||
|     $para->Wrap($self->{width}); | ||||
|     $para->SetMinSize([$self->{width}, -1]); | ||||
|     $self->{vsizer}->Add($para, 0, wxALIGN_LEFT | wxTOP | wxBOTTOM, 10); | ||||
| } | ||||
| 
 | ||||
| sub append_option { | ||||
|     my $self = shift; | ||||
|     my ($opt_key) = @_; | ||||
| 
 | ||||
|     my $option = Slic3r::GUI::ConfigWizard::Option->new($self, option => $opt_key, | ||||
|                                                         callback => sub { | ||||
|                                                             my ($opt_key, $value) = @_; | ||||
|                                                             $self->{options}->{$opt_key} = $value; | ||||
|                                                         }); | ||||
|     $self->{vsizer}->Add($option, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); | ||||
| } | ||||
| 
 | ||||
| sub apply { | ||||
|     my $self = shift; | ||||
|     Slic3r::Config->set($_, $self->{options}->{$_}) foreach (keys %{$self->{options}}); | ||||
| } | ||||
| 
 | ||||
| sub set_previous_page { | ||||
|     my $self = shift; | ||||
|     my ($previous_page) = @_; | ||||
|     $self->{previous_page} = $previous_page; | ||||
| } | ||||
| 
 | ||||
| sub GetPrev { | ||||
|     my $self = shift; | ||||
|     return $self->{previous_page}; | ||||
| } | ||||
| 
 | ||||
| sub set_next_page { | ||||
|     my $self = shift; | ||||
|     my ($next_page) = @_; | ||||
|     $self->{next_page} = $next_page; | ||||
| } | ||||
| 
 | ||||
| sub GetNext { | ||||
|     my $self = shift; | ||||
|     return $self->{next_page}; | ||||
| } | ||||
| 
 | ||||
| sub get_short_title { | ||||
|     my $self = shift; | ||||
|     return $self->{short_title}; | ||||
| } | ||||
| 
 | ||||
| sub build_index { | ||||
|     my $self = shift; | ||||
| 
 | ||||
|     my $page = $self; | ||||
|     $self->{index}->prepend_title($page->get_short_title) while ($page = $page->GetPrev); | ||||
|     $page = $self; | ||||
|     $self->{index}->append_title($page->get_short_title) while ($page = $page->GetNext); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Welcome; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome'); | ||||
| 
 | ||||
|     $self->append_text('Hello, welcome to Slic3r! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.'); | ||||
|     $self->append_text('To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.'); | ||||
|     $self->append_text('To continue, click Next.'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Firmware; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Firmware Type'); | ||||
| 
 | ||||
|     $self->append_text('Choose the type of firmware used by your printer, then click Next.'); | ||||
|     $self->append_option('gcode_flavor'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Bed; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Bed Size'); | ||||
| 
 | ||||
|     $self->append_text('Enter the size of your printers bed, then click Next.'); | ||||
|     $self->append_option('bed_size'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub apply { | ||||
|     my $self = shift; | ||||
|     $self->SUPER::apply; | ||||
| 
 | ||||
|     # set print_center to centre of bed_size | ||||
|     my $bed_size = Slic3r::Config->get_raw('bed_size'); | ||||
|     Slic3r::Config->set('print_center', [$bed_size->[0]/2, $bed_size->[1]/2]); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Nozzle; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Nozzle Diameter'); | ||||
| 
 | ||||
|     $self->append_text('Enter the diameter of your printers hot end nozzle, then click Next.'); | ||||
|     $self->append_option('nozzle_diameter'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub apply { | ||||
|     my $self = shift; | ||||
|     $self->SUPER::apply; | ||||
| 
 | ||||
|     # set first_layer_height + layer_height based on nozzle_diameter | ||||
|     my $nozzle = Slic3r::Config->get_raw('nozzle_diameter'); | ||||
|     Slic3r::Config->set('first_layer_height', $nozzle); | ||||
|     Slic3r::Config->set('layer_height', $nozzle - 0.1); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Filament; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Filament Diameter'); | ||||
| 
 | ||||
|     $self->append_text('Enter the diameter of your filament, then click Next.'); | ||||
|     $self->append_text('Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.'); | ||||
|     $self->append_option('filament_diameter'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Temperature; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Extrusion Temperature'); | ||||
| 
 | ||||
|     $self->append_text('Enter the temperature needed for extruding your filament, then click Next.'); | ||||
|     $self->append_text('A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.'); | ||||
|     $self->append_option('temperature'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub apply { | ||||
|     my $self = shift; | ||||
|     $self->SUPER::apply; | ||||
| 
 | ||||
|     # set first_layer_temperature to temperature + 5 | ||||
|     my $temperature = Slic3r::Config->get_raw('temperature'); | ||||
|     Slic3r::Config->set('first_layer_temperature', $temperature + 5); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::BedTemperature; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Bed Temperature'); | ||||
| 
 | ||||
|     $self->append_text('Enter the bed temperature needed for getting your filament to stick to your heated bed, then click Next.'); | ||||
|     $self->append_text('A rule of thumb is 60 °C for PLA and 110 °C for ABS.'); | ||||
|     $self->append_option('bed_temperature'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub apply { | ||||
|     my $self = shift; | ||||
|     $self->SUPER::apply; | ||||
| 
 | ||||
|     # set first_layer_bed_temperature to temperature + 5 | ||||
|     my $temperature = Slic3r::Config->get_raw('bed_temperature'); | ||||
|     Slic3r::Config->set('first_layer_bed_temperature', $temperature + 5); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::ConfigWizard::Page::Finished; | ||||
| use base 'Slic3r::GUI::ConfigWizard::Page'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Congratulations!', 'Finish'); | ||||
| 
 | ||||
|     $self->append_text("You have successfully completed the Slic3r Configuration $wizard. " . | ||||
|                        'Slic3r is now configured for your printer and filament.'); | ||||
|     $self->append_text('To close this '.lc($wizard).' and apply the newly created configuration, click Finish.'); | ||||
| 
 | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  | @ -2,14 +2,14 @@ package Slic3r::GUI::OptionsGroup; | |||
| use strict; | ||||
| use warnings; | ||||
| 
 | ||||
| use Wx qw(:sizer wxSYS_DEFAULT_GUI_FONT); | ||||
| use Wx::Event qw(EVT_TEXT EVT_CHECKBOX EVT_CHOICE); | ||||
| use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl); | ||||
| use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT); | ||||
| use base 'Wx::StaticBoxSizer'; | ||||
| 
 | ||||
| 
 | ||||
| # not very elegant, but this solution is temporary waiting for a better GUI | ||||
| our @reload_callbacks = (); | ||||
| our %fields = ();  # $key => [$control] | ||||
| our %reload_callbacks = (); # key => $cb | ||||
| our %fields = ();           # $key => [$control] | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|  | @ -18,34 +18,31 @@ sub new { | |||
|     my $box = Wx::StaticBox->new($parent, -1, $p{title}); | ||||
|     my $self = $class->SUPER::new($box, wxVERTICAL); | ||||
|      | ||||
|     my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$p{options}}), 2, 2, 0); | ||||
| 
 | ||||
|     #grab the default font, to fix Windows font issues/keep things consistent | ||||
|     my $bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|     $bold_font->SetWeight(&Wx::wxFONTWEIGHT_BOLD); | ||||
| 
 | ||||
|     my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$p{options}}), 2, ($p{no_labels} ? 1 : 2), 0); | ||||
|     $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); | ||||
|     $grid_sizer->AddGrowableCol($p{no_labels} ? 0 : 1); | ||||
|      | ||||
|     my $sidetext_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | ||||
|      | ||||
|     my $onChange = $p{on_change} || sub {}; | ||||
|      | ||||
|     foreach my $opt_key (@{$p{options}}) { | ||||
|         my $index; | ||||
|         $opt_key =~ s/#(\d+)$// and $index = $1; | ||||
|          | ||||
|         my $opt = $Slic3r::Config::Options->{$opt_key}; | ||||
|         my $label = Wx::StaticText->new($parent, -1, "$opt->{label}:", Wx::wxDefaultPosition, | ||||
|             [$p{label_width} || 180, -1]); | ||||
|         $label->Wrap($p{label_width} || 180);  # needed to avoid Linux/GTK bug | ||||
|         my $label; | ||||
|         if (!$p{no_labels}) {print $opt_key, "\n" if !defined $opt->{label}; | ||||
|             $label = Wx::StaticText->new($parent, -1, "$opt->{label}:", wxDefaultPosition, [$p{label_width} || 180, -1]); | ||||
|             $label->Wrap($p{label_width} || 180) ;  # needed to avoid Linux/GTK bug | ||||
|             $grid_sizer->Add($label); | ||||
|         } | ||||
|          | ||||
|         #set the bold font point size to the same size as all the other labels (for consistency) | ||||
|         $bold_font->SetPointSize($label->GetFont()->GetPointSize()); | ||||
|         $label->SetFont($bold_font) if $opt->{important}; | ||||
|         my $field; | ||||
|         if ($opt->{type} =~ /^(i|f|s|s@)$/) { | ||||
|             my $style = 0; | ||||
|             my $size = Wx::wxDefaultSize; | ||||
|              | ||||
|             if ($opt->{multiline}) { | ||||
|                 $style = &Wx::wxTE_MULTILINE; | ||||
|                 $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); | ||||
|             } | ||||
|             $style = wxTE_MULTILINE if $opt->{multiline}; | ||||
|             my $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); | ||||
|              | ||||
|             # if it's an array type but no index was specified, use the serialized version | ||||
|             my ($get_m, $set_m) = $opt->{type} =~ /\@$/ && !defined $index | ||||
|  | @ -57,8 +54,10 @@ sub new { | |||
|                 $val = $val->[$index] if defined $index; | ||||
|                 return $val; | ||||
|             }; | ||||
|             $field = Wx::TextCtrl->new($parent, -1, $get->(), Wx::wxDefaultPosition, $size, $style); | ||||
|             push @reload_callbacks, sub { $field->SetValue($get->()) }; | ||||
|             $field = $opt->{type} eq 'i' | ||||
|                 ? Wx::SpinCtrl->new($parent, -1, $get->(), wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 100, $get->()) | ||||
|                 : Wx::TextCtrl->new($parent, -1, $get->(), wxDefaultPosition, $size, $style); | ||||
|             $reload_callbacks{$opt_key} = sub { $field->SetValue($get->()) }; | ||||
|              | ||||
|             my $set = sub { | ||||
|                 my $val = $field->GetValue; | ||||
|  | @ -67,51 +66,70 @@ sub new { | |||
|                 } else { | ||||
|                     Slic3r::Config->$set_m($opt_key, $val); | ||||
|                 } | ||||
|                 $onChange->($opt_key); | ||||
|             }; | ||||
|             EVT_TEXT($parent, $field, sub { $set->() }); | ||||
|             $opt->{type} eq 'i' | ||||
|                 ? EVT_SPINCTRL($parent, $field, $set) | ||||
|                 : EVT_TEXT($parent, $field, $set); | ||||
|         } elsif ($opt->{type} eq 'bool') { | ||||
|             $field = Wx::CheckBox->new($parent, -1, ""); | ||||
|             $field->SetValue(Slic3r::Config->get_raw($opt_key)); | ||||
|             EVT_CHECKBOX($parent, $field, sub { Slic3r::Config->set($opt_key, $field->GetValue) }); | ||||
|             push @reload_callbacks, sub { $field->SetValue(Slic3r::Config->get_raw($opt_key)) }; | ||||
|             EVT_CHECKBOX($parent, $field, sub { Slic3r::Config->set($opt_key, $field->GetValue); $onChange->($opt_key) }); | ||||
|             $reload_callbacks{$opt_key} = sub { $field->SetValue(Slic3r::Config->get_raw($opt_key)) }; | ||||
|         } elsif ($opt->{type} eq 'point') { | ||||
|             $field = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|             my $field_size = Wx::Size->new(40, -1); | ||||
|             my $value = Slic3r::Config->get_raw($opt_key); | ||||
|             $field->Add($_) for ( | ||||
|             my @items = ( | ||||
|                 Wx::StaticText->new($parent, -1, "x:"), | ||||
|                 my $x_field = Wx::TextCtrl->new($parent, -1, $value->[0], Wx::wxDefaultPosition, $field_size), | ||||
|                 my $x_field = Wx::TextCtrl->new($parent, -1, $value->[0], wxDefaultPosition, $field_size), | ||||
|                 Wx::StaticText->new($parent, -1, "  y:"), | ||||
|                 my $y_field = Wx::TextCtrl->new($parent, -1, $value->[1], Wx::wxDefaultPosition, $field_size), | ||||
|                 my $y_field = Wx::TextCtrl->new($parent, -1, $value->[1], wxDefaultPosition, $field_size), | ||||
|             ); | ||||
|             $field->Add($_) for @items; | ||||
|             if ($opt->{tooltip}) { | ||||
|                 $_->SetToolTipString($opt->{tooltip}) for @items; | ||||
|             } | ||||
|             my $set_value = sub { | ||||
|                 my ($i, $value) = @_; | ||||
|                 my $val = Slic3r::Config->get_raw($opt_key); | ||||
|                 $val->[$i] = $value; | ||||
|                 Slic3r::Config->set($opt_key, $val); | ||||
|             }; | ||||
|             EVT_TEXT($parent, $x_field, sub { $set_value->(0, $x_field->GetValue) }); | ||||
|             EVT_TEXT($parent, $y_field, sub { $set_value->(1, $y_field->GetValue) }); | ||||
|             push @reload_callbacks, sub { | ||||
|             EVT_TEXT($parent, $x_field, sub { $set_value->(0, $x_field->GetValue); $onChange->($opt_key) }); | ||||
|             EVT_TEXT($parent, $y_field, sub { $set_value->(1, $y_field->GetValue); $onChange->($opt_key) }); | ||||
|             $reload_callbacks{$opt_key} = sub { | ||||
|                 my $value = Slic3r::Config->get_raw($opt_key); | ||||
|                 $x_field->SetValue($value->[0]); | ||||
|                 $y_field->SetValue($value->[1]); | ||||
|             }; | ||||
|             $fields{$opt_key} = [$x_field, $y_field]; | ||||
|         } elsif ($opt->{type} eq 'select') { | ||||
|             $field = Wx::Choice->new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, $opt->{labels} || $opt->{values}); | ||||
|             EVT_CHOICE($parent, $field, sub { | ||||
|             $field = Wx::ComboBox->new($parent, -1, "", wxDefaultPosition, wxDefaultSize, $opt->{labels} || $opt->{values}, wxCB_READONLY); | ||||
|             EVT_COMBOBOX($parent, $field, sub { | ||||
|                 Slic3r::Config->set($opt_key, $opt->{values}[$field->GetSelection]); | ||||
|                 $onChange->($opt_key); | ||||
|             }); | ||||
|             push @reload_callbacks, sub { | ||||
|             $reload_callbacks{$opt_key} = sub { | ||||
|                 my $value = Slic3r::Config->get_raw($opt_key); | ||||
|                 $field->SetSelection(grep $opt->{values}[$_] eq $value, 0..$#{$opt->{values}}); | ||||
|             }; | ||||
|             $reload_callbacks[-1]->(); | ||||
|             $reload_callbacks{$opt_key}->(); | ||||
|         } else { | ||||
|             die "Unsupported option type: " . $opt->{type}; | ||||
|         } | ||||
|         $grid_sizer->Add($_) for $label, $field; | ||||
|         $label->SetToolTipString($opt->{tooltip}) if $label && $opt->{tooltip}; | ||||
|         $field->SetToolTipString($opt->{tooltip}) if $opt->{tooltip} && $field->can('SetToolTipString'); | ||||
|         if ($opt->{sidetext}) { | ||||
|             my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|             $sizer->Add($field); | ||||
|             my $sidetext = Wx::StaticText->new($parent, -1, $opt->{sidetext}, wxDefaultPosition, wxDefaultSize); | ||||
|             $sidetext->SetFont($sidetext_font); | ||||
|             $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); | ||||
|             $grid_sizer->Add($sizer); | ||||
|         } else { | ||||
|             $grid_sizer->Add($field, 0, $opt->{full_width} ? wxEXPAND : 0); | ||||
|         } | ||||
|         $fields{$opt_key} ||= [$field]; | ||||
|     } | ||||
|      | ||||
|  |  | |||
|  | @ -8,10 +8,8 @@ use Math::ConvexHull qw(convex_hull); | |||
| use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale); | ||||
| use Slic3r::Geometry::Clipper qw(JT_ROUND); | ||||
| use threads::shared qw(shared_clone); | ||||
| use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_WARNING wxICON_ERROR wxICON_QUESTION | ||||
|     wxOK wxCANCEL wxID_OK wxFD_OPEN wxFD_SAVE wxDEFAULT wxNORMAL); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_PAINT EVT_MOUSE_EVENTS EVT_LIST_ITEM_SELECTED EVT_LIST_ITEM_DESELECTED | ||||
|     EVT_COMMAND EVT_TOOL); | ||||
| use Wx qw(:bitmap :brush :button :dialog :filedialog :font :icon :id :listctrl :misc :pen :sizer :toolbar :window); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL); | ||||
| use base 'Wx::Panel'; | ||||
| 
 | ||||
| use constant TB_MORE    => 1; | ||||
|  | @ -33,58 +31,58 @@ sub new { | |||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1); | ||||
|      | ||||
|     $self->{canvas} = Wx::Panel->new($self, -1, [-1, -1], [300, 300]); | ||||
|     $self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, [300, 300]); | ||||
|     $self->{canvas}->SetBackgroundColour(Wx::wxWHITE); | ||||
|     EVT_PAINT($self->{canvas}, \&repaint); | ||||
|     EVT_MOUSE_EVENTS($self->{canvas}, \&mouse_event); | ||||
|      | ||||
|     $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), &Wx::wxSOLID); | ||||
|     $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), &Wx::wxSOLID); | ||||
|     $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), &Wx::wxTRANSPARENT); | ||||
|     $self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, &Wx::wxSOLID); | ||||
|     $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, &Wx::wxSOLID); | ||||
|     $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, &Wx::wxSOLID); | ||||
|     $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, &Wx::wxSOLID); | ||||
|     $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); | ||||
|     $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); | ||||
|     $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT); | ||||
|     $self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID); | ||||
|     $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); | ||||
|     $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID); | ||||
|     $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID); | ||||
|      | ||||
|     $self->{list} = Wx::ListView->new($self, -1, [-1, -1], [-1, 180], &Wx::wxLC_SINGLE_SEL | &Wx::wxLC_REPORT | &Wx::wxBORDER_DEFAULT); | ||||
|     $self->{list}->InsertColumn(0, "Name", &Wx::wxLIST_FORMAT_LEFT, 300); | ||||
|     $self->{list}->InsertColumn(1, "Copies", &Wx::wxLIST_FORMAT_CENTER, 50); | ||||
|     $self->{list}->InsertColumn(2, "Scale", &Wx::wxLIST_FORMAT_CENTER, 50); | ||||
|     $self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, [-1, 180], wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN); | ||||
|     $self->{list}->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 300); | ||||
|     $self->{list}->InsertColumn(1, "Copies", wxLIST_FORMAT_CENTER, 50); | ||||
|     $self->{list}->InsertColumn(2, "Scale", wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER); | ||||
|     EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected); | ||||
|     EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected); | ||||
|      | ||||
|     # toolbar for object manipulation | ||||
|     if (!&Wx::wxMSW) { | ||||
|         Wx::ToolTip::Enable(1); | ||||
|         $self->{htoolbar} = Wx::ToolBar->new($self, -1, [-1, -1], [-1, -1], &Wx::wxTB_HORIZONTAL | &Wx::wxTB_HORZ_TEXT); | ||||
|         $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new("$Slic3r::var/add.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_LESS, "Less", Wx::Bitmap->new("$Slic3r::var/delete.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE); | ||||
|         $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_LESS, "Less", Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddSeparator; | ||||
|         $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_ROTATE, "Rotate...", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_ROTATE, "Rotate…", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddSeparator; | ||||
|         $self->{htoolbar}->AddTool(TB_SCALE, "Scale...", Wx::Bitmap->new("$Slic3r::var/arrow_out.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new("$Slic3r::var/arrow_out.png", wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddSeparator; | ||||
|         $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", &Wx::wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new("$Slic3r::var/shape_ungroup.png", wxBITMAP_TYPE_PNG), ''); | ||||
|     } else { | ||||
|         my %tbar_buttons = (increase => "More", decrease => "Less", rotate45ccw => "45°", rotate45cw => "45°", | ||||
|             rotate => "Rotate…", changescale => "Scale…", split => "Split"); | ||||
|         $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         for (qw(increase decrease rotate45ccw rotate45cw rotate changescale split)) { | ||||
|             $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, [-1,-1], [-1,-1], &Wx::wxBU_EXACTFIT); | ||||
|             $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||
|             $self->{btoolbar}->Add($self->{"btn_$_"}); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # general buttons | ||||
|     $self->{btn_load} = Wx::Button->new($self, -1, "Add…", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_remove} = Wx::Button->new($self, -1, "Delete", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_reset} = Wx::Button->new($self, -1, "Delete All", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_arrange} = Wx::Button->new($self, -1, "Autoarrange", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_load} = Wx::Button->new($self, -1, "Add…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|     $self->{btn_remove} = Wx::Button->new($self, -1, "Delete", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|     $self->{btn_reset} = Wx::Button->new($self, -1, "Delete All", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|     $self->{btn_arrange} = Wx::Button->new($self, -1, "Autoarrange", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|     $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|     $self->{btn_export_gcode}->SetDefault; | ||||
|     $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", [-1,-1], [-1,-1], &Wx::wxBU_LEFT); | ||||
|     $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); | ||||
|      | ||||
|     if (&Wx::wxVERSION_STRING =~ / 2\.9\.[1-9]/) { | ||||
|         my %icons = qw( | ||||
|  | @ -104,7 +102,7 @@ sub new { | |||
|             split           shape_ungroup.png | ||||
|         ); | ||||
|         for (grep $self->{"btn_$_"}, keys %icons) { | ||||
|             $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/$icons{$_}", &Wx::wxBITMAP_TYPE_PNG)); | ||||
|             $self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/$icons{$_}", wxBITMAP_TYPE_PNG)); | ||||
|         } | ||||
|     } | ||||
|     $self->selection_changed(0); | ||||
|  | @ -148,7 +146,7 @@ sub new { | |||
|         my ($self, $event) = @_; | ||||
|         my ($percent, $message) = @{$event->GetData}; | ||||
|         $self->statusbar->SetProgress($percent); | ||||
|         $self->statusbar->SetStatusText("$message..."); | ||||
|         $self->statusbar->SetStatusText("$message…"); | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $MESSAGE_DIALOG_EVENT, sub { | ||||
|  | @ -185,17 +183,35 @@ sub new { | |||
|          | ||||
|         my $list_sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         $list_sizer->Add($self->{list}, 1, wxEXPAND | wxALL, 0); | ||||
|         $list_sizer->Add($self->{vtoolbar}, 0, wxEXPAND, 0) if $self->{vtoolbar}; | ||||
|          | ||||
|         my $vertical_sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|         $vertical_sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar}; | ||||
|         $vertical_sizer->Add($self->{btoolbar}, 0, wxEXPAND, 0) if $self->{btoolbar}; | ||||
|         $vertical_sizer->Add($list_sizer, 0, wxEXPAND | &Wx::wxBOTTOM, 10); | ||||
|         $vertical_sizer->Add($list_sizer, 0, wxEXPAND | wxBOTTOM, 10); | ||||
|         $vertical_sizer->Add($buttons, 0, wxEXPAND); | ||||
|          | ||||
|         my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         $sizer->Add($self->{canvas}, 0, wxALL, 10); | ||||
|         $sizer->Add($vertical_sizer, 1, wxEXPAND | wxALL, 10); | ||||
|         my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         $hsizer->Add($self->{canvas}, 0, wxALL, 10); | ||||
|         $hsizer->Add($vertical_sizer, 1, wxEXPAND | wxALL, 10); | ||||
|          | ||||
|         my $presets = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         my %group_labels = ( | ||||
|             print       => 'Print settings', | ||||
|             filament    => 'Filament', | ||||
|             printer     => 'Printer', | ||||
|         ); | ||||
|         $self->{preset_choosers} = {}; | ||||
|         for my $group (qw(print filament printer)) { | ||||
|             my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); | ||||
|             $self->{preset_choosers}{$group} = Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], []); | ||||
|             $presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4); | ||||
|             $presets->Add($self->{preset_choosers}{$group}, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 15); | ||||
|         } | ||||
|          | ||||
|         my $sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|         $sizer->Add($hsizer, 0, wxEXPAND | wxBOTTOM, 10); | ||||
|         $sizer->Add($presets, 0, wxEXPAND | wxLEFT | wxRIGHT, 20); | ||||
|          | ||||
|         $sizer->SetSizeHints($self); | ||||
|         $self->SetSizer($sizer); | ||||
|     } | ||||
|  | @ -206,7 +222,7 @@ sub load { | |||
|     my $self = shift; | ||||
|      | ||||
|     my $dir = $Slic3r::GUI::SkeinPanel::last_skein_dir || $Slic3r::GUI::SkeinPanel::last_config_dir || ""; | ||||
|     my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "", $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_OPEN | &Wx::wxFD_MULTIPLE | &Wx::wxFD_FILE_MUST_EXIST); | ||||
|     my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "", $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); | ||||
|     if ($dialog->ShowModal != wxID_OK) { | ||||
|         $dialog->Destroy; | ||||
|         return; | ||||
|  | @ -222,7 +238,7 @@ sub load_file { | |||
|      | ||||
|     $Slic3r::GUI::SkeinPanel::last_skein_dir = dirname($input_file); | ||||
|      | ||||
|     my $process_dialog = Wx::ProgressDialog->new('Loading...', "Processing input file...", 100, $self, 0); | ||||
|     my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0); | ||||
|     $process_dialog->Pulse; | ||||
|     local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); | ||||
|     $self->{print}->add_object_from_file($input_file); | ||||
|  | @ -336,7 +352,7 @@ sub rotate { | |||
|         return if !$angle || $angle == -1; | ||||
|     } | ||||
|      | ||||
|     $self->statusbar->SetStatusText("Rotating object..."); | ||||
|     $self->statusbar->SetStatusText("Rotating object…"); | ||||
|     $self->statusbar->StartBusy; | ||||
|      | ||||
|     # rotate, realign to 0,0 and update size | ||||
|  | @ -374,7 +390,7 @@ sub changescale { | |||
|     $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $scale*100, 0, 5000, $self); | ||||
|     return if !$scale || $scale == -1; | ||||
|      | ||||
|     $self->statusbar->SetStatusText("Scaling object..."); | ||||
|     $self->statusbar->SetStatusText("Scaling object…"); | ||||
|     $self->statusbar->StartBusy; | ||||
|      | ||||
|     my $object = $self->{print}->objects->[$obj_idx]; | ||||
|  | @ -425,7 +441,7 @@ sub export_gcode { | |||
|     my $self = shift; | ||||
|      | ||||
|     if ($self->{export_thread}) { | ||||
|         Wx::MessageDialog->new($self, "Another slicing job is currently running.", 'Error', wxOK | &Wx::wxICON_ERROR)->ShowModal; | ||||
|         Wx::MessageDialog->new($self, "Another slicing job is currently running.", 'Error', wxOK | wxICON_ERROR)->ShowModal; | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|  | @ -471,7 +487,7 @@ sub export_gcode { | |||
|             progressbar => sub { | ||||
|                 my ($percent, $message) = @_; | ||||
|                 $self->statusbar->SetProgress($percent); | ||||
|                 $self->statusbar->SetStatusText("$message..."); | ||||
|                 $self->statusbar->SetStatusText("$message…"); | ||||
|             }, | ||||
|             message_dialog => sub { Wx::MessageDialog->new($self, @_)->ShowModal }, | ||||
|             on_completed => sub { $self->on_export_completed(@_) }, | ||||
|  | @ -515,12 +531,13 @@ sub export_gcode2 { | |||
|         } | ||||
|          | ||||
|         my $message = "Your files were successfully sliced"; | ||||
|         $message .= sprintf " in %d minutes and %.3f seconds", | ||||
|             int($print->processing_time/60), | ||||
|             $print->processing_time - int($print->processing_time/60)*60 | ||||
|                 if $print->processing_time; | ||||
|         if ($print->processing_time) { | ||||
|             $message .= ' in'; | ||||
|             my $minutes = int($print->processing_time/60); | ||||
|             $message .= sprintf " %d minutes and", $minutes if $minutes; | ||||
|             $message .= sprintf " %.1f seconds", $print->processing_time - $minutes*60; | ||||
|         } | ||||
|         $message .= "."; | ||||
|         Slic3r::GUI::notify($message); | ||||
|         $params{on_completed}->($message); | ||||
|         $print->cleanup; | ||||
|     }; | ||||
|  | @ -536,12 +553,11 @@ sub on_export_completed { | |||
|     $self->statusbar->SetCancelCallback(undef); | ||||
|     $self->statusbar->StopBusy; | ||||
|     $self->statusbar->SetStatusText("G-code file exported to $self->{output_file}"); | ||||
|     Wx::MessageDialog->new($self, $message, 'Done!', wxOK | wxICON_INFORMATION)->ShowModal; | ||||
|     &Wx::wxTheApp->notify($message); | ||||
| } | ||||
| 
 | ||||
| sub on_export_failed { | ||||
|     my $self = shift; | ||||
|     my ($message) = @_; | ||||
|      | ||||
|     $self->{export_thread}->detach if $self->{export_thread}; | ||||
|     $self->{export_thread} = undef; | ||||
|  | @ -561,7 +577,7 @@ sub export_stl { | |||
|         $output_file = $print->expanded_output_filepath($output_file); | ||||
|         $output_file =~ s/\.gcode$/.stl/i; | ||||
|         my $dlg = Wx::FileDialog->new($self, 'Save STL file as:', dirname($output_file), | ||||
|             basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | &Wx::wxFD_OVERWRITE_PROMPT); | ||||
|             basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | ||||
|         if ($dlg->ShowModal != wxID_OK) { | ||||
|             $dlg->Destroy; | ||||
|             return; | ||||
|  | @ -674,12 +690,12 @@ sub repaint { | |||
|         $dc->DrawLine(0, $size[Y]/2, $size[X], $size[Y]/2); | ||||
|         $dc->SetTextForeground(Wx::Colour->new(0,0,0)); | ||||
|         $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); | ||||
|         $dc->DrawLabel("X = " . $Slic3r::print_center->[X], Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), &Wx::wxALIGN_CENTER_HORIZONTAL | &Wx::wxALIGN_BOTTOM); | ||||
|         $dc->DrawLabel("X = " . $Slic3r::print_center->[X], Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM); | ||||
|         $dc->DrawRotatedText("Y = " . $Slic3r::print_center->[Y], 0, $size[Y]/2+15, 90); | ||||
|     } | ||||
|      | ||||
|     # draw frame | ||||
|     $dc->SetPen(Wx::wxBLACK_PEN); | ||||
|     $dc->SetPen(wxBLACK_PEN); | ||||
|     $dc->SetBrush($parent->{transparent_brush}); | ||||
|     $dc->DrawRectangle(0, 0, @size); | ||||
|      | ||||
|  | @ -687,11 +703,11 @@ sub repaint { | |||
|     if (!@{$print->objects}) { | ||||
|         $dc->SetTextForeground(Wx::Colour->new(150,50,50)); | ||||
|         $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); | ||||
|         $dc->DrawLabel("Drag your objects here", Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), &Wx::wxALIGN_CENTER_HORIZONTAL | &Wx::wxALIGN_CENTER_VERTICAL); | ||||
|         $dc->DrawLabel("Drag your objects here", Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); | ||||
|     } | ||||
|      | ||||
|     # draw thumbnails | ||||
|     $dc->SetPen(Wx::wxBLACK_PEN); | ||||
|     $dc->SetPen(wxBLACK_PEN); | ||||
|     @{$parent->{object_previews}} = (); | ||||
|     for my $obj_idx (0 .. $#{$print->objects}) { | ||||
|         next unless $parent->{thumbnails}[$obj_idx]; | ||||
|  | @ -834,7 +850,6 @@ sub _y { | |||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Plater::DropTarget; | ||||
| 
 | ||||
| use Wx::DND; | ||||
| use base 'Wx::FileDropTarget'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ use utf8; | |||
| 
 | ||||
| use File::Basename qw(basename dirname); | ||||
| use Slic3r::Geometry qw(X Y); | ||||
| use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_WARNING wxICON_ERROR wxICON_QUESTION | ||||
|     wxOK wxCANCEL wxID_OK wxFD_OPEN wxFD_SAVE wxDEFAULT wxNORMAL); | ||||
| use Wx qw(:dialog :filedialog :font :icon :id :misc :notebook :sizer); | ||||
| use Wx::Event qw(EVT_BUTTON); | ||||
| use base 'Wx::Panel'; | ||||
| 
 | ||||
|  | @ -21,145 +20,24 @@ sub new { | |||
|     my ($parent) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1); | ||||
|      | ||||
|     no warnings 'qw'; | ||||
|     my %panels = ( | ||||
|         printer => { | ||||
|             title => 'Printer', | ||||
|             options => [qw(nozzle_diameter#0 bed_size print_center z_offset gcode_flavor use_relative_e_distances)], | ||||
|         }, | ||||
|         filament => { | ||||
|             title => 'Filament', | ||||
|             options => [qw(filament_diameter#0 extrusion_multiplier#0 temperature#0 first_layer_temperature#0 bed_temperature first_layer_bed_temperature)], | ||||
|         }, | ||||
|         print_speed => { | ||||
|             title => 'Print speed', | ||||
|             options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed)], | ||||
|         }, | ||||
|         speed => { | ||||
|             title => 'Other speed settings', | ||||
|             options => [qw(travel_speed first_layer_speed)], | ||||
|         }, | ||||
|         accuracy => { | ||||
|             title => 'Accuracy', | ||||
|             options => [qw(layer_height first_layer_height infill_every_layers)], | ||||
|         }, | ||||
|         print => { | ||||
|             title => 'Print settings', | ||||
|             options => [qw(perimeters solid_layers fill_density fill_angle fill_pattern solid_fill_pattern randomize_start)], | ||||
|         }, | ||||
|         retract => { | ||||
|             title => 'Retraction', | ||||
|             options => [qw(retract_length retract_lift retract_speed retract_restart_extra retract_before_travel)], | ||||
|         }, | ||||
|         cooling => { | ||||
|             title => 'Cooling', | ||||
|             options => [qw(cooling min_fan_speed max_fan_speed bridge_fan_speed fan_below_layer_time slowdown_below_layer_time min_print_speed disable_fan_first_layers fan_always_on)], | ||||
|             label_width => 450, | ||||
|         }, | ||||
|         skirt => { | ||||
|             title => 'Skirt', | ||||
|             options => [qw(skirts skirt_distance skirt_height brim_width)], | ||||
|         }, | ||||
|         gcode => { | ||||
|             title => 'G-code', | ||||
|             options => [qw(start_gcode end_gcode layer_gcode gcode_comments post_process)], | ||||
|             label_width => 260, | ||||
|         }, | ||||
|         sequential_printing => { | ||||
|             title => 'Sequential printing', | ||||
|             options => [qw(complete_objects extruder_clearance_radius extruder_clearance_height)], | ||||
|         }, | ||||
|         extrusion => { | ||||
|             title => 'Extrusion', | ||||
|             options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width support_material_extrusion_width bridge_flow_ratio)], | ||||
|         }, | ||||
|         output => { | ||||
|             title => 'Output', | ||||
|             options => [qw(output_filename_format duplicate_distance)], | ||||
|         }, | ||||
|         other => { | ||||
|             title => 'Other', | ||||
|             options => [ ($Slic3r::have_threads ? qw(threads) : ()), qw(extra_perimeters) ], | ||||
|         }, | ||||
|         notes => { | ||||
|             title => 'Notes', | ||||
|             options => [qw(notes)], | ||||
|         }, | ||||
|         support_material => { | ||||
|             title => 'Support material', | ||||
|             options => [qw(support_material support_material_threshold support_material_pattern support_material_spacing support_material_angle support_material_extruder)], | ||||
|         }, | ||||
|     ); | ||||
|     $self->{panels} = \%panels; | ||||
|      | ||||
|     my $tabpanel = Wx::Notebook->new($self, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, &Wx::wxNB_TOP); | ||||
|     my $make_tab = sub { | ||||
|         my @cols = @_; | ||||
|          | ||||
|         my $tab = Wx::Panel->new($tabpanel, -1); | ||||
|         my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         foreach my $col (@cols) { | ||||
|             my $vertical_sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|             for my $optgroup (@$col) { | ||||
|                 next unless @{ $panels{$optgroup}{options} }; | ||||
|                 my $optpanel = Slic3r::GUI::OptionsGroup->new($tab, %{$panels{$optgroup}}); | ||||
|                 $vertical_sizer->Add($optpanel, 0, wxEXPAND | wxALL, 10); | ||||
|             } | ||||
|             $sizer->Add($vertical_sizer); | ||||
|         } | ||||
|          | ||||
|         $tab->SetSizer($sizer); | ||||
|         return $tab; | ||||
|     my $tabpanel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP); | ||||
|     $tabpanel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($tabpanel), "Plater"); | ||||
|     $self->{options_tabs} = { | ||||
|         print       => Slic3r::GUI::Tab::Print->new     ($tabpanel, sync_presets_with => $self->{plater}{preset_choosers}{print}), | ||||
|         filament    => Slic3r::GUI::Tab::Filament->new  ($tabpanel, sync_presets_with => $self->{plater}{preset_choosers}{filament}), | ||||
|         printer     => Slic3r::GUI::Tab::Printer->new   ($tabpanel, sync_presets_with => $self->{plater}{preset_choosers}{printer}), | ||||
|     }; | ||||
|      | ||||
|     my @tabs = ( | ||||
|         $make_tab->([qw(accuracy skirt support_material)], [qw(print retract)]), | ||||
|         $make_tab->([qw(cooling notes)]), | ||||
|         $make_tab->([qw(printer filament)], [qw(print_speed speed)]), | ||||
|         $make_tab->([qw(gcode)]), | ||||
|         $make_tab->([qw(extrusion)], [qw(output other sequential_printing)]), | ||||
|     ); | ||||
|      | ||||
|     $tabpanel->AddPage(Slic3r::GUI::Plater->new($tabpanel), "Plater"); | ||||
|     $tabpanel->AddPage($tabs[0], "Print Settings"); | ||||
|     $tabpanel->AddPage($tabs[1], "Cooling"); | ||||
|     $tabpanel->AddPage($tabs[2], "Printer and Filament"); | ||||
|     $tabpanel->AddPage($tabs[3], "G-code"); | ||||
|     $tabpanel->AddPage($tabs[4], "Advanced"); | ||||
|          | ||||
|     my $buttons_sizer; | ||||
|     { | ||||
|         $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|          | ||||
|         my $slice_button = Wx::Button->new($self, -1, "Quick slice…"); | ||||
|         $slice_button->SetDefault(); | ||||
|         $buttons_sizer->Add($slice_button, 0, wxRIGHT, 20); | ||||
|         EVT_BUTTON($self, $slice_button, sub { $self->do_slice }); | ||||
|          | ||||
|         my $save_button = Wx::Button->new($self, -1, "Save config..."); | ||||
|         $buttons_sizer->Add($save_button, 0, wxRIGHT, 5); | ||||
|         EVT_BUTTON($self, $save_button, sub { $self->save_config }); | ||||
|          | ||||
|         my $load_button = Wx::Button->new($self, -1, "Load config..."); | ||||
|         $buttons_sizer->Add($load_button, 0, wxRIGHT, 5); | ||||
|         EVT_BUTTON($self, $load_button, sub { $self->load_config }); | ||||
|          | ||||
|         my $text = Wx::StaticText->new($self, -1, "Remember to check for updates at http://slic3r.org/\nVersion: $Slic3r::VERSION", Wx::wxDefaultPosition, Wx::wxDefaultSize, wxALIGN_RIGHT); | ||||
|         my $font = Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL); | ||||
|         $text->SetFont($font); | ||||
|         $buttons_sizer->Add($text, 1, wxEXPAND | wxALIGN_RIGHT); | ||||
|     } | ||||
|     $tabpanel->AddPage($self->{options_tabs}{print}, $self->{options_tabs}{print}->title); | ||||
|     $tabpanel->AddPage($self->{options_tabs}{filament}, $self->{options_tabs}{filament}->title); | ||||
|     $tabpanel->AddPage($self->{options_tabs}{printer}, $self->{options_tabs}{printer}->title); | ||||
|      | ||||
|     my $sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $sizer->Add($buttons_sizer, 0, wxEXPAND | wxALL, 10); | ||||
|     $sizer->Add($tabpanel); | ||||
|     $sizer->Add($tabpanel, 1, wxEXPAND); | ||||
|      | ||||
|     $sizer->SetSizeHints($self); | ||||
|     $self->SetSizer($sizer); | ||||
|     $self->Layout; | ||||
|      | ||||
|     $_->() for @Slic3r::GUI::OptionsGroup::reload_callbacks; | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
|  | @ -182,7 +60,7 @@ sub do_slice { | |||
|         $copies = $Slic3r::duplicate if $Slic3r::duplicate > 1; | ||||
|         if ($copies > 1) { | ||||
|             my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", | ||||
|                                                       'Confirm', wxICON_QUESTION | wxOK | wxCANCEL); | ||||
|                                                       'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); | ||||
|             return unless $confirmation->ShowModal == wxID_OK; | ||||
|         } | ||||
|          | ||||
|  | @ -191,23 +69,23 @@ sub do_slice { | |||
| 
 | ||||
|         my $input_file; | ||||
|         if (!$params{reslice}) { | ||||
|             my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", $model_wildcard, wxFD_OPEN | &Wx::wxFD_FILE_MUST_EXIST); | ||||
|             my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", $model_wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); | ||||
|             if ($dialog->ShowModal != wxID_OK) { | ||||
|                 $dialog->Destroy; | ||||
|                 return; | ||||
|             } | ||||
|             $input_file = $dialog->GetPaths; | ||||
|             $dialog->Destroy; | ||||
|             $last_input_file = $input_file; | ||||
|             $last_input_file = $input_file unless $params{export_svg}; | ||||
|         } else { | ||||
|             if (!defined $last_input_file) { | ||||
|                 Wx::MessageDialog->new($self, "No previously sliced file", | ||||
|                                        'Confirm', wxICON_ERROR | wxOK)->ShowModal(); | ||||
|                 Wx::MessageDialog->new($self, "No previously sliced file.", | ||||
|                                        'Error', wxICON_ERROR | wxOK)->ShowModal(); | ||||
|                 return; | ||||
|             } | ||||
|             if (! -e $last_input_file) { | ||||
|                 Wx::MessageDialog->new($self, "Cannot find previously sliced file!", | ||||
|                                        'Confirm', wxICON_ERROR | wxOK)->ShowModal(); | ||||
|                 Wx::MessageDialog->new($self, "Previously sliced file ($last_input_file) not found.", | ||||
|                                        'File Not Found', wxICON_ERROR | wxOK)->ShowModal(); | ||||
|                 return; | ||||
|             } | ||||
|             $input_file = $last_input_file; | ||||
|  | @ -232,12 +110,13 @@ sub do_slice { | |||
|                 $dlg->Destroy; | ||||
|                 return; | ||||
|             } | ||||
|             $output_file = $last_output_file = $dlg->GetPath; | ||||
|             $output_file = $dlg->GetPath; | ||||
|             $last_output_file = $output_file unless $params{export_svg}; | ||||
|             $dlg->Destroy; | ||||
|         } | ||||
|          | ||||
|         # show processbar dialog | ||||
|         $process_dialog = Wx::ProgressDialog->new('Slicing...', "Processing $input_file_basename...",  | ||||
|         $process_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…",  | ||||
|             100, $self, 0); | ||||
|         $process_dialog->Pulse; | ||||
|          | ||||
|  | @ -249,7 +128,7 @@ sub do_slice { | |||
|                 status_cb   => sub { | ||||
|                     my ($percent, $message) = @_; | ||||
|                     if (&Wx::wxVERSION_STRING =~ / 2\.(8\.|9\.[2-9])/) { | ||||
|                         $process_dialog->Update($percent, "$message..."); | ||||
|                         $process_dialog->Update($percent, "$message…"); | ||||
|                     } | ||||
|                 }, | ||||
|             ); | ||||
|  | @ -264,13 +143,15 @@ sub do_slice { | |||
|         undef $process_dialog; | ||||
|          | ||||
|         my $message = "$input_file_basename was successfully sliced"; | ||||
|         $message .= sprintf " in %d minutes and %.3f seconds", | ||||
|             int($print->processing_time/60), | ||||
|             $print->processing_time - int($print->processing_time/60)*60 | ||||
|                 if $print->processing_time; | ||||
|         if ($print->processing_time) { | ||||
|             $message .= ' in'; | ||||
|             my $minutes = int($print->processing_time/60); | ||||
|             $message .= sprintf " %d minutes and", $minutes if $minutes; | ||||
|             $message .= sprintf " %.1f seconds", $print->processing_time - $minutes*60; | ||||
|         } | ||||
|         $message .= "."; | ||||
|         Slic3r::GUI::notify($message); | ||||
|         Wx::MessageDialog->new($self, $message, 'Done!',  | ||||
|         &Wx::wxTheApp->notify($message); | ||||
|         Wx::MessageDialog->new($self, $message, 'Slicing Done!',  | ||||
|             wxOK | wxICON_INFORMATION)->ShowModal; | ||||
|     }; | ||||
|     Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog }); | ||||
|  | @ -289,7 +170,7 @@ sub save_config { | |||
|     my $dir = $last_config ? dirname($last_config) : $last_config_dir || $last_skein_dir || ""; | ||||
|     my $filename = $last_config ? basename($last_config) : "config.ini"; | ||||
|     my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,  | ||||
|         $ini_wildcard, wxFD_SAVE | &Wx::wxFD_OVERWRITE_PROMPT); | ||||
|         $ini_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | ||||
|     if ($dlg->ShowModal == wxID_OK) { | ||||
|         my $file = $dlg->GetPath; | ||||
|         $last_config_dir = dirname($file); | ||||
|  | @ -301,22 +182,44 @@ sub save_config { | |||
| 
 | ||||
| sub load_config { | ||||
|     my $self = shift; | ||||
|     my ($file) = @_; | ||||
|      | ||||
|     my $dir = $last_config ? dirname($last_config) : $last_config_dir || $last_skein_dir || ""; | ||||
|     my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",  | ||||
|         $ini_wildcard, wxFD_OPEN | &Wx::wxFD_FILE_MUST_EXIST); | ||||
|     if ($dlg->ShowModal == wxID_OK) { | ||||
|         my ($file) = $dlg->GetPaths; | ||||
|         $last_config_dir = dirname($file); | ||||
|         $last_config = $file; | ||||
|         eval { | ||||
|             local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); | ||||
|             Slic3r::Config->load($file); | ||||
|         }; | ||||
|         Slic3r::GUI::catch_error($self); | ||||
|         $_->() for @Slic3r::GUI::OptionsGroup::reload_callbacks; | ||||
|     if (!$file) { | ||||
|         return unless $self->check_unsaved_changes; | ||||
|         my $dir = $last_config ? dirname($last_config) : $last_config_dir || $last_skein_dir || ""; | ||||
|         my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",  | ||||
|                 $ini_wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); | ||||
|         return unless $dlg->ShowModal == wxID_OK; | ||||
|         ($file) = $dlg->GetPaths; | ||||
|         $dlg->Destroy; | ||||
|     } | ||||
|     $dlg->Destroy; | ||||
|     $last_config_dir = dirname($file); | ||||
|     $last_config = $file; | ||||
|     $_->external_config_loaded($file) for values %{$self->{options_tabs}}; | ||||
| } | ||||
| 
 | ||||
| sub config_wizard { | ||||
|     my $self = shift; | ||||
| 
 | ||||
|     return unless $self->check_unsaved_changes; | ||||
|     if (Slic3r::GUI::ConfigWizard->new($self)->run) { | ||||
|         $_->() for values %Slic3r::GUI::OptionsGroup::reload_callbacks; | ||||
|         $_->set_dirty(1) for values %{$self->{options_tabs}}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub check_unsaved_changes { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my @dirty = map $_->title, grep $_->is_dirty, values %{$self->{options_tabs}}; | ||||
|     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; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
							
								
								
									
										556
									
								
								lib/Slic3r/GUI/Tab.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										556
									
								
								lib/Slic3r/GUI/Tab.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,556 @@ | |||
| package Slic3r::GUI::Tab; | ||||
| use strict; | ||||
| use warnings; | ||||
| use utf8; | ||||
| 
 | ||||
| use File::Basename qw(basename); | ||||
| use List::Util qw(first); | ||||
| use Wx qw(:bookctrl :dialog :icon :id :misc :sizer :treectrl :window); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_CHOICE EVT_TREE_SEL_CHANGED); | ||||
| use base 'Wx::Panel'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $title, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxBK_LEFT); | ||||
|      | ||||
|     $self->{title} = $title; | ||||
|      | ||||
|     $self->{sync_presets_with} = $params{sync_presets_with}; | ||||
|     EVT_CHOICE($parent, $self->{sync_presets_with}, sub { | ||||
|         $self->{presets_choice}->SetSelection($self->{sync_presets_with}->GetSelection); | ||||
|         $self->on_select_preset; | ||||
|     }); | ||||
|      | ||||
|     # horizontal sizer | ||||
|     $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|     $self->{sizer}->SetSizeHints($self); | ||||
|     $self->SetSizer($self->{sizer}); | ||||
|      | ||||
|     # left vertical sizer | ||||
|     my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3); | ||||
|      | ||||
|     my $left_col_width = 150; | ||||
|      | ||||
|     # preset chooser | ||||
|     { | ||||
|          | ||||
|         # choice menu | ||||
|         $self->{presets_choice} = Wx::Choice->new($self, -1, wxDefaultPosition, [$left_col_width, -1], []); | ||||
|         $self->{presets_choice}->SetFont($Slic3r::GUI::small_font); | ||||
|          | ||||
|         # buttons | ||||
|         $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG)); | ||||
|         $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG)); | ||||
|         $self->{btn_save_preset}->SetToolTipString("Save current " . lc($title)); | ||||
|         $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); | ||||
|         $self->{btn_delete_preset}->Disable; | ||||
|          | ||||
|         ### These cause GTK warnings: | ||||
|         ###my $box = Wx::StaticBox->new($self, -1, "Presets:", wxDefaultPosition, [$left_col_width, 50]); | ||||
|         ###my $hsizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL); | ||||
|          | ||||
|         my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|          | ||||
|         $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); | ||||
|     } | ||||
|      | ||||
|     # tree | ||||
|     $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); | ||||
|     $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); | ||||
|     EVT_TREE_SEL_CHANGED($parent, $self->{treectrl}, sub { | ||||
|         $_->Hide for values %{$self->{pages}}; | ||||
|         $self->{sizer}->Remove(1); | ||||
|         my $page = $self->{pages}->{ $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection) }; | ||||
|         $page->Show; | ||||
|         $self->{sizer}->Add($page, 1, wxEXPAND | wxLEFT, 5); | ||||
|         $self->{sizer}->Layout; | ||||
|     }); | ||||
|      | ||||
|     EVT_CHOICE($parent, $self->{presets_choice}, sub { | ||||
|         $self->on_select_preset; | ||||
|         $self->sync_presets; | ||||
|     }); | ||||
|      | ||||
|     EVT_BUTTON($self, $self->{btn_save_preset}, sub { | ||||
|         my $preset = $self->current_preset; | ||||
|         my $default_name = $preset->{default} ? 'Untitled' : basename($preset->{name}); | ||||
|         $default_name =~ s/\.ini$//i; | ||||
|          | ||||
|         my $dlg = Slic3r::GUI::SavePresetWindow->new($self, | ||||
|             title   => lc($title), | ||||
|             default => $default_name, | ||||
|             values  => [ map { my $name = $_->{name}; $name =~ s/\.ini$//i; $name } @{$self->{presets}} ], | ||||
|         ); | ||||
|         return unless $dlg->ShowModal == wxID_OK; | ||||
|          | ||||
|         my $file = sprintf "$Slic3r::GUI::datadir/$self->{presets_group}/%s.ini", $dlg->get_name; | ||||
|         Slic3r::Config->save($file, $self->{presets_group}); | ||||
|         $self->set_dirty(0); | ||||
|         $self->load_presets; | ||||
|         $self->{presets_choice}->SetSelection(first { basename($self->{presets}[$_]{file}) eq $dlg->get_name . ".ini" } 1 .. $#{$self->{presets}}); | ||||
|         $self->on_select_preset; | ||||
|         $self->sync_presets; | ||||
|     }); | ||||
|      | ||||
|     EVT_BUTTON($self, $self->{btn_delete_preset}, sub { | ||||
|         my $i = $self->{presets_choice}->GetSelection; | ||||
|         return if $i == 0;  # this shouldn't happen but let's trap it anyway | ||||
|         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; | ||||
|         if (-e $self->{presets}[$i-1]{file}) { | ||||
|             unlink $self->{presets}[$i-1]{file}; | ||||
|         } | ||||
|         splice @{$self->{presets}}, $i-1, 1; | ||||
|         $self->{presets_choice}->Delete($i); | ||||
|         $self->{presets_choice}->SetSelection(0); | ||||
|         $self->on_select_preset; | ||||
|         $self->sync_presets; | ||||
|     }); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub current_preset { | ||||
|     my $self = shift; | ||||
|     return $self->{presets}[ $self->{presets_choice}->GetSelection ]; | ||||
| } | ||||
| 
 | ||||
| sub on_select_preset { | ||||
|     my $self = shift; | ||||
|      | ||||
|     if (defined $self->{dirty}) { | ||||
|         # TODO: prompt user? | ||||
|         $self->set_dirty(0); | ||||
|     } | ||||
|      | ||||
|     my $preset = $self->current_preset; | ||||
|     if ($preset->{default}) { | ||||
|         # default settings: disable the delete button | ||||
|         Slic3r::Config->load_hash($Slic3r::Defaults, $self->{presets_group}, 1); | ||||
|         $self->{btn_delete_preset}->Disable; | ||||
|     } else { | ||||
|         if (!-e $preset->{file}) { | ||||
|             Slic3r::GUI::show_error($self, "The selected preset does not exist anymore ($preset->{file})."); | ||||
|             return; | ||||
|         } | ||||
|         eval { | ||||
|             local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); | ||||
|             Slic3r::Config->load($preset->{file}, $self->{presets_group}); | ||||
|         }; | ||||
|         Slic3r::GUI::catch_error($self); | ||||
|         $preset->{external} | ||||
|             ? $self->{btn_delete_preset}->Disable | ||||
|             : $self->{btn_delete_preset}->Enable; | ||||
|     } | ||||
|     $_->() for @Slic3r::GUI::OptionsGroup::reload_callbacks{@{$Slic3r::Config::Groups{$self->{presets_group}}}}; | ||||
|     $self->set_dirty(0); | ||||
|     $Slic3r::Settings->{presets}{$self->{presets_group}} = $preset->{file} ? basename($preset->{file}) : ''; | ||||
|     Slic3r::Config->save_settings("$Slic3r::GUI::datadir/slic3r.ini"); | ||||
| } | ||||
| 
 | ||||
| sub add_options_page { | ||||
|     my $self = shift; | ||||
|     my $title = shift; | ||||
|     my $icon = (ref $_[1]) ? undef : shift; | ||||
|     my $page = Slic3r::GUI::Tab::Page->new($self, @_, on_change => sub { | ||||
|         $self->set_dirty(1); | ||||
|         $self->sync_presets; | ||||
|     }); | ||||
|      | ||||
|     my $bitmap = $icon | ||||
|         ? Wx::Bitmap->new("$Slic3r::var/$icon", wxBITMAP_TYPE_PNG) | ||||
|         : undef; | ||||
|     if ($bitmap) { | ||||
|         $self->{icons}->Add($bitmap); | ||||
|         $self->{iconcount}++; | ||||
|     } | ||||
|     $page->Hide; | ||||
|     my $itemId = $self->{treectrl}->AppendItem($self->{treectrl}->GetRootItem, $title, $self->{iconcount}); | ||||
|     $self->{pages}{$title} = $page; | ||||
|     if (keys %{$self->{pages}} == 1) { | ||||
|         $self->{treectrl}->SelectItem($itemId); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub set_dirty { | ||||
|     my $self = shift; | ||||
|     my ($dirty) = @_; | ||||
|      | ||||
|     my $i = $self->{dirty} // $self->{presets_choice}->GetSelection; #/ | ||||
|     my $text = $self->{presets_choice}->GetString($i); | ||||
|      | ||||
|     if ($dirty) { | ||||
|         $self->{dirty} = $i; | ||||
|         if ($text !~ / \(modified\)$/) { | ||||
|             $self->{presets_choice}->SetString($i, "$text (modified)"); | ||||
|             $self->{presets_choice}->SetSelection($i);  # wxMSW needs this after every SetString() | ||||
|         } | ||||
|     } else { | ||||
|         $self->{dirty} = undef; | ||||
|         $text =~ s/ \(modified\)$//; | ||||
|         $self->{presets_choice}->SetString($i, $text); | ||||
|         $self->{presets_choice}->SetSelection($i);  # wxMSW needs this after every SetString() | ||||
|     } | ||||
|     $self->sync_presets; | ||||
| } | ||||
| 
 | ||||
| sub is_dirty { | ||||
|     my $self = shift; | ||||
|     return (defined $self->{dirty}); | ||||
| } | ||||
| 
 | ||||
| sub title { | ||||
|     my $self = shift; | ||||
|     return $self->{title}; | ||||
| } | ||||
| 
 | ||||
| sub load_presets { | ||||
|     my $self = shift; | ||||
|     my ($group) = @_; | ||||
|      | ||||
|     $self->{presets_group} ||= $group; | ||||
|     $self->{presets} = [{ | ||||
|         default => 1, | ||||
|         name    => '- default -', | ||||
|     }]; | ||||
|      | ||||
|     opendir my $dh, "$Slic3r::GUI::datadir/$self->{presets_group}" or die "Failed to read directory $Slic3r::GUI::datadir/$self->{presets_group} (errno: $!)\n"; | ||||
|     foreach my $file (sort grep /\.ini$/i, readdir $dh) { | ||||
|         my $name = basename($file); | ||||
|         $name =~ s/\.ini$//; | ||||
|         push @{$self->{presets}}, { | ||||
|             file => "$Slic3r::GUI::datadir/$self->{presets_group}/$file", | ||||
|             name => $name, | ||||
|         }; | ||||
|     } | ||||
|     closedir $dh; | ||||
|      | ||||
|     $self->{presets_choice}->Clear; | ||||
|     $self->{presets_choice}->Append($_->{name}) for @{$self->{presets}}; | ||||
|     { | ||||
|         # load last used preset | ||||
|         my $i = first { basename($self->{presets}[$_]{file}) eq ($Slic3r::Settings->{presets}{$self->{presets_group}} || '') } 1 .. $#{$self->{presets}}; | ||||
|         $self->{presets_choice}->SetSelection($i || 0); | ||||
|         $self->on_select_preset; | ||||
|     } | ||||
|     $self->sync_presets; | ||||
| } | ||||
| 
 | ||||
| sub external_config_loaded { | ||||
|     my $self = shift; | ||||
|     my ($file) = @_; | ||||
|      | ||||
|     # 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 | ||||
|         push @{$self->{presets}}, { | ||||
|             file        => $file, | ||||
|             name        => $preset_name, | ||||
|             external    => 1, | ||||
|         }; | ||||
|         $self->{presets_choice}->Append($preset_name); | ||||
|         $i = $#{$self->{presets}}; | ||||
|     } | ||||
|     $self->{presets_choice}->SetSelection($i); | ||||
|     $self->on_select_preset; | ||||
|     $self->sync_presets; | ||||
| } | ||||
| 
 | ||||
| sub sync_presets { | ||||
|     my $self = shift; | ||||
|     return unless $self->{sync_presets_with}; | ||||
|     $self->{sync_presets_with}->Clear; | ||||
|     foreach my $item ($self->{presets_choice}->GetStrings) { | ||||
|         $self->{sync_presets_with}->Append($item); | ||||
|     } | ||||
|     $self->{sync_presets_with}->SetSelection($self->{presets_choice}->GetSelection); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Tab::Print; | ||||
| use base 'Slic3r::GUI::Tab'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Print Settings', %params); | ||||
|      | ||||
|     $self->add_options_page('Layers and perimeters', 'layers.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Layer height', | ||||
|             options => [qw(layer_height first_layer_height)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Vertical shells', | ||||
|             options => [qw(perimeters randomize_start extra_perimeters)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Horizontal shells', | ||||
|             options => [qw(solid_layers)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Infill', 'shading.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Infill', | ||||
|             options => [qw(fill_density fill_angle fill_pattern solid_fill_pattern infill_every_layers)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Speed', 'time.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Speed for print moves', | ||||
|             options => [qw(perimeter_speed small_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Speed for non-print moves', | ||||
|             options => [qw(travel_speed)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Modifiers', | ||||
|             options => [qw(first_layer_speed)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Skirt and brim', 'box.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Skirt', | ||||
|             options => [qw(skirts skirt_distance skirt_height)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Brim', | ||||
|             options => [qw(brim_width)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Support material', 'building.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Support material', | ||||
|             options => [qw(support_material support_material_threshold support_material_pattern support_material_spacing support_material_angle)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Notes', 'note.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Notes', | ||||
|             no_labels => 1, | ||||
|             options => [qw(notes)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Output options', 'page_white_go.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Sequential printing', | ||||
|             options => [qw(complete_objects extruder_clearance_radius extruder_clearance_height)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Output file', | ||||
|             options => [qw(gcode_comments output_filename_format)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Post-processing scripts', | ||||
|             no_labels => 1, | ||||
|             options => [qw(post_process)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Advanced', 'wrench.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Extrusion width', | ||||
|             label_width => 180, | ||||
|             options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Flow', | ||||
|             options => [qw(bridge_flow_ratio)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Other', | ||||
|             options => [qw(duplicate_distance), ($Slic3r::have_threads ? qw(threads) : ())], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->load_presets('print'); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Tab::Filament; | ||||
| use base 'Slic3r::GUI::Tab'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Filament Settings', %params); | ||||
|      | ||||
|     $self->add_options_page('Filament', 'spool.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Filament', | ||||
|             options => ['filament_diameter#0', 'extrusion_multiplier#0'], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Temperature', | ||||
|             options => ['temperature#0', 'first_layer_temperature#0', qw(bed_temperature first_layer_bed_temperature)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Cooling', 'hourglass.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Enable', | ||||
|             options => [qw(cooling)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Fan settings', | ||||
|             options => [qw(min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers fan_always_on)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Cooling thresholds', | ||||
|             label_width => 250, | ||||
|             options => [qw(fan_below_layer_time slowdown_below_layer_time min_print_speed)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->load_presets('filament'); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Tab::Printer; | ||||
| use base 'Slic3r::GUI::Tab'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, 'Printer Settings', %params); | ||||
|      | ||||
|     $self->add_options_page('General', 'printer_empty.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Size and coordinates', | ||||
|             options => [qw(bed_size print_center z_offset)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Firmware', | ||||
|             options => [qw(gcode_flavor use_relative_e_distances)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Extruder 1', 'funnel.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Size', | ||||
|             options => ['nozzle_diameter#0'], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Retraction', | ||||
|             options => [qw(retract_length retract_lift retract_speed retract_restart_extra retract_before_travel)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->add_options_page('Custom G-code', 'cog.png', optgroups => [ | ||||
|         { | ||||
|             title => 'Start G-code', | ||||
|             no_labels => 1, | ||||
|             options => [qw(start_gcode)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'End G-code', | ||||
|             no_labels => 1, | ||||
|             options => [qw(end_gcode)], | ||||
|         }, | ||||
|         { | ||||
|             title => 'Layer change G-code', | ||||
|             no_labels => 1, | ||||
|             options => [qw(layer_gcode)], | ||||
|         }, | ||||
|     ]); | ||||
|      | ||||
|     $self->load_presets('printer'); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Tab::Page; | ||||
| use Wx qw(:sizer); | ||||
| use base 'Wx::ScrolledWindow'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1); | ||||
|      | ||||
|     $self->SetScrollbars(1, 1, 1, 1); | ||||
|      | ||||
|     $self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $self->SetSizer($self->{vsizer}); | ||||
|      | ||||
|     if ($params{optgroups}) { | ||||
|         $self->append_optgroup(%$_, on_change => $params{on_change}) for @{$params{optgroups}}; | ||||
|     } | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub append_optgroup { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $optgroup = Slic3r::GUI::OptionsGroup->new($self, label_width => 200, @_); | ||||
|     $self->{vsizer}->Add($optgroup, 0, wxEXPAND | wxALL, 5); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::SavePresetWindow; | ||||
| use Wx qw(:combobox :dialog :id :misc :sizer); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); | ||||
| use base 'Wx::Dialog'; | ||||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, %params) = @_; | ||||
|     my $self = $class->SUPER::new($parent, -1, "Save preset", wxDefaultPosition, wxDefaultSize); | ||||
|      | ||||
|     my $text = Wx::StaticText->new($self, -1, "Save " . lc($params{title}) . " as:", wxDefaultPosition, wxDefaultSize); | ||||
|     $self->{combo} = Wx::ComboBox->new($self, -1, $params{default}, wxDefaultPosition, wxDefaultSize, $params{values}, | ||||
|                                        wxTE_PROCESS_ENTER); | ||||
|     my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL); | ||||
|      | ||||
|     my $sizer = Wx::BoxSizer->new(wxVERTICAL); | ||||
|     $sizer->Add($text, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); | ||||
|     $sizer->Add($self->{combo}, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); | ||||
|     $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); | ||||
|      | ||||
|     EVT_BUTTON($self, wxID_OK, \&accept); | ||||
|     EVT_TEXT_ENTER($self, $self->{combo}, \&accept); | ||||
|      | ||||
|     $self->SetSizer($sizer); | ||||
|     $sizer->SetSizeHints($self); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub accept { | ||||
|     my ($self, $event) = @_; | ||||
| 
 | ||||
|     if (($self->{chosen_name} = $self->{combo}->GetValue)) { | ||||
|         if ($self->{chosen_name} =~ /^[a-z0-9 _-]+$/i) { | ||||
|             $self->EndModal(wxID_OK); | ||||
|         } else { | ||||
|             Slic3r::GUI::show_error($self, "The supplied name is not valid."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub get_name { | ||||
|     my $self = shift; | ||||
|     return $self->{chosen_name}; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci