Merge branch 'master' into wipe_tower_improvements

This commit is contained in:
Lukas Matena 2017-12-21 13:47:33 +01:00
commit 4583d62edd
126 changed files with 5240 additions and 607 deletions

View file

@ -15,7 +15,7 @@ package Slic3r::GUI::3DScene::Base;
use strict;
use warnings;
use Wx qw(:timer :bitmap :icon :dialog);
use Wx qw(wxTheApp :timer :bitmap :icon :dialog);
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
@ -108,6 +108,7 @@ sub new {
# We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas,
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
my $can_multisample =
! wxTheApp->{app_config}->get('use_legacy_opengl') &&
Wx::wxVERSION >= 3.000003 &&
defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') &&
defined Wx::GLCanvas->can('WX_GL_SAMPLES');
@ -956,6 +957,16 @@ sub UseVBOs {
my ($self) = @_;
if (! defined ($self->{use_VBOs})) {
my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl');
if ($use_legacy eq '1') {
# Disable OpenGL 2.0 rendering.
$self->{use_VBOs} = 0;
# Don't enable the layer editing tool.
$self->{layer_editing_enabled} = 0;
# 2 means failed
$self->{layer_editing_initialized} = 2;
return 0;
}
# This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
# first when an OpenGL widget is shown for the first time. How ugly.
return 0 if (! $self->init && $^O eq 'linux');

View file

@ -14,14 +14,14 @@ our $wizard = 'Wizard';
$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
sub new {
my $class = shift;
my ($parent) = @_;
my ($class, $parent, $presets, $fresh_start) = @_;
my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
# initialize an empty repository
$self->{config} = Slic3r::Config->new;
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Welcome->new($self));
my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self, $fresh_start);
$self->add_page($welcome_page);
$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));
@ -32,12 +32,13 @@ sub new {
$_->build_index for @{$self->{pages}};
$welcome_page->set_selection_presets([@{$presets}, 'Other']);
return $self;
}
sub add_page {
my $self = shift;
my ($page) = @_;
my ($self, $page) = @_;
my $n = push @{$self->{pages}}, $page;
# add first page to the page area sizer
@ -48,13 +49,17 @@ sub add_page {
}
sub run {
my $self = shift;
my ($self) = @_;
my $result;
if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
# it would be cleaner to have these defined inside each page class,
# in some event getting called before leaving the page
{
my $preset_name = $self->{pages}[0]->{preset_name};
$result = {
preset_name => $preset_name,
reset_user_profile => $self->{pages}[0]->{reset_user_profile}
};
if ($preset_name eq 'Other') {
# it would be cleaner to have these defined inside each page class,
# in some event getting called before leaving the page
# set first_layer_height + layer_height based on nozzle_diameter
my $nozzle = $self->{config}->nozzle_diameter;
$self->{config}->set('first_layer_height', $nozzle->[0]);
@ -66,14 +71,11 @@ sub run {
# set first_layer_bed_temperature to temperature + 5
$self->{config}->set('first_layer_bed_temperature',
[ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
$result->{config} = $self->{config};
}
$self->Destroy;
return $self->{config};
} else {
$self->Destroy;
return undef;
}
$self->Destroy;
return $result;
}
package Slic3r::GUI::ConfigWizard::Index;
@ -127,6 +129,8 @@ sub repaint {
$dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index};
$dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h));
# Only show the first bullet if this is the only wizard page to be displayed.
last if $i == 0 && $self->{just_welcome};
$i++;
}
@ -263,19 +267,87 @@ sub config {
package Slic3r::GUI::ConfigWizard::Page::Welcome;
use base 'Slic3r::GUI::ConfigWizard::Page';
use Wx qw(:misc :sizer wxID_FORWARD);
use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE EVT_CHECKBOX);
sub new {
my $class = shift;
my ($parent) = @_;
my ($class, $parent, $fresh_start) = @_;
my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
$self->{full_wizard_workflow} = 1;
$self->{reset_user_profile} = 0;
$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.');
# Test for the existence of the old config path.
my $message_has_legacy;
{
my $datadir = Slic3r::data_dir;
if ($datadir =~ /Slic3rPE/) {
# Check for existence of the legacy Slic3r directory.
my $datadir_legacy = substr $datadir, 0, -2;
my $dir_enc = Slic3r::encode_path($datadir_legacy);
if (-e $dir_enc && -d $dir_enc &&
-e ($dir_enc . '/print') && -d ($dir_enc . '/print') &&
-e ($dir_enc . '/filament') && -d ($dir_enc . '/filament') &&
-e ($dir_enc . '/printer') && -d ($dir_enc . '/printer') &&
-e ($dir_enc . '/slic3r.ini')) {
$message_has_legacy = "Starting with Slic3r 1.38.4, the user profile directory has been renamed to $datadir. You may consider closing Slic3r and renaming $datadir_legacy to $datadir.";
}
}
}
$self->append_text('Hello, welcome to Slic3r Prusa Edition! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.');
$self->append_text('Please select your printer vendor and printer type. If your printer is not listed, you may try your luck and select a similar one. If you select "Other", this ' . lc($wizard) . ' will let you set the basic 3D printer parameters.');
$self->append_text($message_has_legacy) if defined $message_has_legacy;
# 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('If you received a configuration file or a config bundle from your 3D printer vendor, cancel this '.lc($wizard).' and use the "File->Load Config" or "File->Load Config Bundle" menu.');
$self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
$self->{vsizer}->Add($choice, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
if (! $fresh_start) {
$self->{reset_checkbox} = Wx::CheckBox->new($self, -1, "Reset user profile, install from scratch");
$self->{vsizer}->Add($self->{reset_checkbox}, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
}
EVT_CHOICE($parent, $choice, sub {
my $sel = $self->{choice}->GetStringSelection;
$self->{preset_name} = $sel;
$self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq ''));
});
if (! $fresh_start) {
EVT_CHECKBOX($self, $self->{reset_checkbox}, sub {
$self->{reset_user_profile} = $self->{reset_checkbox}->GetValue();
});
}
EVT_ACTIVATE($parent, sub {
$self->set_full_wizard_workflow($self->{preset_name} eq 'Other');
});
return $self;
}
sub set_full_wizard_workflow {
my ($self, $full_workflow) = @_;
$self->{full_wizard_workflow} = $full_workflow;
$self->{index}->{just_welcome} = !$full_workflow;
$self->{index}->Refresh;
my $next_button = $self->GetParent->FindWindow(wxID_FORWARD);
$next_button->SetLabel($full_workflow ? "&Next >" : "&Finish");
}
# Set the preset names, select the first item.
sub set_selection_presets {
my ($self, $names) = @_;
$self->{choice}->Append($names);
$self->{choice}->SetSelection(0);
$self->{preset_name} = $names->[0];
}
sub GetNext {
my $self = shift;
return $self->{full_wizard_workflow} ? $self->{next_page} : undef;
}
package Slic3r::GUI::ConfigWizard::Page::Firmware;
use base 'Slic3r::GUI::ConfigWizard::Page';

View file

@ -7,7 +7,7 @@ use utf8;
use File::Basename qw(basename dirname);
use FindBin;
use List::Util qw(min);
use List::Util qw(min first);
use Slic3r::Geometry qw(X Y);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
:font :icon wxTheApp);
@ -22,6 +22,7 @@ sub new {
my ($class, %params) = @_;
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
Slic3r::GUI::set_main_frame($self);
if ($^O eq 'MSWin32') {
# Load the icon either from the exe, or from the ico file.
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
@ -92,6 +93,8 @@ sub _init_tabpanel {
my ($self) = @_;
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
Slic3r::GUI::set_tab_panel($panel);
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
my $panel = $self->{tabpanel}->GetCurrentPage;
$panel->OnActivate if $panel->can('OnActivate');
@ -129,12 +132,14 @@ sub _init_tabpanel {
$self->{plater}->update_presets($tab_name, @_);
if ($tab_name eq 'printer') {
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
wxTheApp->{preset_bundle}->print->update_tab_ui(
$self->{options_tabs}{'print'}->{presets_choice},
$self->{options_tabs}{'print'}->{show_incompatible_presets});
wxTheApp->{preset_bundle}->filament->update_tab_ui(
$self->{options_tabs}{'filament'}->{presets_choice},
$self->{options_tabs}{'filament'}->{show_incompatible_presets});
my ($presets, $reload_dependent_tabs) = @_;
for my $tab_name_other (qw(print filament)) {
# If the printer tells us that the print or filament preset has been switched or invalidated,
# refresh the print or filament tab page. Otherwise just refresh the combo box.
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
? 'load_current_preset' : 'update_tab_ui';
$self->{options_tabs}{$tab_name_other}->$update_action;
}
# Update the controller printers.
$self->{controller}->update_presets(@_) if $self->{controller};
}
@ -145,6 +150,9 @@ sub _init_tabpanel {
$tab->load_current_preset;
$panel->AddPage($tab, $tab->title);
}
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
# Slic3r::GUI::create_preset_tab("print");
if ($self->{plater}) {
$self->{plater}->on_select_preset(sub {
@ -165,6 +173,9 @@ sub _init_menubar {
# File menu
my $fileMenu = Wx::Menu->new;
{
wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub {
$self->{plater}->add if $self->{plater};
}, undef, undef); #'brick_add.png');
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
$self->load_config_file;
}, undef, 'plugin_add.png');
@ -274,20 +285,22 @@ sub _init_menubar {
# \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
# as the simple numeric accelerators spoil all numeric data entry.
# The camera control accelerators are captured by 3DScene Perl module instead.
$self->_append_menu_item($self->{viewMenu}, "Iso\t\xA00" , 'Iso View' , sub { $self->select_view('iso' ); });
$self->_append_menu_item($self->{viewMenu}, "Top\t\xA01" , 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, "Bottom\t\xA02" , 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, "Front\t\xA03" , 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, "Rear\t\xA04" , 'Rear View' , sub { $self->select_view('rear' ); });
$self->_append_menu_item($self->{viewMenu}, "Left\t\xA05" , 'Left View' , sub { $self->select_view('left' ); });
$self->_append_menu_item($self->{viewMenu}, "Right\t\xA06" , 'Right View' , sub { $self->select_view('right' ); });
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
$self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); });
$self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); });
}
# Help menu
my $helpMenu = Wx::Menu->new;
{
$self->_append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub {
$self->config_wizard;
# Run the config wizard, offer the "reset user profile" checkbox.
$self->config_wizard(0);
});
$helpMenu->AppendSeparator();
$self->_append_menu_item($helpMenu, "Prusa 3D Drivers", 'Open the Prusa3D drivers download page in your browser', sub {
@ -329,6 +342,8 @@ sub _init_menubar {
$menubar->Append($windowMenu, "&Window");
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
$menubar->Append($helpMenu, "&Help");
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
Slic3r::GUI::add_debug_menu($menubar);
$self->SetMenuBar($menubar);
}
}
@ -413,6 +428,7 @@ sub quick_slice {
if ($params{reslice}) {
$output_file = $qs_last_output_file if defined $qs_last_output_file;
} elsif ($params{save_as}) {
# The following line may die if the output_filename_format template substitution fails.
$output_file = $sprint->output_filepath;
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
@ -565,7 +581,7 @@ sub export_configbundle {
# to auto-install a config bundle on a fresh user account,
# but that behavior was not documented and likely buggy.
sub load_configbundle {
my ($self, $file) = @_;
my ($self, $file, $reset_user_profile) = @_;
return unless $self->check_unsaved_changes;
if (!$file) {
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
@ -580,7 +596,7 @@ sub load_configbundle {
wxTheApp->{app_config}->update_config_dir(dirname($file));
my $presets_imported = 0;
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); };
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file, $reset_user_profile ? 1 : 0); };
Slic3r::GUI::catch_error($self) and return;
# Load the currently selected preset into the GUI, update the preset selection box.
@ -601,19 +617,39 @@ sub load_config {
}
sub config_wizard {
my ($self) = @_;
my ($self, $fresh_start) = @_;
# Exit wizard if there are unsaved changes and the user cancels the action.
return unless $self->check_unsaved_changes;
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
for my $tab (values %{$self->{options_tabs}}) {
# Select the first visible preset, force.
$tab->select_preset(undef, 1);
# Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
my $directory = Slic3r::resources_dir() . "/profiles";
my @profiles = ();
if (opendir(DIR, Slic3r::encode_path($directory))) {
while (my $file = readdir(DIR)) {
if ($file =~ /\.ini$/) {
$file =~ s/\.ini$//;
push @profiles, Slic3r::decode_path($file);
}
}
# Load the config over the previously selected defaults.
$self->load_config($config);
for my $tab (values %{$self->{options_tabs}}) {
# Save the settings under a new name, select the name.
$tab->save_preset('My Settings');
closedir(DIR);
}
# Open the wizard.
if (my $result = Slic3r::GUI::ConfigWizard->new($self, \@profiles, $fresh_start)->run) {
eval {
if ($result->{reset_user_profile}) {
wxTheApp->{preset_bundle}->reset(1);
}
if (defined $result->{config}) {
# Load and save the settings into print, filament and printer presets.
wxTheApp->{preset_bundle}->load_config('My Settings', $result->{config});
} else {
# Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $result->{preset_name} . '.ini');
}
};
Slic3r::GUI::catch_error($self) and return;
# Load the currently selected preset into the GUI, update the preset selection box.
foreach my $tab (values %{$self->{options_tabs}}) {
$tab->load_current_preset;
}
}
}

View file

@ -99,6 +99,7 @@ sub new {
$self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click);
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
$self->{canvas3D}->set_on_arrange(sub { $self->arrange });
$self->{canvas3D}->set_on_rotate_object_left(sub { $self->rotate(-45, Z, 'relative') });
$self->{canvas3D}->set_on_rotate_object_right(sub { $self->rotate( 45, Z, 'relative') });
$self->{canvas3D}->set_on_scale_object_uniformly(sub { $self->changescale(undef) });
@ -429,6 +430,7 @@ sub new {
$grid_sizer->AddGrowableCol(3, 1);
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
my @info = (
fil_m => "Used Filament (m)",
fil_mm3 => "Used Filament (mm^3)",
fil_g => "Used Filament (g)",
cost => "Cost",
@ -504,6 +506,7 @@ sub _on_select_preset {
wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
}
if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
# Only update the platter UI for the 2nd and other filaments.
wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice);
} else {
# call GetSelection() in scalar context as it's context-aware
@ -1290,9 +1293,11 @@ sub export_gcode {
# select output file
if ($output_file) {
$self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file);
$self->{export_gcode_output_file} = eval { $self->{print}->output_filepath($output_file) };
Slic3r::GUI::catch_error($self) and return;
} else {
my $default_output_file = $self->{print}->output_filepath($main::opt{output} // '');
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
Slic3r::GUI::catch_error($self) and return;
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -1423,6 +1428,7 @@ sub on_export_completed {
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
$self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight));
$self->{"print_info_fil_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume));
$self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000));
$self->{"print_info_box_show"}->(1);
# this updates buttons status
@ -1437,8 +1443,8 @@ sub do_print {
my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config);
my $filament_stats = $self->{print}->filament_stats;
my @filament_names = wxTheApp->{preset_bundle}->filament_presets;
$filament_stats = { map { $filament_names[$_] => $filament_stats->{$_} } keys %$filament_stats };
my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
$filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
$self->GetFrame->select_tab(1);
@ -1494,6 +1500,7 @@ sub reload_from_disk {
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
#FIXME convert to local file encoding
return if !$model_object->input_file
|| !-e $model_object->input_file;
@ -1504,12 +1511,14 @@ sub reload_from_disk {
my $o = $self->{model}->objects->[$new_obj_idx];
$o->clear_instances;
$o->add_instance($_) for @{$model_object->instances};
#$o->invalidate_bounding_box;
if ($o->volumes_count == $model_object->volumes_count) {
for my $i (0..($o->volumes_count-1)) {
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
}
}
#FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
}
$self->remove($obj_idx);
@ -1540,7 +1549,8 @@ sub export_amf {
sub _get_export_file {
my ($self, $format) = @_;
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
my $output_file = $self->{print}->output_filepath($main::opt{output} // '');
my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
Slic3r::GUI::catch_error($self) and return undef;
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@ -1917,23 +1927,24 @@ sub object_menu {
my $frame = $self->GetFrame;
my $menu = Wx::Menu->new;
$frame->_append_menu_item($menu, "Delete\t\xA0Del", 'Remove the selected object', sub {
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
$frame->_append_menu_item($menu, $accel->('Delete', 'Del'), 'Remove the selected object', sub {
$self->remove;
}, undef, 'brick_delete.png');
$frame->_append_menu_item($menu, "Increase copies\t\xA0+", 'Place one more copy of the selected object', sub {
$frame->_append_menu_item($menu, $accel->('Increase copies', '+'), 'Place one more copy of the selected object', sub {
$self->increase;
}, undef, 'add.png');
$frame->_append_menu_item($menu, "Decrease copies\t\xA0-", 'Remove one copy of the selected object', sub {
$frame->_append_menu_item($menu, $accel->('Decrease copies', '-'), 'Remove one copy of the selected object', sub {
$self->decrease;
}, undef, 'delete.png');
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
$self->set_number_of_copies;
}, undef, 'textfield.png');
$menu->AppendSeparator();
$frame->_append_menu_item($menu, "Rotate 45° clockwise\t\xA0l", 'Rotate the selected object by 45° clockwise', sub {
$frame->_append_menu_item($menu, $accel->('Rotate 45° clockwise', 'l'), 'Rotate the selected object by 45° clockwise', sub {
$self->rotate(-45, Z, 'relative');
}, undef, 'arrow_rotate_clockwise.png');
$frame->_append_menu_item($menu, "Rotate 45° counter-clockwise\t\xA0r", 'Rotate the selected object by 45° counter-clockwise', sub {
$frame->_append_menu_item($menu, $accel->('Rotate 45° counter-clockwise', 'r'), 'Rotate the selected object by 45° counter-clockwise', sub {
$self->rotate(+45, Z, 'relative');
}, undef, 'arrow_rotate_anticlockwise.png');
@ -1966,7 +1977,7 @@ sub object_menu {
my $scaleMenu = Wx::Menu->new;
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleMenu, "Uniformly…\t\xA0s", 'Scale the selected object along the XYZ axes', sub {
$frame->_append_menu_item($scaleMenu, $accel->('Uniformly…', 's'), 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef);
});
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {

View file

@ -9,7 +9,7 @@ use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
use base qw(Slic3r::GUI::3DScene Class::Accessor);
__PACKAGE__->mk_accessors(qw(
on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
on_remove_object on_increase_objects on_decrease_objects));
sub new {
@ -88,7 +88,9 @@ sub new {
$event->Skip;
} else {
my $key = $event->GetKeyCode;
if ($key == ord('l')) {
if ($key == ord('a')) {
$self->on_arrange->() if $self->on_arrange;
} elsif ($key == ord('l')) {
$self->on_rotate_object_left->() if $self->on_rotate_object_left;
} elsif ($key == ord('r')) {
$self->on_rotate_object_right->() if $self->on_rotate_object_right;
@ -122,6 +124,11 @@ sub set_on_right_click {
$self->on_right_click($cb);
}
sub set_on_arrange {
my ($self, $cb) = @_;
$self->on_arrange($cb);
}
sub set_on_rotate_object_left {
my ($self, $cb) = @_;
$self->on_rotate_object_left($cb);

View file

@ -72,6 +72,13 @@ sub new {
'if they are marked as incompatible with the active printer',
default => $app_config->get("show_incompatible_presets"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'use_legacy_opengl',
type => 'bool',
label => 'Use legacy OpenGL 1.1 rendering',
tooltip => 'If you have rendering issues caused by a buggy OpenGL 2.0 driver, you may try to check this checkbox. This will disable the layer height editing and anti aliasing, so it is likely better to upgrade your graphics driver.',
default => $app_config->get("use_legacy_opengl"),
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
@ -90,7 +97,8 @@ sub _accept {
my ($self) = @_;
if (defined($self->{values}{no_controller}) ||
defined($self->{values}{no_defaults})) {
defined($self->{values}{no_defaults}) ||
defined($self->{values}{use_legacy_opengl})) {
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
}

View file

@ -147,11 +147,13 @@ sub save_preset {
return unless $dlg->ShowModal == wxID_OK;
$name = $dlg->get_name;
}
# Save the preset into Slic3r::data_dir/section_name/preset_name.ini
# Save the preset into Slic3r::data_dir/presets/section_name/preset_name.ini
eval { $self->{presets}->save_current_preset($name); };
Slic3r::GUI::catch_error($self) and return;
# Mark the print & filament enabled if they are compatible with the currently selected preset.
wxTheApp->{preset_bundle}->update_compatible_with_printer(0);
# Add the new item into the UI component, remove dirty flags and activate the saved item.
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
$self->update_tab_ui;
# Update the selection boxes at the platter.
$self->_on_presets_changed;
}
@ -177,7 +179,7 @@ sub _toggle_show_hide_incompatible {
my ($self) = @_;
$self->{show_incompatible_presets} = ! $self->{show_incompatible_presets};
$self->_update_show_hide_incompatible_button;
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
$self->update_tab_ui;
}
sub _update_show_hide_incompatible_button {
@ -223,8 +225,9 @@ sub _update {}
# to update the "dirty" flags of the selection boxes,
# to uddate number of "filament" selection boxes when the number of extruders change.
sub _on_presets_changed {
my ($self) = @_;
$self->{on_presets_changed}->($self->{presets}) if $self->{on_presets_changed};
my ($self, $reload_dependent_tabs) = @_;
$self->{on_presets_changed}->($self->{presets}, $reload_dependent_tabs)
if $self->{on_presets_changed};
}
# For the printer profile, generate the extruder pages after a preset is loaded.
@ -237,11 +240,12 @@ sub may_discard_current_dirty_preset
my ($self, $presets, $new_printer_name) = @_;
$presets //= $self->{presets};
# Display a dialog showing the dirty options in a human readable form.
my $old_preset = $presets->get_current_preset;
my $type_name = $presets->name;
my $name = $old_preset->default ?
my $old_preset = $presets->get_current_preset;
my $type_name = $presets->name;
my $tab = ' ';
my $name = $old_preset->default ?
('Default ' . $type_name . ' preset') :
($type_name . " preset \"" . $old_preset->name . "\"");
($type_name . " preset\n$tab" . $old_preset->name);
# Collect descriptions of the dirty options.
my @option_names = ();
foreach my $opt_key (@{$presets->current_dirty_options}) {
@ -251,10 +255,10 @@ sub may_discard_current_dirty_preset
push @option_names, $name;
}
# Show a confirmation dialog with the list of dirty options.
my $changes = join "\n", map "- $_", @option_names;
my $changes = join "\n", map "$tab$_", @option_names;
my $message = (defined $new_printer_name) ?
"$name is not compatible with printer \"$new_printer_name\"\n and it has unsaved changes:" :
"$name has unsaved changes:";
"$name\n\nis not compatible with printer\n$tab$new_printer_name\n\nand it has the following unsaved changes:" :
"$name\n\nhas the following unsaved changes:";
my $confirm = Wx::MessageDialog->new($self,
$message . "\n$changes\n\nDiscard changes and continue anyway?",
'Unsaved Changes', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
@ -267,9 +271,13 @@ sub may_discard_current_dirty_preset
sub select_preset {
my ($self, $name, $force) = @_;
$force //= 0;
my $current_dirty = $self->{presets}->current_is_dirty;
my $canceled = 0;
my $printer_tab = $self->{presets}->name eq 'printer';
my $presets = $self->{presets};
# If no name is provided, select the "-- default --" preset.
$name //= $presets->default_preset->name;
my $current_dirty = $presets->current_is_dirty;
my $canceled = 0;
my $printer_tab = $presets->name eq 'printer';
my @reload_dependent_tabs = ();
if (! $force && $current_dirty && ! $self->may_discard_current_dirty_preset) {
$canceled = 1;
} elsif ($printer_tab) {
@ -277,50 +285,53 @@ sub select_preset {
# are compatible with the new printer.
# If they are not compatible and the the current print or filament are dirty, let user decide
# whether to discard the changes or keep the current printer selection.
my $new_printer_name = $name // '';
my $new_printer_preset = $self->{presets}->find_preset($new_printer_name, 1);
# my $new_nozzle_dmrs = $new_printer_preset->config->get('nozzle_diameter');
my $print_presets = wxTheApp->{preset_bundle}->print;
if ($print_presets->current_is_dirty &&
! $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) {
if ($self->may_discard_current_dirty_preset($print_presets, $new_printer_name)) {
$canceled = 1;
} else {
$print_presets->discard_current_changes;
}
my $new_printer_preset = $presets->find_preset($name, 1);
my $print_presets = wxTheApp->{preset_bundle}->print;
my $print_preset_dirty = $print_presets->current_is_dirty;
my $print_preset_compatible = $print_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
$canceled = ! $force && $print_preset_dirty && ! $print_preset_compatible &&
! $self->may_discard_current_dirty_preset($print_presets, $name);
my $filament_presets = wxTheApp->{preset_bundle}->filament;
my $filament_preset_dirty = $filament_presets->current_is_dirty;
my $filament_preset_compatible = $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_preset);
if (! $canceled && ! $force) {
$canceled = $filament_preset_dirty && ! $filament_preset_compatible &&
! $self->may_discard_current_dirty_preset($filament_presets, $name);
}
my $filament_presets = wxTheApp->{preset_bundle}->filament;
# if ((@$new_nozzle_dmrs <= 1) &&
if (! $canceled && $filament_presets->current_is_dirty &&
! $filament_presets->get_edited_preset->is_compatible_with_printer($new_printer_name)) {
if ($self->may_discard_current_dirty_preset($filament_presets, $new_printer_name)) {
$canceled = 1;
} else {
$filament_presets->discard_current_changes;
if (! $canceled) {
if (! $print_preset_compatible) {
# The preset will be switched to a different, compatible preset, or the '-- default --'.
push @reload_dependent_tabs, 'print';
$print_presets->discard_current_changes if $print_preset_dirty;
}
if (! $filament_preset_compatible) {
# The preset will be switched to a different, compatible preset, or the '-- default --'.
push @reload_dependent_tabs, 'filament';
$filament_presets->discard_current_changes if $filament_preset_dirty;
}
}
}
if ($canceled) {
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
# Trigger the on_presets_changed event so that we also restore the previous value in the plater selector.
$self->update_tab_ui;
# Trigger the on_presets_changed event so that we also restore the previous value in the plater selector,
# if this action was initiated from the platter.
$self->_on_presets_changed;
} else {
if (defined $name) {
$self->{presets}->select_preset_by_name($name);
} else {
$self->{presets}->select_preset(0);
}
$presets->discard_current_changes if $current_dirty;
$presets->select_preset_by_name($name);
# Mark the print & filament enabled if they are compatible with the currently selected preset.
# The following method should not discard changes of current print or filament presets on change of a printer profile,
# if they are compatible with the current printer.
wxTheApp->{preset_bundle}->update_compatible_with_printer(1)
if $current_dirty || $printer_tab;
# Initialize the UI from the current preset.
$self->load_current_preset;
$self->load_current_preset(\@reload_dependent_tabs);
}
}
# Initialize the UI from the current preset.
sub load_current_preset {
my ($self) = @_;
my ($self, $dependent_tab_names) = @_;
my $preset = $self->{presets}->get_current_preset;
eval {
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
@ -337,8 +348,8 @@ sub load_current_preset {
# preset dirty again
# (not sure this is true anymore now that update_dirty is idempotent)
wxTheApp->CallAfter(sub {
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
$self->_on_presets_changed;
$self->update_tab_ui;
$self->_on_presets_changed($dependent_tab_names);
});
}
@ -430,11 +441,10 @@ sub _load_key_value {
$self->{config}->set($opt_key, $value);
# Mark the print & filament enabled if they are compatible with the currently selected preset.
if ($opt_key eq 'compatible_printers') {
# $opt_key eq 'compatible_printers_condition') {
wxTheApp->{preset_bundle}->update_compatible_with_printer(0);
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets});
} else {
$self->{presets}->update_dirty_ui($self->{presets_choice});
}
$self->{presets}->update_dirty_ui($self->{presets_choice});
$self->_on_presets_changed;
$self->_update;
}
@ -485,6 +495,7 @@ sub _compatible_printers_widget {
$btn->$method;
# All printers have been made compatible with this preset.
$self->_load_key_value('compatible_printers', []) if $checkbox->GetValue;
$self->get_field('compatible_printers_condition')->toggle($checkbox->GetValue);
});
EVT_BUTTON($self, $btn, sub {
@ -506,6 +517,7 @@ sub _compatible_printers_widget {
my $value = [ @presets[$dlg->GetSelections] ];
if (!@$value) {
$checkbox->SetValue(1);
$self->get_field('compatible_printers_condition')->toggle(1);
$btn->Disable;
}
# All printers have been made compatible with this preset.
@ -523,6 +535,7 @@ sub _reload_compatible_printers_widget {
my $method = $has_any ? 'Enable' : 'Disable';
$self->{compatible_printers_checkbox}->SetValue(! $has_any);
$self->{compatible_printers_btn}->$method;
$self->get_field('compatible_printers_condition')->toggle(! $has_any);
}
sub update_ui_from_settings {
@ -538,10 +551,14 @@ sub update_ui_from_settings {
} else {
if ($self->{show_incompatible_presets}) {
$self->{show_incompatible_presets} = 0;
$self->{presets}->update_tab_ui($self->{presets_choice}, 0);
$self->update_tab_ui;
}
}
}
sub update_tab_ui {
my ($self) = @_;
$self->{presets}->update_tab_ui($self->{presets_choice}, $self->{show_incompatible_presets})
}
package Slic3r::GUI::Tab::Print;
use base 'Slic3r::GUI::Tab';
@ -825,6 +842,10 @@ sub build {
widget => $self->_compatible_printers_widget,
);
$optgroup->append_line($line);
my $option = $optgroup->get_option('compatible_printers_condition');
$option->full_width(1);
$optgroup->append_single_option_line($option);
}
}
}
@ -1194,6 +1215,10 @@ sub build {
widget => $self->_compatible_printers_widget,
);
$optgroup->append_line($line);
my $option = $optgroup->get_option('compatible_printers_condition');
$option->full_width(1);
$optgroup->append_single_option_line($option);
}
}
}
@ -1223,6 +1248,12 @@ sub _update {
for qw(min_fan_speed disable_fan_first_layers);
}
sub OnActivate {
my ($self) = @_;
$self->{volumetric_speed_description_line}->SetText(
Slic3r::GUI::PresetHints::maximum_volumetric_flow_description(wxTheApp->{preset_bundle}));
}
package Slic3r::GUI::Tab::Printer;
use base 'Slic3r::GUI::Tab';
use Wx qw(wxTheApp :sizer :button :bitmap :misc :id :icon :dialog);