More work for background processing

This commit is contained in:
Alessandro Ranellucci 2014-06-13 14:27:55 +02:00
parent d9e7a50a6e
commit a0674714b1
2 changed files with 137 additions and 119 deletions

View file

@ -264,10 +264,9 @@ sub catch_error {
my ($self, $cb, $message_dialog) = @_; my ($self, $cb, $message_dialog) = @_;
if (my $err = $@) { if (my $err = $@) {
$cb->() if $cb; $cb->() if $cb;
my @params = ($err, 'Error', wxOK | wxICON_ERROR);
$message_dialog $message_dialog
? $message_dialog->(@params) ? $message_dialog->($err, 'Error', wxOK | wxICON_ERROR)
: Wx::MessageDialog->new($self, @params)->ShowModal; : Slic3r::GUI::show_error($self, $err);
return 1; return 1;
} }
return 0; return 0;

View file

@ -30,9 +30,8 @@ use constant TB_SETTINGS => &Wx::NewId;
# package variables to avoid passing lexicals to threads # package variables to avoid passing lexicals to threads
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType; our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType;
our $MESSAGE_DIALOG_EVENT : shared = Wx::NewEventType; our $ERROR_EVENT : shared = Wx::NewEventType;
our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType;
our $EXPORT_FAILED_EVENT : shared = Wx::NewEventType;
our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType; our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType;
use constant CANVAS_SIZE => [335,335]; use constant CANVAS_SIZE => [335,335];
@ -51,6 +50,16 @@ sub new {
$self->{print} = Slic3r::Print->new; $self->{print} = Slic3r::Print->new;
$self->{objects} = []; $self->{objects} = [];
$self->{print}->set_status_cb(sub {
my ($percent, $message) = @_;
if ($Slic3r::have_threads) {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message])));
} else {
$self->on_progress_event($percent, $message);
}
});
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self, CANVAS_SIZE, $self->{objects}, $self->{model}, $self->{config}); $self->{canvas} = Slic3r::GUI::Plater::2D->new($self, CANVAS_SIZE, $self->{objects}, $self->{model}, $self->{config});
$self->{canvas}->on_select_object(sub { $self->{canvas}->on_select_object(sub {
my ($obj_idx) = @_; my ($obj_idx) = @_;
@ -200,31 +209,22 @@ sub new {
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
my ($self, $event) = @_; my ($self, $event) = @_;
my ($percent, $message) = @{$event->GetData}; my ($percent, $message) = @{$event->GetData};
$self->statusbar->SetProgress($percent); $self->on_progress_event($percent, $message);
$self->statusbar->SetStatusText("$message…");
}); });
EVT_COMMAND($self, -1, $MESSAGE_DIALOG_EVENT, sub { EVT_COMMAND($self, -1, $ERROR_EVENT, sub {
my ($self, $event) = @_; my ($self, $event) = @_;
Wx::MessageDialog->new($self, @{$event->GetData})->ShowModal; Slic3r::GUI::show_error($self, @{$event->GetData});
}); });
EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub { EVT_COMMAND($self, -1, $EXPORT_COMPLETED_EVENT, sub {
my ($self, $event) = @_; my ($self, $event) = @_;
$self->on_export_completed(@{$event->GetData}); $self->on_export_completed($event->GetData);
});
EVT_COMMAND($self, -1, $EXPORT_FAILED_EVENT, sub {
my ($self, $event) = @_;
$self->on_export_failed;
}); });
EVT_COMMAND($self, -1, $PROCESS_COMPLETED_EVENT, sub { EVT_COMMAND($self, -1, $PROCESS_COMPLETED_EVENT, sub {
my ($self, $event) = @_; my ($self, $event) = @_;
$self->on_process_completed($event->GetData);
Slic3r::debugf "Background processing completed.\n";
$self->{process_thread}->detach if $self->{process_thread};
$self->{process_thread} = undef;
}); });
$self->{canvas}->update_bed_size; $self->{canvas}->update_bed_size;
@ -706,9 +706,23 @@ sub start_background_process {
} }
# start thread # start thread
@_ = ();
$self->{process_thread} = threads->create(sub { $self->{process_thread} = threads->create(sub {
local $SIG{'KILL'} = sub {
Slic3r::debugf "Background process cancelled; exiting thread...\n";
Slic3r::thread_cleanup();
threads->exit();
};
eval {
$self->{print}->process; $self->{print}->process;
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROCESS_COMPLETED_EVENT, undef)); };
if ($@) {
Slic3r::debugf "Discarding background process error: $@\n";
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROCESS_COMPLETED_EVENT, 0));
} else {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROCESS_COMPLETED_EVENT, 1));
}
Slic3r::thread_cleanup(); Slic3r::thread_cleanup();
}); });
Slic3r::debugf "Background processing started.\n"; Slic3r::debugf "Background processing started.\n";
@ -717,6 +731,10 @@ sub start_background_process {
sub stop_background_process { sub stop_background_process {
my ($self) = @_; my ($self) = @_;
$self->statusbar->SetCancelCallback(undef);
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("");
if ($self->{process_thread}) { if ($self->{process_thread}) {
Slic3r::debugf "Killing background process.\n"; Slic3r::debugf "Killing background process.\n";
$self->{process_thread}->kill('KILL')->join; $self->{process_thread}->kill('KILL')->join;
@ -724,19 +742,32 @@ sub stop_background_process {
} else { } else {
Slic3r::debugf "No background process running.\n"; Slic3r::debugf "No background process running.\n";
} }
# if there's an export process, kill that one as well
if ($self->{export_thread}) {
Slic3r::debugf "Killing background export process.\n";
$self->{export_thread}->kill('KILL')->join;
$self->{export_thread} = undef;
}
} }
sub export_gcode { sub export_gcode {
my $self = shift; my $self = shift;
if ($self->{export_gcode_output_file}) { if ($self->{export_gcode_output_file}) {
Wx::MessageDialog->new($self, "Another slicing job is currently running.", 'Error', wxOK | wxICON_ERROR)->ShowModal; Wx::MessageDialog->new($self, "Another export job is currently running.", 'Error', wxOK | wxICON_ERROR)->ShowModal;
return; return;
} }
# It looks like declaring a local $SIG{__WARN__} prevents the ugly # if process is not running, validate config
# "Attempt to free unreferenced scalar" warning... # (we assume that if it is running, config is valid)
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); eval {
# this will throw errors if config is not valid
$self->skeinpanel->config->validate;
$self->{print}->validate;
};
Slic3r::GUI::catch_error($self) and return;
# apply config and validate print # apply config and validate print
my $config = $self->skeinpanel->config; my $config = $self->skeinpanel->config;
@ -746,133 +777,121 @@ sub export_gcode {
$self->{print}->apply_config($config); $self->{print}->apply_config($config);
$self->{print}->validate; $self->{print}->validate;
}; };
if (!$Slic3r::have_threads) {
Slic3r::GUI::catch_error($self) and return; Slic3r::GUI::catch_error($self) and return;
# apply extra variables
{
my $extra = $self->skeinpanel->extra_variables;
$self->{print}->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
} }
# select output file # select output file
$self->{output_file} = $main::opt{output}; $self->{export_gcode_output_file} = $main::opt{output};
{ {
my $output_file = $self->{print}->expanded_output_filepath($self->{output_file}); my $default_output_file = $self->{print}->expanded_output_filepath($self->{export_gcode_output_file});
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', Slic3r::GUI->output_path(dirname($output_file)), my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', Slic3r::GUI->output_path(dirname($default_output_file)),
basename($output_file), &Slic3r::GUI::SkeinPanel::FILE_WILDCARDS->{gcode}, wxFD_SAVE); basename($default_output_file), &Slic3r::GUI::SkeinPanel::FILE_WILDCARDS->{gcode}, wxFD_SAVE);
if ($dlg->ShowModal != wxID_OK) { if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy; $dlg->Destroy;
$self->{export_gcode_output_file} = undef;
return; return;
} }
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($dlg->GetPath); $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($dlg->GetPath);
Slic3r::GUI->save_settings; Slic3r::GUI->save_settings;
$self->{output_file} = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath; $self->{export_gcode_output_file} = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath;
$dlg->Destroy; $dlg->Destroy;
} }
$self->statusbar->StartBusy; $self->statusbar->StartBusy;
if ($Slic3r::have_threads) { if ($Slic3r::have_threads) {
@_ = ();
# some perls (including 5.14.2) crash on threads->exit if we pass lexicals to the thread
our $_thread_self = $self;
$self->{export_thread} = threads->create(sub {
$_thread_self->export_gcode2(
$_thread_self->{output_file},
progressbar => sub { Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) },
message_dialog => sub { Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) },
on_completed => sub { Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, shared_clone([@_]))) },
catch_error => sub {
Slic3r::GUI::catch_error($_thread_self, $_[0], sub {
Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_])));
Wx::PostEvent($_thread_self, Wx::PlThreadEvent->new(-1, $EXPORT_FAILED_EVENT, undef));
});
},
);
Slic3r::thread_cleanup();
});
$self->statusbar->SetCancelCallback(sub { $self->statusbar->SetCancelCallback(sub {
$self->{export_thread}->kill('KILL')->join; $self->stop_background_process;
$self->{export_thread} = undef;
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("Export cancelled"); $self->statusbar->SetStatusText("Export cancelled");
}); });
# start background process, whose completion event handler
# will detect $self->{export_gcode_output_file} and proceed with export
$self->start_background_process;
} else { } else {
$self->export_gcode2( eval {
$self->{output_file}, $self->{print}->process;
progressbar => sub { $self->{print}->export_gcode(output_file => $self->{export_gcode_output_file});
my ($percent, $message) = @_; };
$self->statusbar->SetProgress($percent); my $result = !Slic3r::GUI::catch_error($self);
$self->statusbar->SetStatusText("$message…"); $self->on_export_completed($result);
},
message_dialog => sub { Wx::MessageDialog->new($self, @_)->ShowModal },
on_completed => sub { $self->on_export_completed(@_) },
catch_error => sub { Slic3r::GUI::catch_error($self, @_) && $self->on_export_failed },
);
} }
# this method gets executed in a separate thread by wxWidgets since it's a button handler # this method gets executed in a separate thread by wxWidgets since it's a button handler
Slic3r::thread_cleanup() if $Slic3r::have_threads; Slic3r::thread_cleanup() if $Slic3r::have_threads;
} }
sub export_gcode2 { # This gets called only if we have threads.
my $self = shift; sub on_process_completed {
my ($output_file, %params) = @_; my ($self, $result) = @_;
$self->statusbar->SetCancelCallback(undef);
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("");
Slic3r::debugf "Background processing completed.\n";
$self->{process_thread}->detach if $self->{process_thread};
$self->{process_thread} = undef;
return if !$result;
# if we have an export filename, start a new thread for exporting G-code
if ($self->{export_gcode_output_file}) {
@_ = ();
$self->{export_thread} = threads->create(sub {
local $SIG{'KILL'} = sub { local $SIG{'KILL'} = sub {
Slic3r::debugf "Exporting cancelled; exiting thread...\n"; Slic3r::debugf "Export process cancelled; exiting thread...\n";
Slic3r::thread_cleanup(); Slic3r::thread_cleanup();
threads->exit(); threads->exit();
} if $Slic3r::have_threads; };
my $print = $self->{print};
eval { eval {
{ $self->{print}->export_gcode(output_file => $self->{export_gcode_output_file});
my @warnings = ();
local $SIG{__WARN__} = sub { push @warnings, $_[0] };
$print->set_status_cb(sub { $params{progressbar}->(@_) });
if ($params{export_svg}) {
$print->export_svg(output_file => $output_file);
} else {
$print->process;
$print->export_gcode(output_file => $output_file);
}
$print->set_status_cb(undef);
Slic3r::GUI::warning_catcher($self, $Slic3r::have_threads ? sub {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_])));
} : undef)->($_) for @warnings;
}
$params{on_completed}->();
}; };
$params{catch_error}->(); if ($@) {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $ERROR_EVENT, shared_clone([ $@ ])));
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, 0));
} else {
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $EXPORT_COMPLETED_EVENT, 1));
}
Slic3r::thread_cleanup();
});
Slic3r::debugf "Background G-code export started.\n";
}
} }
sub on_export_completed { # This gets called also if we have no threads.
my $self = shift; sub on_progress_event {
my ($self, $percent, $message) = @_;
$self->statusbar->SetProgress($percent);
$self->statusbar->SetStatusText("$message…");
}
# This gets called also if we don't have threads.
sub on_export_completed {
my ($self, $result) = @_;
$self->{export_thread}->detach if $self->{export_thread};
$self->{export_thread} = undef;
$self->statusbar->SetCancelCallback(undef); $self->statusbar->SetCancelCallback(undef);
$self->statusbar->StopBusy; $self->statusbar->StopBusy;
my $message = "G-code file exported to $self->{output_file}"; $self->statusbar->SetStatusText("");
Slic3r::debugf "Background export process completed.\n";
$self->{export_thread}->detach if $self->{export_thread};
$self->{export_thread} = undef;
my $message;
if ($result) {
$message = "G-code file exported to " . $self->{export_gcode_output_file};
} else {
$message = "Export failed";
}
$self->{export_gcode_output_file} = undef;
$self->statusbar->SetStatusText($message); $self->statusbar->SetStatusText($message);
&Wx::wxTheApp->notify($message); &Wx::wxTheApp->notify($message);
} }
sub on_export_failed {
my $self = shift;
$self->{export_thread}->detach if $self->{export_thread};
$self->{export_thread} = undef;
$self->statusbar->SetCancelCallback(undef);
$self->statusbar->StopBusy;
$self->statusbar->SetStatusText("Export failed");
}
sub export_stl { sub export_stl {
my $self = shift; my $self = shift;