More work on print controller

This commit is contained in:
Alessandro Ranellucci 2015-01-03 23:25:55 +01:00
parent c06ce3b58c
commit 2c0d216c1a
15 changed files with 626 additions and 63 deletions

View File

@ -194,6 +194,7 @@ sub thread_cleanup {
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
*Slic3r::Flow::DESTROY = sub {};
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
*Slic3r::GCode::Sender::DESTROY = sub {};
*Slic3r::GCode::Writer::DESTROY = sub {};
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};

View File

@ -68,6 +68,7 @@ our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$small_font->SetPointSize(11) if !&Wx::wxMSW;
our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$medium_font->SetPointSize(12);
our $grey = Wx::Colour->new(100,100,100);
sub OnInit {
my ($self) = @_;
@ -297,9 +298,22 @@ sub CallAfter {
sub show_printer_controller {
my ($self) = @_;
$self->{controller_frame} = Slic3r::GUI::Controller::Frame->new;
$self->{controller_frame} //= Slic3r::GUI::Controller::Frame->new;
$self->{controller_frame}->Show;
return $self->{controller_frame};
}
sub scan_serial_ports {
my ($self) = @_;
my @ports = ();
# TODO: Windows ports
# UNIX and OS X
push @ports, glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*';
return @ports;
}
1;

View File

@ -3,31 +3,94 @@ use strict;
use warnings;
use utf8;
use Wx qw(:frame :id :misc :sizer);
use Wx::Event qw(EVT_CLOSE);
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button);
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
use base 'Wx::Frame';
sub new {
my ($class) = @_;
my $self = $class->SUPER::new(undef, -1, "Controller", wxDefaultPosition, [500,350], wxDEFAULT_FRAME_STYLE);
my $self = $class->SUPER::new(undef, -1, "Controller", wxDefaultPosition, [600,350],
wxDEFAULT_FRAME_STYLE | wxFRAME_EX_METAL);
$self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add(Slic3r::GUI::Controller::PrinterPanel->new($self), 1, wxEXPAND);
{
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
$btn->SetToolTipString("Add printer…")
if $btn->can('SetToolTipString');
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
my %presets = wxTheApp->presets('printer');
foreach my $preset_name (sort keys %presets) {
my $config = Slic3r::Config->load($presets{$preset_name});
next if !$config->serial_port;
my $id = &Wx::NewId();
$menu->Append($id, $preset_name);
EVT_MENU($menu, $id, sub {
$self->add_printer($preset_name, $config);
});
}
$self->PopupMenu($menu, $btn->GetPosition);
$menu->Destroy;
});
$self->{sizer}->Add($btn, 0, wxTOP | wxLEFT, 10);
}
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
$sizer->SetSizeHints($self);
$self->Layout;
#$sizer->SetSizeHints($self);
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
# ...
foreach my $panel ($self->print_panels) {
$panel->disconnect;
}
undef wxTheApp->{controller_frame};
$event->Skip;
});
# if only one preset exists, load it
{
my %presets = wxTheApp->presets('printer');
my %configs = map { my $name = $_; $name => Slic3r::Config->load($presets{$name}) } keys %presets;
my @presets_with_printer = grep $configs{$_}->serial_port, keys %presets;
if (@presets_with_printer == 1) {
my $name = $presets_with_printer[0];
$self->add_printer($name, $configs{$name});
}
}
$self->Layout;
return $self;
}
sub add_printer {
my ($self, $printer_name, $config) = @_;
# check that printer doesn't exist already
foreach my $panel ($self->print_panels) {
if ($panel->printer_name eq $printer_name) {
return $panel;
}
}
my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $printer_name, $config);
$self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
$self->Layout;
return $printer_panel;
}
sub print_panels {
my ($self) = @_;
return grep $_->isa('Slic3r::GUI::Controller::PrinterPanel'),
map $_->GetWindow, $self->{sizer}->GetChildren;
}
1;

View File

@ -3,105 +3,193 @@ use strict;
use warnings;
use utf8;
use Wx qw(:panel :id :misc :sizer :button :bitmap);
use Wx::Event qw(EVT_BUTTON);
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer);
use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER);
use base qw(Wx::Panel Class::Accessor);
__PACKAGE__->mk_accessors(qw(sender));
__PACKAGE__->mk_accessors(qw(printer_name config sender jobs
printing print_status_timer));
use constant PRINT_STATUS_TIMER_INTERVAL => 1000; # milliseconds
sub new {
my ($class, $parent) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
my ($class, $parent, $printer_name, $config) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]);
$self->{sizer} = my $sizer = Wx::StaticBoxSizer->new(Wx::StaticBox->new($self, -1, "Printer"), wxVERTICAL);
$self->printer_name($printer_name || 'Printer');
$self->config($config);
$self->jobs([]);
{
my $timer_id = &Wx::NewId();
$self->print_status_timer(Wx::Timer->new($self, $timer_id));
EVT_TIMER($self, $timer_id, sub {
my ($self, $event) = @_;
return if !$self->printing;
my $queue_size = $self->sender->queue_size;
printf "queue = %d\n", $queue_size;
$self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size);
if ($queue_size == 0) {
$self->print_completed;
return;
}
# TODO: get temperature messages
});
}
my $box = Wx::StaticBox->new($self, -1, "");
my $sizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL);
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
# printer name
{
my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [250,-1]);
my $font = $text->GetFont;
$font->SetPointSize(20);
$text->SetFont($font);
$left_sizer->Add($text, 0, wxEXPAND, 0);
}
# connection info
{
my $conn_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$sizer->Add($conn_sizer, 0, wxEXPAND);
my $conn_sizer = Wx::FlexGridSizer->new(2, 2, 1, 0);
$conn_sizer->SetFlexibleDirection(wxHORIZONTAL);
$conn_sizer->AddGrowableCol(1, 1);
$left_sizer->Add($conn_sizer, 0, wxEXPAND | wxTOP, 5);
{
my $text = Wx::StaticText->new($self, -1, "Port:", wxDefaultPosition, wxDefaultSize);
my $text = Wx::StaticText->new($box, -1, "Port:", wxDefaultPosition, wxDefaultSize);
$text->SetFont($Slic3r::GUI::small_font);
$conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
}
my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
{
$self->{serial_port_combobox} = Wx::ComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, []);
$self->scan_serial_ports;
$conn_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
$self->{serial_port_combobox} = Wx::ComboBox->new($box, -1, $config->serial_port, wxDefaultPosition, wxDefaultSize, []);
$self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font);
$self->update_serial_ports;
$serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
}
{
$self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG),
$self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE);
$conn_sizer->Add($btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
EVT_BUTTON($self, $btn, sub { $self->scan_serial_ports });
$serial_port_sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL, 0);
EVT_BUTTON($self, $btn, sub { $self->update_serial_ports });
}
$conn_sizer->Add($serial_port_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
{
my $text = Wx::StaticText->new($self, -1, "Speed:", wxDefaultPosition, wxDefaultSize);
$conn_sizer->Add($text, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
my $text = Wx::StaticText->new($box, -1, "Speed:", wxDefaultPosition, wxDefaultSize);
$text->SetFont($Slic3r::GUI::small_font);
$conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
}
my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
{
$self->{serial_speed_combobox} = Wx::ComboBox->new($self, -1, "250000", wxDefaultPosition, wxDefaultSize,
$self->{serial_speed_combobox} = Wx::ComboBox->new($box, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize,
["115200", "250000"]);
$conn_sizer->Add($self->{serial_speed_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
$serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0);
}
{
$self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize);
$btn->SetFont($Slic3r::GUI::small_font);
if ($Slic3r::GUI::have_button_icons) {
$btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG));
}
$serial_speed_sizer->Add($btn, 0, wxLEFT, 5);
EVT_BUTTON($self, $btn, \&disconnect);
}
$conn_sizer->Add($serial_speed_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
}
# buttons
{
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$sizer->Add($buttons_sizer, 0, wxEXPAND);
{
$self->{btn_connect} = my $btn = Wx::Button->new($self, -1, "Connect", wxDefaultPosition, wxDefaultSize);
$buttons_sizer->Add($btn, 0, wxRIGHT, 5);
EVT_BUTTON($self, $btn, \&connect);
}
{
$self->{btn_disconnect} = my $btn = Wx::Button->new($self, -1, "Disconnect", wxDefaultPosition, wxDefaultSize);
$buttons_sizer->Add($btn, 0, wxRIGHT, 5);
EVT_BUTTON($self, $btn, \&disconnect);
$self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect", wxDefaultPosition, [-1, 40]);
my $font = $btn->GetFont;
$font->SetPointSize($font->GetPointSize + 2);
$btn->SetFont($font);
if ($Slic3r::GUI::have_button_icons) {
$btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/arrow_up.png", wxBITMAP_TYPE_PNG));
}
$left_sizer->Add($btn, 0, wxTOP, 15);
EVT_BUTTON($self, $btn, \&connect);
}
# print progress bar
{
my $gauge = $self->{gauge} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
$left_sizer->Add($self->{gauge}, 0, wxEXPAND | wxTOP, 15);
$gauge->Hide;
}
# status
$self->{status_text} = Wx::StaticText->new($self, -1, "Not connected", wxDefaultPosition, wxDefaultSize);
$sizer->Add($self->{status_text}, 0, wxEXPAND);
$self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [250,-1]);
$left_sizer->Add($self->{status_text}, 0, wxEXPAND | wxTOP, 15);
# print jobs panel
my $print_jobs_sizer = Wx::BoxSizer->new(wxVERTICAL);
{
my $text = Wx::StaticText->new($box, -1, "Queue:", wxDefaultPosition, wxDefaultSize);
$text->SetFont($Slic3r::GUI::small_font);
$print_jobs_sizer->Add($text, 0, wxEXPAND, 0);
$self->{jobs_panel} = Wx::ScrolledWindow->new($box, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN);
$self->{jobs_panel}->SetScrollbars(0, 1, 0, 1);
$self->{jobs_panel_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{jobs_panel}->SetSizer($self->{jobs_panel_sizer});
$print_jobs_sizer->Add($self->{jobs_panel}, 1, wxEXPAND, 0);
}
$sizer->Add($left_sizer, 0, wxEXPAND | wxALL, 0);
$sizer->Add($print_jobs_sizer, 1, wxEXPAND | wxALL, 0);
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
$sizer->SetSizeHints($self);
$self->_update_connection_controls;
$self->set_status('Printer is offline. Click the Connect button.');
return $self;
}
sub is_connected {
my ($self) = @_;
return $self->sender && $self->sender->is_connected;
}
sub _update_connection_controls {
my ($self) = @_;
if ($self->sender && $self->sender->is_connected) {
$self->{btn_connect}->Show;
$self->{btn_disconnect}->Hide;
$self->{serial_port_combobox}->Enable;
$self->{serial_speed_combobox}->Enable;
$self->{btn_rescan_serial}->Enable;
if ($self->is_connected) {
$self->{btn_connect}->Hide;
$self->{btn_disconnect}->Show;
if (!$self->printing) {
$self->{btn_disconnect}->Show;
}
$self->{serial_port_combobox}->Disable;
$self->{serial_speed_combobox}->Disable;
$self->{btn_rescan_serial}->Disable;
} else {
$self->{btn_connect}->Show;
$self->{btn_disconnect}->Hide;
$self->{serial_port_combobox}->Enable;
$self->{serial_speed_combobox}->Enable;
$self->{btn_rescan_serial}->Enable;
}
$self->Layout;
}
sub set_status {
my ($self, $status) = @_;
$self->{status_text}->SetLabel($status);
$self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth - 30);
$self->{status_text}->Refresh;
$self->Layout;
}
sub connect {
my ($self) = @_;
return if $self->sender && $self->sender->is_connected;
return if $self->is_connected;
$self->set_status("Connecting...");
$self->sender(Slic3r::GCode::Sender->new);
@ -113,30 +201,301 @@ sub connect {
$self->set_status("Connection failed");
}
1 until $self->sender->is_connected;
$self->set_status("Connected");
$self->set_status("Printer is online. You can now start printing from the queue on the right.");
$self->_update_connection_controls;
$self->reload_jobs;
}
sub disconnect {
my ($self) = @_;
return if !$self->sender || !$self->sender->is_connected;
$self->print_status_timer->Stop;
return if !$self->is_connected;
$self->printing->printing(0) if $self->printing;
$self->printing(undef);
$self->{gauge}->Hide;
$self->sender->disconnect;
$self->set_status("Not connected");
$self->_update_connection_controls;
$self->reload_jobs;
}
sub scan_serial_ports {
sub update_serial_ports {
my ($self) = @_;
$self->{serial_port_combobox}->Clear;
# TODO: Windows ports
# UNIX and OS X
$self->{serial_port_combobox}->Append($_)
for glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*';
for wxTheApp->scan_serial_ports;
}
sub load_print_job {
my ($self, $gcode_file, $filament_stats) = @_;
push @{$self->jobs}, my $job = Slic3r::GUI::Controller::PrinterPanel::PrintJob->new(
id => time() . $gcode_file . rand(1000),
gcode_file => $gcode_file,
filament_stats => $filament_stats,
);
$self->reload_jobs;
return $job;
}
sub delete_job {
my ($self, $job) = @_;
$self->jobs([ grep $_->id ne $job->id, @{$self->jobs} ]);
$self->reload_jobs;
}
sub print_job {
my ($self, $job) = @_;
$self->printing($job);
$job->printing(1);
$self->reload_jobs;
open my $fh, '<', $job->gcode_file;
my $line_count = 0;
while (my $row = <$fh>) {
$self->sender->send($row);
$line_count++;
}
close $fh;
$self->_update_connection_controls;
$self->{gauge}->SetRange($line_count);
$self->{gauge}->SetValue(0);
$self->{gauge}->Enable;
$self->{gauge}->Show;
$self->Layout;
$self->print_status_timer->Start(PRINT_STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS);
$self->set_status('Printing...');
}
sub print_completed {
my ($self) = @_;
my $job = $self->printing;
$self->printing(undef);
$job->printing(0);
$job->printed(1);
$self->_update_connection_controls;
$self->{gauge}->Hide;
$self->Layout;
$self->print_status_timer->Stop;
$self->set_status('Print completed.');
# reorder jobs
@{$self->jobs} = sort { $a->printed <=> $b->printed } @{$self->jobs};
$self->reload_jobs;
}
sub reload_jobs {
my ($self) = @_;
# remove all panels
foreach my $child ($self->{jobs_panel_sizer}->GetChildren) {
my $window = $child->GetWindow;
$self->{jobs_panel_sizer}->Detach($window);
# now $child does not exist anymore
$window->Destroy;
}
# re-add all panels
foreach my $job (@{$self->jobs}) {
my $panel = Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel->new($self->{jobs_panel}, $job);
$self->{jobs_panel_sizer}->Add($panel, 0, wxEXPAND | wxBOTTOM, 5);
$panel->on_delete_job(sub {
my ($job) = @_;
$self->delete_job($job);
});
$panel->on_print_job(sub {
my ($job) = @_;
$self->print_job($job);
});
$panel->on_pause_print(sub {
my ($job) = @_;
$self->sender->pause_queue;
$job->paused(1);
$self->reload_jobs;
$self->_update_connection_controls;
$self->{gauge}->Disable;
$self->set_status('Print is paused. Click on Resume to continue.');
});
$panel->on_resume_print(sub {
my ($job) = @_;
$self->sender->resume_queue;
$job->paused(0);
$self->reload_jobs;
$self->_update_connection_controls;
$self->{gauge}->Enable;
$self->set_status('Printing...');
});
$panel->enable_print if $self->is_connected && !$self->printing;
EVT_MOUSEWHEEL($panel, sub {
my (undef, $event) = @_;
Wx::PostEvent($self->{jobs_panel}, $event);
$event->Skip;
});
}
$self->{jobs_panel}->Layout;
}
package Slic3r::GUI::Controller::PrinterPanel::PrintJob;
use Moo;
use File::Basename qw(basename);
has 'id' => (is => 'ro', required => 1);
has 'gcode_file' => (is => 'ro', required => 1);
has 'filament_stats' => (is => 'rw');
has 'printing' => (is => 'rw', default => sub { 0 });
has 'paused' => (is => 'rw', default => sub { 0 });
has 'printed' => (is => 'rw', default => sub { 0 });
sub name {
my ($self) = @_;
return basename($self->gcode_file);
}
package Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel;
use strict;
use warnings;
use utf8;
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :font :dialog :icon);
use Wx::Event qw(EVT_BUTTON);
use base qw(Wx::Panel Class::Accessor);
__PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print));
sub new {
my ($class, $parent, $job) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
$self->job($job);
$self->SetBackgroundColour(Wx::wxWHITE);
my $title_and_buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
{
my $text = Wx::StaticText->new($self, -1, $job->name, wxDefaultPosition, wxDefaultSize);
my $font = $text->GetFont;
$font->SetWeight(wxFONTWEIGHT_BOLD);
$text->SetFont($font);
if ($job->printing) {
$text->SetForegroundColour(Wx::wxGREEN);
} elsif ($job->printed) {
$text->SetForegroundColour($Slic3r::GUI::grey);
}
$title_and_buttons_sizer->Add($text, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
}
{
my $btn = $self->{btn_delete} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
$btn->SetToolTipString("Delete this job from print queue")
if $btn->can('SetToolTipString');
$btn->SetFont($Slic3r::GUI::small_font);
$title_and_buttons_sizer->Add($btn, 0, wxEXPAND | wxBOTTOM, 0);
EVT_BUTTON($self, $btn, sub {
my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete this print job?", 'Delete Job', wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal;
return unless $res == wxID_YES;
wxTheApp->CallAfter(sub {
$self->on_delete_job->($job);
});
});
}
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
{
my $filament_stats = join "\n",
map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/100) . "m)",
sort keys %{$job->filament_stats};
my $text = Wx::StaticText->new($self, -1, $filament_stats, wxDefaultPosition, wxDefaultSize);
$text->SetFont($Slic3r::GUI::small_font);
if ($job->printed) {
$text->SetForegroundColour($Slic3r::GUI::grey);
}
$left_sizer->Add($text, 1, wxEXPAND | wxTOP | wxBOTTOM, 7);
}
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
{
my $label = $job->printed ? 'Print Again' : 'Print This';
my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize);
$btn->Hide;
if ($Slic3r::GUI::have_button_icons) {
$self->{btn_print}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/arrow_up.png", wxBITMAP_TYPE_PNG));
}
$right_sizer->Add($btn, 0, wxEXPAND | wxBOTTOM, 7);
EVT_BUTTON($self, $btn, sub {
wxTheApp->CallAfter(sub {
$self->on_print_job->($job);
});
});
}
{
my $btn = $self->{btn_pause} = Wx::Button->new($self, -1, "Pause", wxDefaultPosition, wxDefaultSize);
if (!$job->printing || $job->paused) {
$btn->Hide;
}
if ($Slic3r::GUI::have_button_icons) {
$self->{btn_print}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/arrow_up.png", wxBITMAP_TYPE_PNG));
}
$right_sizer->Add($btn, 0, wxEXPAND | wxBOTTOM, 7);
EVT_BUTTON($self, $btn, sub {
wxTheApp->CallAfter(sub {
$self->on_pause_print->($job);
});
});
}
{
my $btn = $self->{btn_resume} = Wx::Button->new($self, -1, "Resume", wxDefaultPosition, wxDefaultSize);
if (!$job->printing || !$job->paused) {
$btn->Hide;
}
if ($Slic3r::GUI::have_button_icons) {
$self->{btn_print}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/arrow_up.png", wxBITMAP_TYPE_PNG));
}
$right_sizer->Add($btn, 0, wxEXPAND | wxBOTTOM, 7);
EVT_BUTTON($self, $btn, sub {
wxTheApp->CallAfter(sub {
$self->on_resume_print->($job);
});
});
}
my $middle_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$middle_sizer->Add($left_sizer, 1, wxEXPAND, 0);
$middle_sizer->Add($right_sizer, 0, wxEXPAND, 0);
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add($title_and_buttons_sizer, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 3);
$sizer->Add($middle_sizer, 1, wxEXPAND, 0);
$self->SetSizer($sizer);
return $self;
}
sub enable_print {
my ($self) = @_;
if (!$self->job->printing) {
$self->{btn_print}->Show;
}
$self->Layout;
}
1;

View File

@ -229,7 +229,7 @@ sub _init_menubar {
$self->select_tab($tab_count-1);
});
$windowMenu->AppendSeparator();
$self->_append_menu_item($windowMenu, "Printer Controller", 'Show the printer controller', sub {
$self->_append_menu_item($windowMenu, "Printer &Controller\tCtrl+T", 'Show the printer controller', sub {
wxTheApp->show_printer_controller;
});
}
@ -674,6 +674,17 @@ sub config {
return $config;
}
sub filament_preset_names {
my ($self) = @_;
if ($self->{mode} eq 'simple') {
return '';
}
return map $self->{options_tabs}{filament}->get_preset($_)->name,
$self->{plater}->filament_presets;
}
sub check_unsaved_changes {
my $self = shift;

View File

@ -48,7 +48,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config->new_from_defaults(qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width
octoprint_host octoprint_apikey
serial_port serial_speed octoprint_host octoprint_apikey
));
$self->{model} = Slic3r::Model->new;
$self->{print} = Slic3r::Print->new;
@ -173,10 +173,12 @@ sub new {
# right pane buttons
$self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
$self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
$self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT);
$self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
$self->{btn_print}->Hide;
$self->{btn_send_gcode}->Hide;
if ($Slic3r::GUI::have_button_icons) {
@ -186,6 +188,7 @@ sub new {
reset cross.png
arrange bricks.png
export_gcode cog_go.png
print arrow_up.png
send_gcode arrow_up.png
export_stl brick_go.png
@ -208,6 +211,10 @@ sub new {
$self->export_gcode;
Slic3r::thread_cleanup();
});
EVT_BUTTON($self, $self->{btn_print}, sub {
$self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir());
Slic3r::thread_cleanup();
});
EVT_BUTTON($self, $self->{btn_send_gcode}, sub {
$self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir());
Slic3r::thread_cleanup();
@ -354,6 +361,7 @@ sub new {
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$buttons_sizer->AddStretchSpacer(1);
$buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0);
$buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0);
@ -1064,8 +1072,12 @@ sub on_export_completed {
my $message;
my $send_gcode = 0;
my $do_print = 0;
if ($result) {
if ($self->{send_gcode_file}) {
if ($self->{print_file}) {
$message = "Adding file to print queue...";
$do_print = 1;
} elsif ($self->{send_gcode_file}) {
$message = "Sending G-code file to the OctoPrint server...";
$send_gcode = 1;
} else {
@ -1078,10 +1090,32 @@ sub on_export_completed {
$self->statusbar->SetStatusText($message);
wxTheApp->notify($message);
$self->do_print if $do_print;
$self->send_gcode if $send_gcode;
$self->{print_file} = undef;
$self->{send_gcode_file} = undef;
}
sub do_print {
my ($self) = @_;
my $printer_tab = $self->GetFrame->{options_tabs}{printer};
my $printer_name = $printer_tab->get_current_preset->name;
my $controller = wxTheApp->show_printer_controller;
my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config);
my $filament_stats = $self->{print}->filament_stats;
my @filament_names = $self->GetFrame->filament_preset_names;
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats };
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
$controller->Iconize(0); # restore the window if minimized
$controller->SetFocus(); # focus on my window
$controller->Raise(); # bring window to front
$controller->Show(1); # show the window
}
sub send_gcode {
my ($self) = @_;
@ -1250,6 +1284,13 @@ sub on_config_change {
$self->{canvas}->update_bed_size;
$self->{canvas3D}->update_bed_size if $self->{canvas3D};
$self->update;
} elsif ($opt_key eq 'serial_port') {
if ($config->get('serial_port')) {
$self->{btn_print}->Show;
} else {
$self->{btn_print}->Hide;
}
$self->Layout;
} elsif ($opt_key eq 'octoprint_host') {
if ($config->get('octoprint_host')) {
$self->{btn_send_gcode}->Show;
@ -1362,7 +1403,7 @@ sub object_list_changed {
my $have_objects = @{$self->{objects}} ? 1 : 0;
my $method = $have_objects ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl send_gcode);
for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl print send_gcode);
if ($self->{htoolbar}) {
$self->{htoolbar}->EnableTool($_, $have_objects)

View File

@ -923,6 +923,7 @@ sub build {
$self->init_config_options(qw(
bed_shape z_offset
gcode_flavor use_relative_e_distances
serial_port serial_speed
octoprint_host octoprint_apikey
use_firmware_retraction pressure_advance vibration_limit
start_gcode end_gcode layer_gcode toolchange_gcode
@ -997,6 +998,11 @@ sub build {
}
});
}
{
my $optgroup = $page->new_optgroup('USB/Serial connection');
$optgroup->append_single_option_line('serial_port');
$optgroup->append_single_option_line('serial_speed');
}
{
my $optgroup = $page->new_optgroup('OctoPrint upload');

View File

@ -243,11 +243,14 @@ sub export {
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode);
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
# get filament stats
$self->print->clear_filament_stats;
$self->print->total_used_filament(0);
$self->print->total_extruded_volume(0);
foreach my $extruder (@{$gcodegen->writer->extruders}) {
my $used_filament = $extruder->used_filament;
my $extruded_volume = $extruder->extruded_volume;
$self->print->set_filament_stats($extruder->id, $used_filament);
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
$used_filament, $extruded_volume/1000;

View File

@ -23,7 +23,8 @@ namespace Slic3r {
namespace asio = boost::asio;
GCodeSender::GCodeSender()
: io(), serial(io), can_send(false), sent(0), error(false), connected(false)
: io(), serial(io), can_send(false), sent(0), error(false), connected(false),
queue_paused(false)
{}
GCodeSender::~GCodeSender()
@ -140,6 +141,23 @@ GCodeSender::queue_size() const
return this->queue.size();
}
void
GCodeSender::pause_queue()
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->queue_paused = true;
}
void
GCodeSender::resume_queue()
{
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->queue_paused = false;
}
this->send();
}
void
GCodeSender::do_close()
{
@ -260,6 +278,7 @@ GCodeSender::send()
{
// printer is not connected or we're still waiting for the previous ack
if (!this->can_send) return;
if (this->queue_paused) return;
boost::lock_guard<boost::mutex> l(this->queue_mutex);
if (this->queue.empty()) return;

View File

@ -25,6 +25,8 @@ class GCodeSender : private boost::noncopyable {
bool error_status() const;
bool is_connected() const;
size_t queue_size() const;
void pause_queue();
void resume_queue();
private:
asio::io_service io;
@ -39,6 +41,7 @@ class GCodeSender : private boost::noncopyable {
mutable boost::mutex queue_mutex;
std::queue<std::string> queue;
bool can_send;
bool queue_paused;
size_t sent;
void set_baud_rate(unsigned int baud_rate);

View File

@ -161,6 +161,7 @@ class Print
PlaceholderParser placeholder_parser;
// TODO: status_cb
double total_used_filament, total_extruded_volume;
std::map<size_t,float> filament_stats;
PrintState<PrintStep> state;
// ordered collections of extrusion paths to build skirt loops and brim

View File

@ -661,6 +661,18 @@ PrintConfigDef::build_def() {
Options["seam_position"].enum_labels.push_back("Nearest");
Options["seam_position"].enum_labels.push_back("Aligned");
Options["serial_port"].type = coString;
Options["serial_port"].label = "Serial port";
Options["serial_port"].tooltip = "USB/serial port for printer connection.";
Options["serial_port"].cli = "serial-port=s";
Options["serial_speed"].type = coInt;
Options["serial_speed"].label = "Speed";
Options["serial_speed"].tooltip = "Speed (baud) of USB/serial port for printer connection.";
Options["serial_speed"].cli = "serial-speed=i";
Options["serial_speed"].min = 1;
Options["serial_speed"].max = 300000;
Options["skirt_distance"].type = coFloat;
Options["skirt_distance"].label = "Distance from object";
Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";

View File

@ -587,15 +587,21 @@ class HostConfig : public virtual StaticPrintConfig
public:
ConfigOptionString octoprint_host;
ConfigOptionString octoprint_apikey;
ConfigOptionString serial_port;
ConfigOptionInt serial_speed;
HostConfig() : StaticPrintConfig() {
this->octoprint_host.value = "";
this->octoprint_apikey.value = "";
this->serial_port.value = "";
this->serial_speed.value = 250000;
};
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
if (opt_key == "octoprint_host") return &this->octoprint_host;
if (opt_key == "octoprint_apikey") return &this->octoprint_apikey;
if (opt_key == "serial_port") return &this->serial_port;
if (opt_key == "serial_speed") return &this->serial_speed;
return NULL;
};

View File

@ -16,6 +16,8 @@
bool is_connected() const;
int queue_size() const;
void send(std::string s);
void pause_queue();
void resume_queue();
};
#endif

View File

@ -174,6 +174,28 @@ _constant()
RETVAL.push_back(*e);
}
%};
void clear_filament_stats()
%code%{
THIS->filament_stats.clear();
%};
void set_filament_stats(int extruder_id, float length)
%code%{
THIS->filament_stats.insert(std::pair<size_t,float>(extruder_id, 0));
THIS->filament_stats[extruder_id] += length;
%};
SV* filament_stats()
%code%{
HV* hv = newHV();
for (std::map<size_t,float>::const_iterator it = THIS->filament_stats.begin(); it != THIS->filament_stats.end(); ++it) {
// stringify extruder_id
std::ostringstream ss;
ss << it->first;
std::string str = ss.str();
(void)hv_store( hv, str.c_str(), str.length(), newSViv(it->second), 0 );
RETVAL = newRV_noinc((SV*)hv);
}
%};
void _simplify_slices(double distance);
double max_allowed_layer_height() const;
bool has_support_material() const;