diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 76fccc5f..00f2c30d 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -75,20 +75,26 @@ sub About { } sub catch_error { - my ($self, $cb) = @_; + my ($self, $cb, $message_dialog) = @_; if (my $err = $@) { $cb->() if $cb; - Wx::MessageDialog->new($self, $err, 'Error', &Wx::wxOK | &Wx::wxICON_ERROR)->ShowModal; + my @params = ($err, 'Error', &Wx::wxOK | &Wx::wxICON_ERROR); + $message_dialog + ? $message_dialog->(@params) + : Wx::MessageDialog->new($self, @params)->ShowModal; return 1; } return 0; } sub warning_catcher { - my ($self) = @_; + my ($self, $message_dialog) = @_; return sub { my $message = shift; - Wx::MessageDialog->new($self, $message, 'Warning', &Wx::wxOK | &Wx::wxICON_WARNING)->ShowModal; + my @params = ($message, 'Warning', &Wx::wxOK | &Wx::wxICON_WARNING); + $message_dialog + ? $message_dialog->(@params) + : Wx::MessageDialog->new($self, @params)->ShowModal; }; } @@ -104,13 +110,19 @@ sub new { $self->{timer} = Wx::Timer->new($self); $self->{prog} = Wx::Gauge->new($self, &Wx::wxGA_HORIZONTAL, 100, [-1,-1], [-1,-1]); $self->{prog}->Hide; + $self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", [-1,-1], [-1,8]); + $self->{cancelbutton}->Hide; - $self->SetFieldsCount(2); - $self->SetStatusWidths(-1, 155); + $self->SetFieldsCount(3); + $self->SetStatusWidths(-1, 150, 155); Wx::Event::EVT_IDLE($self, sub { $self->_Reposition }); Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer}); Wx::Event::EVT_SIZE($self, \&OnSize); + Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub { + $self->{cancel_cb}->(); + $self->{cancelbutton}->Hide; + }); return $self; } @@ -124,11 +136,18 @@ sub _Reposition { my $self = shift; ##if ($self->{_changed}) { + { my $rect = $self->GetFieldRect($self->GetFieldsCount - 1); my $prog_pos = [$rect->GetX + 2, $rect->GetY + 2]; $self->{prog}->Move($prog_pos); $self->{prog}->SetSize($rect->GetWidth - 8, $rect->GetHeight - 4); - ##} + } + { + my $rect = $self->GetFieldRect($self->GetFieldsCount - 2); + my $pos = [$rect->GetX + 2, $rect->GetY + 2]; + $self->{cancelbutton}->Move($pos); + $self->{cancelbutton}->SetSize($rect->GetWidth - 8, $rect->GetHeight - 4); + } $self->{_changed} = 0; } @@ -149,6 +168,13 @@ sub OnTimer { $self->{prog}->Pulse if $self->{_busy}; } +sub SetCancelCallback { + my $self = shift; + my ($cb) = @_; + $self->{cancel_cb} = $cb; + $cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide; +} + sub Run { my $self = shift; my $rate = shift || 100; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 47b78220..2fa2db77 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -23,7 +23,10 @@ use constant TB_ROTATE => 5; use constant TB_SCALE => 6; use constant TB_SPLIT => 7; -my $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; +my $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; +my $PROGRESS_BAR_EVENT : shared = Wx::NewEventType; +my $MESSAGE_DIALOG_EVENT : shared = Wx::NewEventType; +my $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; sub new { my $class = shift; @@ -141,6 +144,23 @@ sub new { $self->make_thumbnail2; }); + EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { + my ($self, $event) = @_; + my ($percent, $message) = @{$event->GetData}; + $self->statusbar->SetProgress($percent); + $self->statusbar->SetStatusText("$message..."); + }); + + EVT_COMMAND($self, -1, $MESSAGE_DIALOG_EVENT, sub { + my ($self, $event) = @_; + Wx::MessageDialog->new($self, @{$event->GetData})->ShowModal; + }); + + EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub { + my ($self, $event) = @_; + $self->on_export_completed(@{$event->GetData}); + }); + $self->update_bed_size; $self->{print} = Slic3r::Print->new; $self->{thumbnails} = []; # polygons, each one aligned to 0,0 @@ -393,37 +413,80 @@ sub split_object { 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; + return; + } + + # select output file + $self->{output_file} = $main::opt{output}; + { + $self->{output_file} = $self->{print}->expanded_output_filepath($self->{output_file}); + my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}), + basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); + if ($dlg->ShowModal != wxID_OK) { + $dlg->Destroy; + return; + } + $self->{output_file} = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath; + $dlg->Destroy; + } + + $self->statusbar->StartBusy; + if ($have_threads) { + $self->{export_thread} = threads->create(sub { + $self->export_gcode2( + $self->{output_file}, + progressbar => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) }, + message_dialog => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) }, + on_completed => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, shared_clone([@_]))) }, + catch_error => sub { + Slic3r::GUI::catch_error($self, $_[0], sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) }); + }, + ); + }); + $self->statusbar->SetCancelCallback(sub { + $self->{export_thread}->kill('KILL'); + $self->{export_thread} = undef; + $self->statusbar->StopBusy; + $self->statusbar->SetStatusText("Export cancelled"); + }); + } else { + $self->export_gcode2( + $self->{output_file}, + progressbar => sub { + my ($percent, $message) = @_; + $self->statusbar->SetProgress($percent); + $self->statusbar->SetStatusText("$message..."); + }, + message_dialog => sub { Wx::MessageDialog->new($self, @_)->ShowModal }, + on_completed => sub { $self->on_export_completed(@_) }, + catch_error => sub { Slic3r::GUI::catch_error($self, @_) }, + ); + } +} + +sub export_gcode2 { + my $self = shift; + my ($output_file, %params) = @_; + $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; + local $SIG{'KILL'} = sub { + Slic3r::debugf "Exporting cancelled; exiting thread...\n"; + threads->exit(); + }; + eval { # validate configuration Slic3r::Config->validate; my $print = $self->{print}; - # select output file - my $output_file = $main::opt{output}; - { - $output_file = $print->expanded_output_filepath($output_file); - my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($output_file), - basename($output_file), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); - if ($dlg->ShowModal != wxID_OK) { - $dlg->Destroy; - return; - } - $output_file = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath; - $dlg->Destroy; - } - - $self->statusbar->StartBusy; { my @warnings = (); local $SIG{__WARN__} = sub { push @warnings, $_[0] }; my %params = ( output_file => $output_file, - status_cb => sub { - my ($percent, $message) = @_; - $self->statusbar->SetProgress($percent); - $self->statusbar->SetStatusText("$message..."); - }, + status_cb => sub { $params{progressbar}->(@_) }, keep_meshes => 1, ); if ($params{export_svg}) { @@ -431,9 +494,10 @@ sub export_gcode { } else { $print->export_gcode(%params); } - Slic3r::GUI::warning_catcher($self)->($_) for @warnings; + Slic3r::GUI::warning_catcher($self, sub { + Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))); + })->($_) for @warnings; } - $self->statusbar->StopBusy; my $message = "Your files were successfully sliced"; $message .= sprintf " in %d minutes and %.3f seconds", @@ -442,20 +506,28 @@ sub export_gcode { if $print->processing_time; $message .= "."; eval { + # TODO: fix it as we don't have $self->{growler} $self->{growler}->notify(Event => 'SKEIN_DONE', Title => 'Slicing Done!', Message => $message) if ($self->{growler}); }; - $self->statusbar->SetStatusText("G-code file exported to $output_file"); - Wx::MessageDialog->new($self, $message, 'Done!', - wxOK | wxICON_INFORMATION)->ShowModal; + $params{on_completed}->($message); $print->cleanup; }; - Slic3r::GUI::catch_error($self, sub { - $self->statusbar->StartBusy; + $params{catch_error}->(sub { + $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); }); } +sub on_export_completed { + my $self = shift; + my ($message) = @_; + + $self->statusbar->StopBusy; + $self->statusbar->SetStatusText("G-code file exported to $self->{output_file}"); + Wx::MessageDialog->new($self, $message, 'Done!', wxOK | wxICON_INFORMATION)->ShowModal; +} + sub export_stl { my $self = shift;