merge with master

This commit is contained in:
Enrico Turri committed 2017-12-11 12:01:30 +01:00
commit 50a45949d1
177 files changed
+9348 -3124

No files matched your search

+2
View File
@@ -10,6 +10,8 @@ my %prereqs = qw(
Devel::CheckLib 0
ExtUtils::MakeMaker 6.80
ExtUtils::ParseXS 3.22
ExtUtils::XSpp 0
ExtUtils::Typemaps 0
File::Basename 0
File::Spec 0
Getopt::Long 0
+2
View File
@@ -116,6 +116,8 @@ The author of the Silk icon set is Mark James.
--gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab
--no-gui Forces the command line slicing instead of gui.
This takes precedence over --gui if both are present.
--autosave <file> Automatically export current configuration to the specified file
Output options:
+9 -17
View File
@@ -23,25 +23,14 @@ sub debugf {
our $loglevel = 0;
# load threads before Moo as required by it
our $have_threads;
BEGIN {
# Test, whether the perl was compiled with ithreads support and ithreads actually work.
use Config;
$have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
warn "threads.pm >= 1.96 is required, please update\n" if $have_threads && $threads::VERSION < 1.96;
### temporarily disable threads if using the broken Moo version
use Moo;
$have_threads = 0 if $Moo::VERSION == 1.003000;
# Disable multi threading completely by an environment value.
# This is useful for debugging as the Perl debugger does not work
# in multi-threaded context at all.
# A good interactive perl debugger is the ActiveState Komodo IDE
# or the EPIC http://www.epic-ide.org/
$have_threads = 0 if (defined($ENV{'SLIC3R_SINGLETHREADED'}) && $ENV{'SLIC3R_SINGLETHREADED'} == 1);
print "Threading disabled\n" if !$have_threads;
my $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
die "Slic3r Prusa Edition requires working Perl threads.\n" if ! $have_threads;
die "threads.pm >= 1.96 is required, please update\n" if $threads::VERSION < 1.96;
die "Perl threading is broken with this Moo version: " . $Moo::VERSION . "\n" if $Moo::VERSION == 1.003000;
$debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1);
print "Debugging output enabled\n" if $debug;
}
@@ -50,8 +39,10 @@ warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
if $^V == v5.16;
use FindBin;
# Path to the images.
our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] };
# Let the XS module know where the GUI resources reside.
set_resources_dir(decode_path($FindBin::Bin) . (($^O eq 'darwin') ? '/../Resources' : '/resources'));
set_var_dir(resources_dir() . "/icons");
use Moo 1.003001;
@@ -163,6 +154,7 @@ sub thread_cleanup {
*Slic3r::Surface::Collection::DESTROY = sub {};
*Slic3r::Print::SupportMaterial2::DESTROY = sub {};
*Slic3r::TriangleMesh::DESTROY = sub {};
*Slic3r::GUI::AppConfig::DESTROY = sub {};
*Slic3r::GUI::PresetBundle::DESTROY = sub {};
return undef; # this prevents a "Scalars leaked" warning
}
+5 -295
View File
@@ -8,44 +8,21 @@ use utf8;
use List::Util qw(first max);
# cemetery of old config settings
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang
randomize_start seal_position bed_size print_center g0 vibration_limit gcode_arcs pressure_advance);
# C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes.
# The C++ counterpart is a constant singleton.
our $Options = print_config_def();
# overwrite the hard-coded readonly value (this information is not available in XS)
$Options->{threads}{readonly} = !$Slic3r::have_threads;
# generate accessors
# Generate accessors.
{
no strict 'refs';
for my $opt_key (keys %$Options) {
*{$opt_key} = sub { $_[0]->get($opt_key) };
*{$opt_key} = sub {
#print "Slic3r::Config::accessor $opt_key\n";
$_[0]->get($opt_key)
};
}
}
# Fill in the underlying C++ Slic3r::DynamicPrintConfig with the content of the defaults
# provided by the C++ class Slic3r::FullPrintConfig.
# Used by the UI.
sub new_from_defaults {
my ($class, @opt_keys) = @_;
my $self = $class->new;
# Instantiating the C++ class Slic3r::FullPrintConfig.
my $defaults = Slic3r::Config::Full->new;
if (@opt_keys) {
$self->set($_, $defaults->get($_))
for grep $defaults->has($_), @opt_keys;
} else {
$self->apply_static($defaults);
}
return $self;
}
# From command line parameters, used by slic3r.pl
sub new_from_cli {
my $class = shift;
@@ -87,273 +64,6 @@ sub new_from_cli {
return $self;
}
sub merge {
my $class = shift;
my $config = $class->new;
$config->apply($_) for @_;
return $config;
}
# Load a flat ini file without a category into the underlying C++ Slic3r::DynamicConfig class,
# convert legacy configuration names.
sub load {
my ($class, $file) = @_;
# Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed
# explicitely to avoid having to bundle the UTF8 Perl library.
if ($file =~ /\.[gG][cC][oO][dD][eE]/ || $file =~ /\.[gG]/) {
my $config = $class->new;
$config->_load_from_gcode($file);
return $config;
} else {
my $config = $class->new;
$config->_load($file);
return $config;
}
}
# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class,
# convert legacy configuration names.
# Used to load a config bundle.
sub load_ini_hash {
my ($class, $ini_hash) = @_;
my $config = $class->new;
$config->set_deserialize($_, $ini_hash->{$_}) for keys %$ini_hash;
return $config;
}
sub clone {
my $self = shift;
my $new = (ref $self)->new;
$new->apply($self);
return $new;
}
sub get_value {
my ($self, $opt_key) = @_;
return $Options->{$opt_key}{ratio_over}
? $self->get_abs_value($opt_key)
: $self->get($opt_key);
}
# Create a hash of hashes from the underlying C++ Slic3r::DynamicPrintConfig.
# The first hash key is '_' meaning no category.
# Used to create a config bundle.
sub as_ini {
my ($self) = @_;
my $ini = { _ => {} };
foreach my $opt_key (sort @{$self->get_keys}) {
next if $Options->{$opt_key}{shortcut};
$ini->{_}{$opt_key} = $self->serialize($opt_key);
}
return $ini;
}
# this method is idempotent by design and only applies to ::DynamicConfig or ::Full
# objects because it performs cross checks
sub validate {
my $self = shift;
# -j, --threads
die "Invalid value for --threads\n"
if $self->threads < 1;
# --layer-height
die "Invalid value for --layer-height\n"
if $self->layer_height <= 0;
die "--layer-height must be a multiple of print resolution\n"
if $self->layer_height / &Slic3r::SCALING_FACTOR % 1 != 0;
# --first-layer-height
die "Invalid value for --first-layer-height\n"
if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
die "Invalid value for --first-layer-height\n"
if $self->get_value('first_layer_height') <= 0;
# --filament-diameter
die "Invalid value for --filament-diameter\n"
if grep $_ < 1, @{$self->filament_diameter};
# --nozzle-diameter
die "Invalid value for --nozzle-diameter\n"
if grep $_ < 0, @{$self->nozzle_diameter};
# --perimeters
die "Invalid value for --perimeters\n"
if $self->perimeters < 0;
# --solid-layers
die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0;
die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
# --gcode-flavor
die "Invalid value for --gcode-flavor\n"
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
die "--use-firmware-retraction is only supported by Marlin, Smoothie, Repetier and Machinekit firmware\n"
if $self->use_firmware_retraction && $self->gcode_flavor ne 'smoothie'
&& $self->gcode_flavor ne 'reprap'
&& $self->gcode_flavor ne 'machinekit'
&& $self->gcode_flavor ne 'repetier';
die "--use-firmware-retraction is not compatible with --wipe\n"
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
# --fill-pattern
die "Invalid value for --fill-pattern\n"
if !first { $_ eq $self->fill_pattern } @{$Options->{fill_pattern}{values}};
# --external-fill-pattern
die "Invalid value for --external-fill-pattern\n"
if !first { $_ eq $self->external_fill_pattern } @{$Options->{external_fill_pattern}{values}};
# --fill-density
die "The selected fill pattern is not supposed to work at 100% density\n"
if $self->fill_density == 100
&& !first { $_ eq $self->fill_pattern } @{$Options->{external_fill_pattern}{values}};
# --infill-every-layers
die "Invalid value for --infill-every-layers\n"
if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1;
# --skirt-height
die "Invalid value for --skirt-height\n"
if $self->skirt_height < -1; # -1 means as tall as the object
# --bridge-flow-ratio
die "Invalid value for --bridge-flow-ratio\n"
if $self->bridge_flow_ratio <= 0;
# extruder clearance
die "Invalid value for --extruder-clearance-radius\n"
if $self->extruder_clearance_radius <= 0;
die "Invalid value for --extruder-clearance-height\n"
if $self->extruder_clearance_height <= 0;
# --extrusion-multiplier
die "Invalid value for --extrusion-multiplier\n"
if defined first { $_ <= 0 } @{$self->extrusion_multiplier};
# --default-acceleration
die "Invalid zero value for --default-acceleration when using other acceleration settings\n"
if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration)
&& !$self->default_acceleration;
# --spiral-vase
if ($self->spiral_vase) {
# Note that we might want to have more than one perimeter on the bottom
# solid layers.
die "Can't make more than one perimeter when spiral vase mode is enabled\n"
if $self->perimeters > 1;
die "Can't make less than one perimeter when spiral vase mode is enabled\n"
if $self->perimeters < 1;
die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
if $self->fill_density > 0;
die "Spiral vase mode is not compatible with top solid layers\n"
if $self->top_solid_layers > 0;
die "Spiral vase mode is not compatible with support material\n"
if $self->support_material || $self->support_material_enforce_layers > 0;
}
# extrusion widths
{
my $max_nozzle_diameter = max(@{ $self->nozzle_diameter });
die "Invalid extrusion width (too large)\n"
if defined first { $_ > 10 * $max_nozzle_diameter }
map $self->get_abs_value_over("${_}_extrusion_width", $max_nozzle_diameter),
qw(perimeter infill solid_infill top_infill support_material first_layer);
}
# general validation, quick and dirty
foreach my $opt_key (@{$self->get_keys}) {
my $opt = $Options->{$opt_key};
next unless defined $self->$opt_key;
next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/;
my $type = $1;
my @values = ();
if ($type =~ s/\@$//) {
die "Invalid value for $opt_key\n" if ref($self->$opt_key) ne 'ARRAY';
@values = @{ $self->$opt_key };
} else {
@values = ($self->$opt_key);
}
foreach my $value (@values) {
if ($type eq 'i' || $type eq 'f' || $opt->{type} eq 'percent') {
$value =~ s/%$// if $opt->{type} eq 'percent';
die "Invalid value for $opt_key\n"
if ($type eq 'i' && $value !~ /^-?\d+$/)
|| (($type eq 'f' || $opt->{type} eq 'percent') && $value !~ /^-?(?:\d+|\d*\.\d+)$/)
|| (defined $opt->{min} && $value < $opt->{min})
|| (defined $opt->{max} && $value > $opt->{max});
} elsif ($type eq 's' && $opt->{type} eq 'select') {
die "Invalid value for $opt_key\n"
unless first { $_ eq $value } @{ $opt->{values} };
}
}
}
return 1;
}
# CLASS METHODS:
# Write a "Windows" style ini file with categories enclosed in squre brackets.
# Used by config-bundle-to-config.pl and to save slic3r.ini.
sub write_ini {
my $class = shift;
my ($file, $ini) = @_;
Slic3r::open(\my $fh, '>', $file);
binmode $fh, ':utf8';
my $localtime = localtime;
printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime";
# make sure the _ category is the first one written
foreach my $category (sort { ($a eq '_') ? -1 : ($a cmp $b) } keys %$ini) {
printf $fh "\n[%s]\n", $category if $category ne '_';
foreach my $key (sort keys %{$ini->{$category}}) {
printf $fh "%s = %s\n", $key, $ini->{$category}{$key};
}
}
close $fh;
}
# Parse a "Windows" style ini file with categories enclosed in squre brackets.
# Returns a hash of hashes over strings.
# {category}{name}=value
# Non-categorized entries are stored under a category '_'.
# Used by config-bundle-to-config.pl and to read slic3r.ini.
sub read_ini {
my $class = shift;
my ($file) = @_;
local $/ = "\n";
Slic3r::open(\my $fh, '<', $file)
or die "Unable to open $file: $!\n";
binmode $fh, ':utf8';
my $ini = { _ => {} };
my $category = '_';
while (<$fh>) {
s/\R+$//;
next if /^\s+/;
next if /^$/;
next if /^\s*#/;
if (/^\[(.+?)\]$/) {
$category = $1;
next;
}
/^(\w+) *= *(.*)/ or die "Unreadable configuration file (invalid data at line $.)\n";
$ini->{$category}{$1} = $2;
}
close $fh;
return $ini;
}
package Slic3r::Config::Static;
use parent 'Slic3r::Config';
+44 -207
View File
@@ -52,27 +52,12 @@ use constant FILE_WILDCARDS => {
};
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)};
# Datadir provided on the command line.
our $datadir;
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
our $no_controller;
our $no_plater;
our $autosave;
our @cb;
our $Settings = {
_ => {
version_check => 1,
autocenter => 1,
# Disable background processing by default as it is not stable.
background_processing => 0,
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
# By default, Prusa has the controller hidden.
no_controller => 1,
# If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
no_defaults => 1,
},
};
our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$small_font->SetPointSize(11) if &Wx::wxMAC;
our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
@@ -82,134 +67,71 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
$medium_font->SetPointSize(12);
our $grey = Wx::Colour->new(200,200,200);
#our $VERSION_CHECK_EVENT : shared = Wx::NewEventType;
our $DLP_projection_screen;
sub OnInit {
my ($self) = @_;
$self->SetAppName('Slic3r');
$self->SetAppName('Slic3rPE');
$self->SetAppDisplayName('Slic3r Prusa Edition');
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
$self->{notifier} = Slic3r::GUI::Notifier->new;
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
# locate or create data directory
# Set the Slic3r data directory at the Slic3r XS module.
# Unix: ~/.Slic3r
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
# Mac: "~/Library/Application Support/Slic3r"
$datadir ||= Wx::StandardPaths::Get->GetUserDataDir;
my $enc_datadir = Slic3r::encode_path($datadir);
Slic3r::debugf "Data directory: %s\n", $datadir;
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
Slic3r::GUI::set_wxapp($self);
# just checking for existence of $datadir is not enough: it may be an empty directory
$self->{notifier} = Slic3r::GUI::Notifier->new;
$self->{app_config} = Slic3r::GUI::AppConfig->new;
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
# just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
# supplied as argument to --datadir; in that case we should still run the wizard
my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1;
foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") {
next if -d $dir;
if (!mkdir $dir) {
my $error = "Slic3r was unable to create its data directory at $dir ($!).";
warn "$error\n";
fatal_error(undef, $error);
}
eval { $self->{preset_bundle}->setup_directories() };
if ($@) {
warn $@ . "\n";
fatal_error(undef, $@);
}
my $run_wizard = ! $self->{app_config}->exists;
# load settings
my $last_version;
if (-f "$enc_datadir/slic3r.ini") {
my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") };
$Settings = $ini if $ini;
$last_version = $Settings->{_}{version};
$Settings->{_}{autocenter} //= 1;
$Settings->{_}{background_processing} //= 1;
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
$Settings->{_}{no_controller} //= 1;
# If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available.
$Settings->{_}{no_defaults} //= 1;
$self->{app_config}->load if ! $run_wizard;
$self->{app_config}->set('version', $Slic3r::VERSION);
$self->{app_config}->save;
# Suppress the '- default -' presets.
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
eval { $self->{preset_bundle}->load_presets };
if ($@) {
warn $@ . "\n";
show_error(undef, $@);
}
$Settings->{_}{version} = $Slic3r::VERSION;
$self->save_settings;
eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
$run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
# application frame
Wx::Image::AddHandler(Wx::PNGHandler->new);
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
no_controller => $no_controller // $Settings->{_}{no_controller},
no_controller => $self->{app_config}->get('no_controller'),
no_plater => $no_plater,
);
$self->SetTopWindow($frame);
# load init bundle
{
my @dirs = ($FindBin::Bin);
if (&Wx::wxMAC) {
push @dirs, qw();
} elsif (&Wx::wxMSW) {
push @dirs, qw();
}
my $init_bundle = first { -e $_ } map "$_/.init_bundle.ini", @dirs;
if ($init_bundle) {
Slic3r::debugf "Loading config bundle from %s\n", $init_bundle;
$self->{mainframe}->load_configbundle($init_bundle, 1);
$run_wizard = 0;
}
if ($run_wizard) {
$self->{mainframe}->config_wizard;
}
if (!$run_wizard && (!defined $last_version || $last_version ne $Slic3r::VERSION)) {
# user was running another Slic3r version on this computer
if (!defined $last_version || $last_version =~ /^0\./) {
show_info($self->{mainframe}, "Hello! Support material was improved since the "
. "last version of Slic3r you used. It is strongly recommended to revert "
. "your support material settings to the factory defaults and start from "
. "those. Enjoy and provide feedback!", "Support Material");
}
if (!defined $last_version || $last_version =~ /^(?:0|1\.[01])\./) {
show_info($self->{mainframe}, "Hello! In this version a new Bed Shape option was "
. "added. If the bed coordinates in the plater preview screen look wrong, go "
. "to Print Settings and click the \"Set\" button next to \"Bed Shape\".", "Bed Shape");
}
}
$self->{mainframe}->config_wizard if $run_wizard;
eval { $self->{preset_bundle}->load_presets($datadir) };
# $self->check_version
# if $self->have_version_check
# && ($Settings->{_}{version_check} // 1)
# && (!$Settings->{_}{last_version_check} || (time - $Settings->{_}{last_version_check}) >= 86400);
EVT_IDLE($frame, sub {
while (my $cb = shift @cb) {
$cb->();
}
$self->{app_config}->save if $self->{app_config}->dirty;
});
# EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub {
# my ($self, $event) = @_;
# my ($success, $response, $manual_check) = @{$event->GetData};
#
# if ($success) {
# if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) {
# my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?",
# 'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal;
# Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES;
# } else {
# Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check;
# }
# $Settings->{_}{last_version_check} = time();
# $self->save_settings;
# } else {
# Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check;
# }
# });
return 1;
}
sub about {
my ($self) = @_;
my $about = Slic3r::GUI::AboutDialog->new(undef);
$about->ShowModal;
$about->Destroy;
@@ -217,7 +139,6 @@ sub about {
sub system_info {
my ($self) = @_;
my $slic3r_info = Slic3r::slic3r_info(format => 'html');
my $copyright_info = Slic3r::copyright_info(format => 'html');
my $system_info = Slic3r::system_info(format => 'html');
@@ -295,11 +216,6 @@ sub notify {
$self->{notifier}->notify($message);
}
sub save_settings {
my ($self) = @_;
Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings);
}
# Called after the Preferences dialog is closed and the program settings are saved.
# Update the UI based on the current preferences.
sub update_ui_from_settings {
@@ -307,64 +223,11 @@ sub update_ui_from_settings {
$self->{mainframe}->update_ui_from_settings;
}
sub presets {
my ($self, $section) = @_;
my %presets = ();
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
# Instead of using the /i modifier for case-insensitive matching, the case insensitivity is expressed
# explicitely to avoid having to bundle the UTF8 Perl library.
foreach my $file (grep /\.[iI][nN][iI]$/, readdir $dh) {
$file = Slic3r::decode_path($file);
my $name = basename($file);
$name =~ s/\.ini$//;
$presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
}
closedir $dh;
return %presets;
}
#sub have_version_check {
# my ($self) = @_;
#
# # return an explicit 0
# return ($Slic3r::have_threads && $Slic3r::build && $have_LWP) || 0;
#}
#sub check_version {
# my ($self, $manual_check) = @_;
#
# Slic3r::debugf "Checking for updates...\n";
#
# @_ = ();
# threads->create(sub {
# my $ua = LWP::UserAgent->new;
# $ua->timeout(10);
# my $response = $ua->get('http://slic3r.org/updatecheck');
# Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $VERSION_CHECK_EVENT,
# threads::shared::shared_clone([ $response->is_success, $response->decoded_content, $manual_check ])));
# Slic3r::thread_cleanup();
# })->detach;
#}
sub output_path {
my ($self, $dir) = @_;
return ($Settings->{_}{last_output_path} && $Settings->{_}{remember_output_path})
? $Settings->{_}{last_output_path}
: $dir;
}
sub open_model {
my ($self, $window) = @_;
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory}
|| $Slic3r::GUI::Settings->{recent}{config_directory}
|| '';
my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):', $dir, "",
my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):',
$self->{app_config}->get_last_dir, "",
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
@@ -380,31 +243,6 @@ sub CallAfter {
push @cb, $cb;
}
sub scan_serial_ports {
my ($self) = @_;
my @ports = ();
if ($^O eq 'MSWin32') {
# Windows
if (eval "use Win32::TieRegistry; 1") {
my $ts = Win32::TieRegistry->new("HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM",
{ Access => 'KEY_READ' });
if ($ts) {
# when no serial ports are available, the registry key doesn't exist and
# TieRegistry->new returns undef
$ts->Tie(\my %reg);
push @ports, sort values %reg;
}
}
} else {
# UNIX and OS X
push @ports, glob '/dev/{ttyUSB,ttyACM,tty.,cu.,rfcomm}*';
}
return grep !/Bluetooth|FireFly/, @ports;
}
sub append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
@@ -434,32 +272,31 @@ sub set_menu_item_icon {
# SetBitmap was not available on OS X before Wx 0.9927
if ($icon && $menuItem->can('SetBitmap')) {
$menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
$menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
}
}
sub save_window_pos {
my ($self, $window, $name) = @_;
$Settings->{_}{"${name}_pos"} = join ',', $window->GetScreenPositionXY;
$Settings->{_}{"${name}_size"} = join ',', $window->GetSizeWH;
$Settings->{_}{"${name}_maximized"} = $window->IsMaximized;
$self->save_settings;
$self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY);
$self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH);
$self->{app_config}->set("${name}_maximized", $window->IsMaximized);
$self->{app_config}->save;
}
sub restore_window_pos {
my ($self, $window, $name) = @_;
if (defined $Settings->{_}{"${name}_pos"}) {
my $size = [ split ',', $Settings->{_}{"${name}_size"}, 2 ];
if ($self->{app_config}->has("${name}_pos")) {
my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ];
$window->SetSize($size);
my $display = Wx::Display->new->GetClientArea();
my $pos = [ split ',', $Settings->{_}{"${name}_pos"}, 2 ];
my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ];
if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) {
$window->Move($pos);
}
$window->Maximize(1) if $Settings->{_}{"${name}_maximized"};
$window->Maximize(1) if $self->{app_config}->get("${name}_maximized");
}
}
+1 -1
View File
@@ -1390,7 +1390,7 @@ sub _load_image_set_texture {
my ($self, $file_name) = @_;
# Load a PNG with an alpha channel.
my $img = Wx::Image->new;
$img->LoadFile($Slic3r::var->($file_name), wxBITMAP_TYPE_PNG);
$img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG);
# Get RGB & alpha raw data from wxImage, interleave them into a Perl array.
my @rgb = unpack 'C*', $img->GetData();
my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3);
+1 -1
View File
@@ -97,7 +97,7 @@ sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{logo} = Wx::Bitmap->new($Slic3r::var->("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
$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);
+67 -26
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) = @_;
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);
$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,13 @@ sub add_page {
}
sub run {
my $self = shift;
my ($self) = @_;
my $result = undef;
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};
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 +67,13 @@ 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 = $self->{config};
} else {
$result = $preset_name;
}
$self->Destroy;
return $self->{config};
} else {
$self->Destroy;
return undef;
}
$self->Destroy;
return $result;
}
package Slic3r::GUI::ConfigWizard::Index;
@@ -89,11 +89,11 @@ sub new {
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->{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->{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);
@@ -127,6 +127,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++;
}
@@ -202,7 +204,7 @@ sub append_option {
# populate repository with the factory default
my ($opt_key, $opt_index) = split /#/, $full_key, 2;
$self->config->apply(Slic3r::Config->new_from_defaults($opt_key));
$self->config->apply(Slic3r::Config::new_from_defaults_keys([$opt_key]));
# draw the control
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
@@ -263,19 +265,58 @@ 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);
sub new {
my $class = shift;
my ($parent) = @_;
my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
$self->{full_wizard_workflow} = 1;
$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.');
$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.');
# 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);
EVT_CHOICE($parent, $choice, sub {
my $sel = $self->{choice}->GetStringSelection;
$self->{preset_name} = $sel;
$self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq ''));
});
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';
@@ -300,7 +341,7 @@ sub new {
$self->append_text('Set the shape of your printer\'s bed, then click Next.');
$self->config->apply(Slic3r::Config->new_from_defaults('bed_shape'));
$self->config->apply(Slic3r::Config::new_from_defaults_keys(['bed_shape']));
$self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape);
$self->{bed_shape_panel}->on_change(sub {
$self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue);
+23 -35
View File
@@ -10,6 +10,7 @@ use utf8;
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
use base qw(Wx::ScrolledWindow Class::Accessor);
use List::Util qw(first);
__PACKAGE__->mk_accessors(qw(_selected_printer_preset));
@@ -32,27 +33,25 @@ sub new {
# button for adding new printer panels
{
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
my $btn = $self->{btn_add} = 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');
my $menu = Wx::Menu->new;
my @panels = $self->print_panels;
# remove printers that already exist
my @panels = $self->print_panels;
delete $presets{$_} for map $_->printer_name, @panels;
foreach my $preset_name (sort keys %presets) {
my $config = Slic3r::Config->load($presets{$preset_name});
next if !$config->serial_port;
# update configs of currently loaded print panels
foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) {
my $preset_name = $preset->name;
next if ! $preset->config->serial_port ||
defined first { defined $_ && $_->printer_name eq $preset_name } @panels;
my $myconfig = $preset->config->clone_only(\@ConfigOptions);
my $id = &Wx::NewId();
$menu->Append($id, $preset_name);
EVT_MENU($menu, $id, sub {
$self->add_printer($preset_name, $config);
$self->add_printer($preset_name, $myconfig);
});
}
$self->PopupMenu($menu, $btn->GetPosition);
@@ -98,12 +97,8 @@ sub OnActivate {
my ($self) = @_;
# get all available presets
my %presets = ();
{
my %all = wxTheApp->presets('printer');
my %configs = map { my $name = $_; $name => Slic3r::Config->load($all{$name}) } keys %all;
%presets = map { $_ => $configs{$_} } grep $configs{$_}->serial_port, keys %all;
}
my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) }
grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer};
# decide which ones we want to keep
my %active = ();
@@ -121,7 +116,7 @@ sub OnActivate {
}
if (!%active) {
# enable printers whose port is available
my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports;
my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports;
$active{$_} = 1
for grep exists $ports{$presets{$_}->serial_port}, keys %presets;
}
@@ -177,26 +172,19 @@ sub print_panels {
map $_->GetWindow, $self->{sizer}->GetChildren;
}
# Called by
# Slic3r::GUI::Tab::Print::_on_presets_changed
# Slic3r::GUI::Tab::Filament::_on_presets_changed
# Slic3r::GUI::Tab::Printer::_on_presets_changed
# when the presets are loaded or the user select another preset.
# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed
# when the presets are loaded or the user selects another preset.
sub update_presets {
my $self = shift;
my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_;
my ($self, $presets) = @_;
# update configs of currently loaded print panels
my @presets = @$presets;
foreach my $panel ($self->print_panels) {
foreach my $preset (@$presets) {
if ($panel->printer_name eq $preset->name) {
my $config = $preset->config(\@ConfigOptions);
$panel->config->apply($config);
}
}
my $preset = $presets->find_preset($panel->printer_name, 0);
$panel->config($preset->config->clone_only(\@ConfigOptions))
if defined $preset;
}
$self->_selected_printer_preset($presets->[$selected]->name);
$self->_selected_printer_preset($presets->get_selected_preset->name);
}
1;
@@ -34,7 +34,7 @@ sub new {
my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
wxBU_LEFT | wxBU_EXACTFIT);
$btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("$icon.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapPosition($pos);
EVT_BUTTON($self, $btn, $handler);
$sizer->Add($btn, 1, wxEXPAND | wxALL, 0);
+14 -14
View File
@@ -103,7 +103,7 @@ sub new {
$serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
}
{
$self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -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);
$btn->SetToolTipString("Rescan serial ports")
if $btn->can('SetToolTipString');
@@ -127,7 +127,7 @@ sub new {
{
$self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize);
$btn->SetFont($Slic3r::GUI::small_font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG));
$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);
}
@@ -140,7 +140,7 @@ sub new {
my $font = $btn->GetFont;
$font->SetPointSize($font->GetPointSize + 2);
$btn->SetFont($font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("arrow_up.png"), wxBITMAP_TYPE_PNG));
$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);
}
@@ -160,7 +160,7 @@ sub new {
{
$self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize);
$btn->SetFont($Slic3r::GUI::small_font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG));
$btn->Hide;
$left_sizer->Add($btn, 0, wxTOP, 15);
EVT_BUTTON($self, $btn, sub {
@@ -348,7 +348,7 @@ sub update_serial_ports {
my $cb = $self->{serial_port_combobox};
my $current = $cb->GetValue;
$cb->Clear;
$cb->Append($_) for wxTheApp->scan_serial_ports;
$cb->Append($_) for Slic3r::GUI::scan_serial_ports;
$cb->SetValue($current);
}
@@ -577,7 +577,7 @@ sub new {
$btn->SetToolTipString("Delete this job from print queue")
if $btn->can('SetToolTipString');
$btn->SetFont($Slic3r::GUI::small_font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG));
if ($job->printing) {
$btn->Hide;
}
@@ -597,8 +597,8 @@ sub new {
my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
$button_style);
$btn->SetFont($Slic3r::GUI::small_bold_font);
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
#$btn->SetBitmapPosition(wxRIGHT);
$btn->Hide;
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
@@ -616,8 +616,8 @@ sub new {
if (!$job->printing || $job->paused) {
$btn->Hide;
}
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_pause.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_pause_blue.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
@@ -633,8 +633,8 @@ sub new {
if (!$job->printing || !$job->paused) {
$btn->Hide;
}
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_play.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_play_blue.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
@@ -650,8 +650,8 @@ sub new {
if (!$job->printing) {
$btn->Hide;
}
$btn->SetBitmap(Wx::Bitmap->new($Slic3r::var->("control_stop.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new($Slic3r::var->("control_stop_blue.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG));
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG));
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
EVT_BUTTON($self, $btn, sub {
+166 -241
View File
@@ -6,6 +6,7 @@ use warnings;
use utf8;
use File::Basename qw(basename dirname);
use FindBin;
use List::Util qw(min);
use Slic3r::Geometry qw(X Y);
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
@@ -21,13 +22,14 @@ 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 fron the ico file.
my $iconfile = $Slic3r::var->('..\slic3r.exe');
$iconfile = $Slic3r::var->("Slic3r.ico") unless -f $iconfile;
# Load the icon either from the exe, or from the ico file.
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
$iconfile = Slic3r::var("Slic3r.ico") unless -f $iconfile;
$self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO));
} else {
$self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
$self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
}
# store input params
@@ -69,15 +71,15 @@ sub new {
# declare events
EVT_CLOSE($self, sub {
my (undef, $event) = @_;
if ($event->CanVeto && !$self->check_unsaved_changes) {
$event->Veto;
return;
}
# save window size
wxTheApp->save_window_pos($self, "main_frame");
# Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
# but in rare cases it may not have been called yet.
wxTheApp->{app_config}->save;
# propagate event
$event->Skip;
});
@@ -91,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');
@@ -112,7 +116,7 @@ sub _init_tabpanel {
# Callback to be executed after any of the configuration fields (Perl class Slic3r::GUI::OptionsGroup::Field) change their value.
$tab->on_value_change(sub {
my ($opt_key, $value) = @_;
my $config = $tab->config;
my $config = $tab->{presets}->get_current_preset->config;
if ($self->{plater}) {
$self->{plater}->on_config_change($config); # propagate config change events to the plater
$self->{plater}->on_extruders_change($value) if $opt_key eq 'extruders_count';
@@ -126,24 +130,38 @@ sub _init_tabpanel {
if ($self->{plater}) {
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
$self->{plater}->update_presets($tab_name, @_);
$self->{plater}->on_config_change($tab->config);
if ($self->{controller}) {
$self->{controller}->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});
# Update the controller printers.
$self->{controller}->update_presets(@_) if $self->{controller};
}
$self->{plater}->on_config_change($tab->{presets}->get_current_preset->config);
}
});
$tab->load_presets;
# Load the currently selected preset into the GUI, update the preset selection box.
$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 {
my ($group, $i) = @_;
$self->{options_tabs}{$group}->select_preset($i);
my ($group, $name) = @_;
$self->{options_tabs}{$group}->select_preset($name);
});
# load initial config
$self->{plater}->on_config_change($self->config);
my $full_config = wxTheApp->{preset_bundle}->full_config;
$self->{plater}->on_config_change($full_config);
# Show a correct number of filament fields.
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
}
}
@@ -153,6 +171,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');
@@ -262,13 +283,14 @@ 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
@@ -317,6 +339,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);
}
}
@@ -329,7 +353,6 @@ sub is_loaded {
# Selection of a 3D object changed on the platter.
sub on_plater_selection_changed {
my ($self, $have_selection) = @_;
return if !defined $self->{object_menu};
$self->{object_menu}->Enable($_->GetId, $have_selection)
for $self->{object_menu}->GetMenuItems;
@@ -337,20 +360,20 @@ sub on_plater_selection_changed {
# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
sub quick_slice {
my $self = shift;
my %params = @_;
my ($self, %params) = @_;
my $progress_dialog;
eval {
# validate configuration
my $config = $self->config;
my $config = wxTheApp->{preset_bundle}->full_config();
$config->validate;
# select input file
my $input_file;
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
if (!$params{reslice}) {
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', $dir, "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):',
wxTheApp->{app_config}->get_last_dir, "",
&Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
@@ -372,8 +395,7 @@ sub quick_slice {
$input_file = $qs_last_input_file;
}
my $input_file_basename = basename($input_file);
$Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
wxTheApp->save_settings;
wxTheApp->{app_config}->update_skein_dir(dirname($input_file));
my $print_center;
{
@@ -395,20 +417,19 @@ sub quick_slice {
$sprint->apply_config($config);
$sprint->set_model($model);
{
my $extra = $self->extra_variables;
$sprint->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
}
# Copy the names of active presets into the placeholder parser.
wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser);
# select output file
my $output_file;
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:',
wxTheApp->output_path(dirname($output_file)),
wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
@@ -416,8 +437,7 @@ sub quick_slice {
}
$output_file = $dlg->GetPath;
$qs_last_output_file = $output_file unless $params{export_svg};
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file);
wxTheApp->save_settings;
wxTheApp->{app_config}->update_last_output_dir(dirname($output_file));
$dlg->Destroy;
}
@@ -452,9 +472,7 @@ sub quick_slice {
sub reslice_now {
my ($self) = @_;
if ($self->{plater}) {
$self->{plater}->reslice;
}
$self->{plater}->reslice if $self->{plater};
}
sub repair_stl {
@@ -462,8 +480,9 @@ sub repair_stl {
my $input_file;
{
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:', $dir, "", &Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:',
wxTheApp->{app_config}->get_last_dir, "",
&Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if ($dialog->ShowModal != wxID_OK) {
$dialog->Destroy;
return;
@@ -492,36 +511,25 @@ sub repair_stl {
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
}
sub extra_variables {
my $self = shift;
my %extra_variables = ();
$extra_variables{"${_}_preset"} = $self->{options_tabs}{$_}->get_current_preset->name
for qw(print filament printer);
return { %extra_variables };
}
sub export_config {
my $self = shift;
my $config = $self->config;
eval {
# validate configuration
$config->validate;
};
# Generate a cummulative configuration for the selected print, filaments and printer.
my $config = wxTheApp->{preset_bundle}->full_config();
# Validate the cummulative configuration.
eval { $config->validate; };
Slic3r::GUI::catch_error($self) and return;
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
my $filename = $last_config ? basename($last_config) : "config.ini";
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
# Ask user for the file name for the config file.
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:',
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
$last_config ? basename($last_config) : "config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal == wxID_OK) {
my $file = $dlg->GetPath;
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings;
my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
$dlg->Destroy;
if (defined $file) {
wxTheApp->{app_config}->update_config_dir(dirname($file));
$last_config = $file;
$config->save($file);
}
$dlg->Destroy;
}
# Load a config file containing a Print, Filament & Printer preset.
@@ -529,222 +537,139 @@ sub load_config_file {
my ($self, $file) = @_;
if (!$file) {
return unless $self->check_unsaved_changes;
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
"config.ini",
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK;
$file = $dlg->GetPaths;
$dlg->Destroy;
}
for my $tab (values %{$self->{options_tabs}}) {
# Dont proceed further if the config file cannot be loaded.
return undef if ! $tab->load_config_file($file);
}
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings;
eval { wxTheApp->{preset_bundle}->load_config_file($file); };
# Dont proceed further if the config file cannot be loaded.
return if Slic3r::GUI::catch_error($self);
$_->load_current_preset for (values %{$self->{options_tabs}});
wxTheApp->{app_config}->update_config_dir(dirname($file));
$last_config = $file;
}
sub export_configbundle {
my $self = shift;
eval {
# validate current configuration in case it's dirty
$self->config->validate;
};
my ($self) = @_;
return unless $self->check_unsaved_changes;
# validate current configuration in case it's dirty
eval { wxTheApp->{preset_bundle}->full_config->validate; };
Slic3r::GUI::catch_error($self) and return;
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
my $filename = "Slic3r_config_bundle.ini";
my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename,
# Ask user for a file name.
my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:',
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
"Slic3r_config_bundle.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal == wxID_OK) {
my $file = $dlg->GetPath;
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings;
# leave default category empty to prevent the bundle from being parsed as a normal config file
my $ini = { _ => {} };
$ini->{settings}{$_} = $Slic3r::GUI::Settings->{_}{$_} for qw(autocenter);
$ini->{presets} = $Slic3r::GUI::Settings->{presets};
foreach my $section (qw(print filament printer)) {
my %presets = wxTheApp->presets($section);
foreach my $preset_name (keys %presets) {
my $config = Slic3r::Config->load($presets{$preset_name});
$ini->{"$section:$preset_name"} = $config->as_ini->{_};
}
}
Slic3r::Config->write_ini($file, $ini);
}
my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
$dlg->Destroy;
if (defined $file) {
# Export the config bundle.
wxTheApp->{app_config}->update_config_dir(dirname($file));
eval { wxTheApp->{preset_bundle}->export_configbundle($file); };
Slic3r::GUI::catch_error($self) and return;
}
}
# Loading a config bundle with an external file name used to be used
# 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, $skip_no_id) = @_;
my ($self, $file) = @_;
return unless $self->check_unsaved_changes;
if (!$file) {
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
"config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK;
$file = $dlg->GetPaths;
$dlg->Destroy;
}
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings;
# load .ini file
my $ini = Slic3r::Config->read_ini($file);
if ($ini->{settings}) {
$Slic3r::GUI::Settings->{_}{$_} = $ini->{settings}{$_} for keys %{$ini->{settings}};
wxTheApp->save_settings;
}
if ($ini->{presets}) {
$Slic3r::GUI::Settings->{presets} = $ini->{presets};
wxTheApp->save_settings;
}
wxTheApp->{app_config}->update_config_dir(dirname($file));
my $imported = 0;
INI_BLOCK: foreach my $ini_category (sort keys %$ini) {
next unless $ini_category =~ /^(print|filament|printer):(.+)$/;
my ($section, $preset_name) = ($1, $2);
my $config = Slic3r::Config->load_ini_hash($ini->{$ini_category});
next if $skip_no_id && !$config->get($section . "_settings_id");
{
my %current_presets = Slic3r::GUI->presets($section);
my %current_ids = map { $_ => 1 }
grep $_,
map Slic3r::Config->load($_)->get($section . "_settings_id"),
values %current_presets;
next INI_BLOCK if exists $current_ids{$config->get($section . "_settings_id")};
}
$config->save(sprintf "$Slic3r::GUI::datadir/%s/%s.ini", $section, $preset_name);
Slic3r::debugf "Imported %s preset %s\n", $section, $preset_name;
$imported++;
}
my $presets_imported = 0;
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); };
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_presets;
$tab->load_current_preset;
}
return if !$imported;
my $message = sprintf "%d presets successfully imported.", $imported;
my $message = sprintf "%d presets successfully imported.", $presets_imported;
Slic3r::GUI::show_info($self, $message);
}
# Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
# Also update the platter with the new presets.
sub load_config {
my $self = shift;
my ($config) = @_;
foreach my $tab (values %{$self->{options_tabs}}) {
$tab->load_config($config);
}
if ($self->{plater}) {
$self->{plater}->on_config_change($config);
}
my ($self, $config) = @_;
$_->load_config($config) foreach values %{$self->{options_tabs}};
$self->{plater}->on_config_change($config) if $self->{plater};
}
sub config_wizard {
my $self = shift;
return unless $self->check_unsaved_changes;
if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) {
for my $tab (values %{$self->{options_tabs}}) {
$tab->select_default_preset;
}
$self->load_config($config);
for my $tab (values %{$self->{options_tabs}}) {
$tab->save_preset('My Settings');
}
}
}
=head2 config
This method collects all config values from the tabs and merges them into a single config object.
=cut
sub config {
my $self = shift;
return Slic3r::Config->new_from_defaults
if !exists $self->{options_tabs}{print}
|| !exists $self->{options_tabs}{filament}
|| !exists $self->{options_tabs}{printer};
# retrieve filament presets and build a single config object for them
my $filament_config;
if (!$self->{plater} || $self->{plater}->filament_presets == 1) {
$filament_config = $self->{options_tabs}{filament}->config;
} else {
my $i = -1;
foreach my $preset_idx ($self->{plater}->filament_presets) {
$i++;
my $config;
if ($preset_idx == $self->{options_tabs}{filament}->current_preset) {
# the selected preset for this extruder is the one in the tab
# use the tab's config instead of the preset in case it is dirty
# perhaps plater shouldn't expose dirty presets at all in multi-extruder environments.
$config = $self->{options_tabs}{filament}->config;
} else {
my $preset = $self->{options_tabs}{filament}->get_preset($preset_idx);
$config = $self->{options_tabs}{filament}->get_preset_config($preset);
}
if (!$filament_config) {
$filament_config = $config->clone;
next;
}
foreach my $opt_key (@{$config->get_keys}) {
my $value = $filament_config->get($opt_key);
next unless ref $value eq 'ARRAY';
$value->[$i] = $config->get($opt_key)->[0];
$filament_config->set($opt_key, $value);
}
}
}
my $config = Slic3r::Config->merge(
Slic3r::Config->new_from_defaults,
$self->{options_tabs}{print}->config,
$self->{options_tabs}{printer}->config,
$filament_config,
);
my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
$config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
for qw(perimeter infill solid_infill support_material support_material_interface);
return $config;
}
sub filament_preset_names {
my ($self) = @_;
return map $self->{options_tabs}{filament}->get_preset($_)->name,
$self->{plater}->filament_presets;
# Exit wizard if there are unsaved changes and the user cancels the action.
return unless $self->check_unsaved_changes;
# 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);
}
}
closedir(DIR);
}
# Open the wizard.
if (my $config = Slic3r::GUI::ConfigWizard->new($self, \@profiles)->run) {
if (ref($config)) {
# Wizard returned a config. Add the config to each of the preset types.
for my $tab (values %{$self->{options_tabs}}) {
# Select the first visible preset, force.
$tab->select_preset(undef, 1);
}
# 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');
}
} else {
# Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
eval { wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $config . '.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;
}
}
}
}
# This is called when closing the application, when loading a config file or when starting the config wizard
# to notify the user whether he is aware that some preset changes will be lost.
sub check_unsaved_changes {
my $self = shift;
my @dirty = ();
foreach my $tab (values %{$self->{options_tabs}}) {
push @dirty, $tab->title if $tab->is_dirty;
push @dirty, $tab->title if $tab->{presets}->current_is_dirty;
}
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 $confirm->ShowModal == wxID_YES;
}
return 1;
@@ -765,21 +690,18 @@ sub select_view {
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon) = @_;
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description);
$self->_set_menu_item_icon($item, $icon);
EVT_MENU($self, $id, $cb);
return $item;
}
sub _set_menu_item_icon {
my ($self, $menuItem, $icon) = @_;
# SetBitmap was not available on OS X before Wx 0.9927
if ($icon && $menuItem->can('SetBitmap')) {
$menuItem->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icon), wxBITMAP_TYPE_PNG));
$menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
}
}
@@ -787,8 +709,11 @@ sub _set_menu_item_icon {
# Update the UI based on the current preferences.
sub update_ui_from_settings {
my ($self) = @_;
$self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing});
$self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing"));
$self->{plater}->update_ui_from_settings if ($self->{plater});
for my $tab_name (qw(print filament printer)) {
$self->{options_tabs}{$tab_name}->update_ui_from_settings;
}
}
1;
+1 -1
View File
@@ -6,7 +6,7 @@ use Moo;
has 'growler' => (is => 'rw');
my $icon = $Slic3r::var->("Slic3r.png");
my $icon = Slic3r::var("Slic3r.png");
sub BUILD {
my ($self) = @_;
+4
View File
@@ -124,6 +124,10 @@ sub BUILD {
});
}
sub get_value {
my ($self) = @_;
return $self->wxWindow->GetValue ? 1 : 0;
}
package Slic3r::GUI::OptionsGroup::Field::SpinCtrl;
use Moo;
+152 -300
View File
@@ -46,15 +46,14 @@ use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
my $PreventListEvents = 0;
sub new {
my $class = shift;
my ($parent) = @_;
my ($class, $parent) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
$self->{config} = Slic3r::Config->new_from_defaults(qw(
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
serial_port serial_speed octoprint_host octoprint_apikey
nozzle_diameter single_extruder_multi_material
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
));
)]);
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
$self->{model} = Slic3r::Model->new;
# C++ Slic3r::Print with Perl extensions in Slic3r/Print.pm
@@ -64,12 +63,7 @@ sub new {
$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);
}
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([$percent, $message])));
});
# Initialize preview notebook
@@ -105,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) });
@@ -120,7 +115,7 @@ sub new {
$self->GetFrame->{options_tabs}{print}->load_config($cfg);
});
$self->{canvas3D}->set_on_model_update(sub {
if ($Slic3r::GUI::Settings->{_}{background_processing}) {
if (wxTheApp->{app_config}->get("background_processing")) {
$self->schedule_background_process;
} else {
# Hide the print info box, it is no more valid.
@@ -166,22 +161,22 @@ sub new {
if (!&Wx::wxMSW) {
Wx::ToolTip::Enable(1);
$self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
$self->{htoolbar}->AddTool(TB_ADD, "Add…", Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new($Slic3r::var->("cross.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new($Slic3r::var->("bricks.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_ADD, "Add…", Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_FEWER, "Fewer", 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"), 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_SCALE, "Scale…", Wx::Bitmap->new($Slic3r::var->("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), 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_SCALE, "Scale…", Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
} else {
my %tbar_buttons = (
add => "Add…",
@@ -256,7 +251,7 @@ sub new {
settings cog.png
);
for (grep $self->{"btn_$_"}, keys %icons) {
$self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new($Slic3r::var->($icons{$_}), wxBITMAP_TYPE_PNG));
$self->{"btn_$_"}->SetBitmap(Wx::Bitmap->new(Slic3r::var($icons{$_}), wxBITMAP_TYPE_PNG));
}
$self->selection_changed(0);
$self->object_list_changed;
@@ -331,7 +326,7 @@ sub new {
$self->on_process_completed($event->GetData);
});
if ($Slic3r::have_threads) {
{
my $timer_id = Wx::NewId();
$self->{apply_config_timer} = Wx::Timer->new($self, $timer_id);
EVT_TIMER($self, $timer_id, sub {
@@ -366,20 +361,17 @@ sub new {
# once a printer preset with multiple extruders is activated.
# $self->{preset_choosers}{$group}[$idx]
$self->{preset_choosers} = {};
# Boolean indicating whether the '- default -' preset is shown by the combo box.
$self->{preset_choosers_default_suppressed} = {};
for my $group (qw(print filament printer)) {
my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
$text->SetFont($Slic3r::GUI::small_font);
my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } );
$self->{preset_choosers}{$group} = [$choice];
$self->{preset_choosers_default_suppressed}{$group} = 0;
# setup the listener
EVT_COMBOBOX($choice, $choice, sub {
my ($choice) = @_;
wxTheApp->CallAfter(sub {
$self->_on_select_preset($group, $choice);
$self->_on_select_preset($group, $choice, 0);
});
});
$presets->Add($text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
@@ -414,7 +406,7 @@ sub new {
$self->{"object_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
$self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
if ($field eq 'manifold') {
$self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new($Slic3r::var->("error.png"), wxBITMAP_TYPE_PNG));
$self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
$self->{object_info_manifold_warning_icon}->Hide;
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -438,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",
@@ -453,7 +446,6 @@ sub new {
$self->{"print_info_$field"}->SetFont($Slic3r::GUI::small_font);
$grid_sizer->Add($self->{"print_info_$field"}, 0);
}
}
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
@@ -505,30 +497,26 @@ sub on_select_preset {
$self->{on_select_preset} = $cb;
}
# Called from the platter combo boxes selecting the active print, filament or printer.
sub _on_select_preset {
my $self = shift;
my ($group, $choice) = @_;
my ($self, $group, $choice, $idx) = @_;
# If user changed a filament preset and the selected machine is equipped with multiple extruders,
# there are multiple filament selection combo boxes shown at the platter. In that case
# don't propagate the filament selection changes to the tab.
my $default_suppressed = $self->{preset_choosers_default_suppressed}{$group};
if ($group eq 'filament') {
wxTheApp->{preset_bundle}->set_filament_preset($idx, $choice->GetStringSelection);
}
if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) {
# Indices of the filaments selected.
my @filament_presets = $self->filament_presets;
$Slic3r::GUI::Settings->{presets}{filament} = $choice->GetString($filament_presets[0] - $default_suppressed) . ".ini";
$Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_] - $default_suppressed)
for 1 .. $#filament_presets;
wxTheApp->save_settings;
$self->update_filament_colors_preview($choice);
wxTheApp->{preset_bundle}->update_platter_filament_ui($idx, $choice);
} else {
# call GetSelection() in scalar context as it's context-aware
$self->{on_select_preset}->($group, scalar($choice->GetSelection) + $default_suppressed)
$self->{on_select_preset}->($group, $choice->GetStringSelection)
if $self->{on_select_preset};
}
# Synchronize config.ini with the current selections.
wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
# get new config and generate on_config_change() event for updating plater and other things
$self->on_config_change($self->GetFrame->config);
$self->on_config_change(wxTheApp->{preset_bundle}->full_config);
}
sub on_layer_editing_toggled {
@@ -558,8 +546,8 @@ sub GetFrame {
sub update_ui_from_settings
{
my ($self) = @_;
if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! $Slic3r::GUI::Settings->{_}{background_processing})) {
$self->{buttons_sizer}->Show($self->{btn_reslice}, ! $Slic3r::GUI::Settings->{_}{background_processing});
if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! wxTheApp->{app_config}->get("background_processing"))) {
$self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing"));
$self->{buttons_sizer}->Layout;
}
}
@@ -573,128 +561,41 @@ sub update_ui_from_settings
# For Print settings and Printer, synchronize the selection index with their tabs.
# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection.
sub update_presets {
my $self = shift;
# $presets: one of qw(print filament printer)
# $selected: index of the selected preset in the array. This may not correspond
# with the index of selection in the UI element, where not all items are displayed.
my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_;
my @choosers = @{ $self->{preset_choosers}{$group} };
my $choice_idx = 0;
foreach my $choice (@choosers) {
if ($group eq 'filament' && @choosers > 1) {
# if we have more than one filament chooser, keep our selection
# instead of importing the one from the tab
$selected = $choice->GetSelection + $self->{preset_choosers_default_suppressed}{$group};
$is_dirty = 0;
# $group: one of qw(print filament printer)
# $presets: PresetCollection
my ($self, $group, $presets) = @_;
my @choosers = @{$self->{preset_choosers}{$group}};
if ($group eq 'filament') {
my $choice_idx = 0;
if (int(@choosers) == 1) {
# Single filament printer, synchronize the filament presets.
wxTheApp->{preset_bundle}->set_filament_preset(0, wxTheApp->{preset_bundle}->filament->get_selected_preset->name);
}
$choice->Clear;
foreach my $preset (@$presets) {
next if ($preset->default && $default_suppressed);
my $bitmap;
if ($group eq 'filament') {
$bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG);
} elsif ($group eq 'print') {
$bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG);
} elsif ($group eq 'printer') {
$bitmap = Wx::Bitmap->new($Slic3r::var->("printer_empty.png"), wxBITMAP_TYPE_PNG);
}
$choice->AppendString($preset->name, $bitmap);
foreach my $choice (@choosers) {
wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice);
$choice_idx += 1;
}
if ($selected <= $#$presets) {
my $idx = $selected - $default_suppressed;
if ($idx >= 0) {
if ($is_dirty) {
$choice->SetString($idx, $choice->GetString($idx) . " (modified)");
}
# call SetSelection() only after SetString() otherwise the new string
# won't be picked up as the visible string
$choice->SetSelection($idx);
}
}
$choice_idx += 1;
}
$self->{preset_choosers_default_suppressed}{$group} = $default_suppressed;
wxTheApp->CallAfter(sub { $self->update_filament_colors_preview }) if $group eq 'filament' || $group eq 'printer';
}
# Update the color icon in front of each filament selection on the platter.
# If the extruder has a preview color assigned, apply the extruder color to the active selection.
# Always apply the filament color to the non-active selections.
sub update_filament_colors_preview {
my ($self, $extruder_idx) = shift;
my @choosers = @{$self->{preset_choosers}{filament}};
if (ref $extruder_idx) {
# $extruder_idx is the chooser.
foreach my $chooser (@choosers) {
if ($extruder_idx == $chooser) {
$extruder_idx = $chooser;
last;
}
} elsif ($group eq 'print') {
wxTheApp->{preset_bundle}->print->update_platter_ui($choosers[0]);
} elsif ($group eq 'printer') {
# Update the print choosers to only contain the compatible presets, update the dirty flags.
wxTheApp->{preset_bundle}->print->update_platter_ui($self->{preset_choosers}{print}->[0]);
# Update the printer choosers, update the dirty flags.
wxTheApp->{preset_bundle}->printer->update_platter_ui($choosers[0]);
# Update the filament choosers to only contain the compatible presets, update the color preview,
# update the dirty flags.
my $choice_idx = 0;
foreach my $choice (@{$self->{preset_choosers}{filament}}) {
wxTheApp->{preset_bundle}->update_platter_filament_ui($choice_idx, $choice);
$choice_idx += 1;
}
}
my @extruder_colors = @{$self->{config}->extruder_colour};
my @extruder_list;
if (defined $extruder_idx) {
@extruder_list = ($extruder_idx);
} else {
# Collect extruder indices.
@extruder_list = (0..$#extruder_colors);
}
my $filament_tab = $self->GetFrame->{options_tabs}{filament};
my $presets = $filament_tab->{presets};
my $default_suppressed = $filament_tab->{default_suppressed};
foreach my $extruder_idx (@extruder_list) {
my $chooser = $choosers[$extruder_idx];
my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx];
my $preset_idx = 0;
my $selection_idx = $chooser->GetSelection;
foreach my $preset (@$presets) {
my $bitmap;
if ($preset->default) {
next if $default_suppressed;
} else {
# Assign an extruder color to the selected item if the extruder color is defined.
my $filament_rgb = $preset->config(['filament_colour'])->filament_colour->[0];
my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb;
$filament_rgb =~ s/^#//;
$extruder_rgb =~ s/^#//;
my $image = Wx::Image->new(24,16);
if ($filament_rgb ne $extruder_rgb) {
my @rgb = unpack 'C*', pack 'H*', $extruder_rgb;
$image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb);
@rgb = unpack 'C*', pack 'H*', $filament_rgb;
$image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb);
} else {
my @rgb = unpack 'C*', pack 'H*', $filament_rgb;
$image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb);
}
$bitmap = Wx::Bitmap->new($image);
}
$chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap;
$preset_idx += 1;
}
}
}
# Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes.
sub filament_presets {
my $self = shift;
# force scalar context for GetSelection() as it's context-aware
return map scalar($_->GetSelection) + $self->{preset_choosers_default_suppressed}{filament}, @{ $self->{preset_choosers}{filament} };
# Synchronize config.ini with the current selections.
wxTheApp->{preset_bundle}->export_selections(wxTheApp->{app_config});
}
sub add {
my $self = shift;
my ($self) = @_;
my @input_files = wxTheApp->open_model($self);
$self->load_files(\@input_files);
}
@@ -759,8 +660,7 @@ sub load_files {
}
# Note the current directory for the file open dialog.
$Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_files->[-1]);
wxTheApp->save_settings;
wxTheApp->{app_config}->update_skein_dir(dirname($input_files->[-1]));
$process_dialog->Destroy;
$self->statusbar->SetStatusText("Loaded " . join(',', @loaded_files));
@@ -808,7 +708,7 @@ sub load_model_objects {
}
# if user turned autocentering off, automatic arranging would disappoint them
if (!$Slic3r::GUI::Settings->{_}{autocenter}) {
if (! wxTheApp->{app_config}->get("autocenter")) {
$need_arrange = 0;
}
@@ -921,7 +821,7 @@ sub increase {
# only autoarrange if user has autocentering enabled
$self->stop_background_process;
if ($Slic3r::GUI::Settings->{_}{autocenter}) {
if (wxTheApp->{app_config}->get("autocenter")) {
$self->arrange;
} else {
$self->update;
@@ -1164,7 +1064,7 @@ sub arrange {
$self->pause_background_process;
my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape);
my $success = $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb);
my $success = $self->{model}->arrange_objects(wxTheApp->{preset_bundle}->full_config->min_object_distance, $bb);
# ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
# when parts don't fit in print bed
@@ -1225,7 +1125,7 @@ sub async_apply_config {
$self->pause_background_process;
# apply new config
my $invalidated = $self->{print}->apply_config($self->GetFrame->config);
my $invalidated = $self->{print}->apply_config(wxTheApp->{preset_bundle}->full_config);
# Just redraw the 3D canvas without reloading the scene.
# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled);
@@ -1234,7 +1134,7 @@ sub async_apply_config {
# Hide the slicing results if the current slicing status is no more valid.
$self->{"print_info_box_show"}->(0) if $invalidated;
if ($Slic3r::GUI::Settings->{_}{background_processing}) {
if (wxTheApp->{app_config}->get("background_processing")) {
if ($invalidated) {
# kill current thread if any
$self->stop_background_process;
@@ -1256,7 +1156,6 @@ sub async_apply_config {
sub start_background_process {
my ($self) = @_;
return if !$Slic3r::have_threads;
return if !@{$self->{objects}};
return if $self->{process_thread};
@@ -1267,7 +1166,7 @@ sub start_background_process {
# don't start process thread if config is not valid
eval {
# this will throw errors if config is not valid
$self->GetFrame->config->validate;
wxTheApp->{preset_bundle}->full_config->validate;
$self->{print}->validate;
};
if ($@) {
@@ -1275,11 +1174,8 @@ sub start_background_process {
return;
}
# apply extra variables
{
my $extra = $self->GetFrame->extra_variables;
$self->{print}->placeholder_parser->set($_, $extra->{$_}) for keys %$extra;
}
# Copy the names of active presets into the placeholder parser.
wxTheApp->{preset_bundle}->export_selections_pp($self->{print}->placeholder_parser);
# start thread
@_ = ();
@@ -1350,7 +1246,7 @@ sub reslice {
# explicitly cancel a previous thread and start a new one.
my ($self) = @_;
# Don't reslice if export of G-code or sending to OctoPrint is running.
if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) {
if (! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) {
# Stop the background processing threads, stop the async update timer.
$self->stop_background_process;
# Rather perform one additional unnecessary update of the print object instead of skipping a pending async update.
@@ -1379,66 +1275,57 @@ sub export_gcode {
# (we assume that if it is running, config is valid)
eval {
# this will throw errors if config is not valid
$self->GetFrame->config->validate;
wxTheApp->{preset_bundle}->full_config->validate;
$self->{print}->validate;
};
Slic3r::GUI::catch_error($self) and return;
# apply config and validate print
my $config = $self->GetFrame->config;
my $config = wxTheApp->{preset_bundle}->full_config;
eval {
# this will throw errors if config is not valid
$config->validate;
$self->{print}->apply_config($config);
$self->{print}->validate;
};
if (!$Slic3r::have_threads) {
Slic3r::GUI::catch_error($self) and return;
}
Slic3r::GUI::catch_error($self) and return;
# 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 $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)),
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);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return;
}
my $path = $dlg->GetPath;
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path);
wxTheApp->save_settings;
wxTheApp->{app_config}->update_last_output_dir(dirname($path));
$self->{export_gcode_output_file} = $path;
$dlg->Destroy;
}
$self->statusbar->StartBusy;
if ($Slic3r::have_threads) {
$self->statusbar->SetCancelCallback(sub {
$self->stop_background_process;
$self->statusbar->SetStatusText("Export cancelled");
$self->{export_gcode_output_file} = undef;
$self->{send_gcode_file} = undef;
# this updates buttons status
$self->object_list_changed;
});
$self->statusbar->SetCancelCallback(sub {
$self->stop_background_process;
$self->statusbar->SetStatusText("Export cancelled");
$self->{export_gcode_output_file} = undef;
$self->{send_gcode_file} = undef;
# start background process, whose completion event handler
# will detect $self->{export_gcode_output_file} and proceed with export
$self->start_background_process;
} else {
eval {
$self->{print}->process;
$self->{print}->export_gcode(output_file => $self->{export_gcode_output_file});
};
my $result = !Slic3r::GUI::catch_error($self);
$self->on_export_completed($result);
}
# this updates buttons status
$self->object_list_changed;
});
# start background process, whose completion event handler
# will detect $self->{export_gcode_output_file} and proceed with export
$self->start_background_process;
# this updates buttons status
$self->object_list_changed;
@@ -1542,6 +1429,7 @@ sub on_export_completed {
$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_time"}->SetLabel(sprintf("%.2f" , $self->{print}->estimated_print_time));
$self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000));
$self->{"print_info_box_show"}->(1);
# this updates buttons status
@@ -1551,14 +1439,12 @@ sub on_export_completed {
sub do_print {
my ($self) = @_;
my $printer_tab = $self->GetFrame->{options_tabs}{printer};
my $printer_name = $printer_tab->get_current_preset->name;
my $controller = $self->GetFrame->{controller};
my $printer_panel = $controller->add_printer($printer_name, $printer_tab->config);
my $printer_preset = wxTheApp->{preset_bundle}->printer->get_edited_preset;
my $printer_panel = $controller->add_printer($printer_preset->name, $printer_preset->config);
my $filament_stats = $self->{print}->filament_stats;
my @filament_names = $self->GetFrame->filament_preset_names;
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);
@@ -1599,11 +1485,11 @@ sub send_gcode {
}
sub export_stl {
my $self = shift;
my ($self) = @_;
return if !@{$self->{objects}};
# Ask user for a file name to write into.
my $output_file = $self->_get_export_file('STL') or return;
# Store a binary STL.
$self->{model}->store_stl($output_file, 1);
$self->statusbar->SetStatusText("STL file exported to $output_file");
}
@@ -1615,6 +1501,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;
@@ -1625,59 +1512,55 @@ 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);
}
sub export_object_stl {
my $self = shift;
my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
# Ask user for a file name to write into.
my $output_file = $self->_get_export_file('STL') or return;
$model_object->mesh->write_binary($output_file);
$self->statusbar->SetStatusText("STL file exported to $output_file");
}
sub export_amf {
my $self = shift;
my ($self) = @_;
return if !@{$self->{objects}};
# Ask user for a file name to write into.
my $output_file = $self->_get_export_file('AMF') or return;
$self->{model}->store_amf($output_file);
$self->statusbar->SetStatusText("AMF file exported to $output_file");
}
# Ask user to select an output file for a given file format (STl, AMF, 3MF).
# Propose a default file name based on the 'output_filename_format' configuration value.
sub _get_export_file {
my $self = shift;
my ($format) = @_;
my ($self, $format) = @_;
my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
my $output_file = $main::opt{output};
{
$output_file = $self->{print}->output_filepath($output_file);
$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);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return undef;
}
$output_file = $dlg->GetPath;
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);
if ($dlg->ShowModal != wxID_OK) {
$dlg->Destroy;
return undef;
}
$output_file = $dlg->GetPath;
$dlg->Destroy;
return $output_file;
}
@@ -1691,7 +1574,7 @@ sub reset_thumbnail {
sub update {
my ($self, $force_autocenter) = @_;
if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) {
$self->{model}->center_instances_around_point($self->bed_centerf);
}
@@ -1714,11 +1597,13 @@ sub update {
}
# When a number of extruders changes, the UI needs to be updated to show a single filament selection combo box per extruder.
# Also the wxTheApp->{preset_bundle}->filament_presets needs to be resized accordingly
# and some reasonable default has to be selected for the additional extruders.
sub on_extruders_change {
my ($self, $num_extruders) = @_;
my $choices = $self->{preset_choosers}{filament};
while (@$choices < $num_extruders) {
while (int(@$choices) < $num_extruders) {
# copy strings from first choice
my @presets = $choices->[0]->GetStrings;
@@ -1727,25 +1612,20 @@ sub on_extruders_change {
my $extruder_idx = scalar @$choices;
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } );
push @$choices, $choice;
# copy icons from first choice
$choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
# insert new choice into sizer
$self->{presets_sizer}->Insert(4 + ($#$choices-1)*2, 0, 0);
$self->{presets_sizer}->Insert(5 + ($#$choices-1)*2, $choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING);
# setup the listener
EVT_COMBOBOX($choice, $choice, sub {
my ($choice) = @_;
wxTheApp->CallAfter(sub {
$self->_on_select_preset('filament', $choice);
$self->_on_select_preset('filament', $choice, $extruder_idx);
});
});
# initialize selection
my $i = first { $choice->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets;
$choice->SetSelection($i || 0);
wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $choice);
}
# remove unused choices if any
@@ -1759,8 +1639,7 @@ sub on_extruders_change {
}
sub on_config_change {
my $self = shift;
my ($config) = @_;
my ($self, $config) = @_;
my $update_scheduled;
foreach my $opt_key (@{$self->{config}->diff($config)}) {
@@ -1817,7 +1696,6 @@ sub on_config_change {
sub list_item_deselected {
my ($self, $event) = @_;
return if $PreventListEvents;
if ($self->{list}->GetFirstSelected == -1) {
$self->select_object(undef);
$self->{canvas}->Refresh;
@@ -1829,7 +1707,6 @@ sub list_item_deselected {
sub list_item_selected {
my ($self, $event) = @_;
return if $PreventListEvents;
my $obj_idx = $event->GetIndex;
$self->select_object($obj_idx);
$self->{canvas}->Refresh;
@@ -1861,19 +1738,18 @@ sub filament_color_box_lmouse_down
my $dialog = Wx::ColourDialog->new($self->GetFrame, $data);
if ($dialog->ShowModal == wxID_OK) {
my $cfg = Slic3r::Config->new;
my $colors = $self->GetFrame->config->get('extruder_colour');
my $colors = wxTheApp->{preset_bundle}->full_config->get('extruder_colour');
$colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX);
$cfg->set('extruder_colour', $colors);
$self->GetFrame->{options_tabs}{printer}->load_config($cfg);
$self->update_filament_colors_preview($extruder_idx);
wxTheApp->{preset_bundle}->update_platter_filament_ui($extruder_idx, $combobox);
}
$dialog->Destroy();
}
}
sub object_cut_dialog {
my $self = shift;
my ($obj_idx) = @_;
my ($self, $obj_idx) = @_;
if (!defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
@@ -1898,23 +1774,20 @@ sub object_cut_dialog {
}
sub object_settings_dialog {
my $self = shift;
my ($obj_idx) = @_;
if (!defined $obj_idx) {
($obj_idx, undef) = $self->selected_object;
}
my ($self, $obj_idx) = @_;
($obj_idx, undef) = $self->selected_object if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
# validate config before opening the settings dialog because
# that dialog can't be closed if validation fails, but user
# can't fix any error which is outside that dialog
return unless $self->validate_config;
eval { wxTheApp->{preset_bundle}->full_config->validate; };
return if Slic3r::GUI::catch_error($_[0]);
my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self,
object => $self->{objects}[$obj_idx],
model_object => $model_object,
config => $self->GetFrame->config,
config => wxTheApp->{preset_bundle}->full_config,
);
$self->pause_background_process;
$dlg->ShowModal;
@@ -1965,9 +1838,9 @@ sub object_list_changed {
for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
}
# Selection of an active 3D object changed.
sub selection_changed {
my $self = shift;
my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
my $have_sel = defined $obj_idx;
@@ -2028,7 +1901,6 @@ sub select_object {
$_->selected(0) for @{ $self->{objects} };
if (defined $obj_idx) {
$self->{objects}->[$obj_idx]->selected(1);
# We use this flag to avoid circular event handling
# Select() happens to fire a wxEVT_LIST_ITEM_SELECTED on Windows,
# whose event handler calls this method again and again and again
@@ -2042,26 +1914,13 @@ sub select_object {
}
sub selected_object {
my $self = shift;
my ($self) = @_;
my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} };
return undef if !defined $obj_idx;
return ($obj_idx, $self->{objects}[$obj_idx]),
}
sub validate_config {
my $self = shift;
eval {
$self->GetFrame->config->validate;
};
return 0 if Slic3r::GUI::catch_error($self);
return 1;
return defined $obj_idx ? ($obj_idx, $self->{objects}[$obj_idx]) : undef;
}
sub statusbar {
my $self = shift;
return $self->GetFrame->{statusbar};
return $_[0]->GetFrame->{statusbar};
}
sub object_menu {
@@ -2069,23 +1928,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');
@@ -2118,7 +1978,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 {
@@ -2187,24 +2047,19 @@ use Wx::DND;
use base 'Wx::FileDropTarget';
sub new {
my $class = shift;
my ($window) = @_;
my ($class, $window) = @_;
my $self = $class->SUPER::new;
$self->{window} = $window;
return $self;
}
sub OnDropFiles {
my $self = shift;
my ($x, $y, $filenames) = @_;
my ($self, $x, $y, $filenames) = @_;
# stop scalars leaking on older perl
# https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
@_ = ();
# only accept STL, OBJ and AMF files
return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames;
$self->{window}->load_files($filenames);
}
@@ -2220,10 +2075,8 @@ has 'selected' => (is => 'rw', default => sub { 0 });
sub make_thumbnail {
my ($self, $model, $obj_idx) = @_;
# make method idempotent
$self->thumbnail->clear;
# raw_mesh is the non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
my $mesh = $model->objects->[$obj_idx]->raw_mesh;
#FIXME The "correct" variant could be extremely slow.
@@ -2239,7 +2092,6 @@ sub make_thumbnail {
my $convex_hull = Slic3r::ExPolygon->new($mesh->convex_hull);
$self->thumbnail->append($convex_hull);
# }
return $self->thumbnail;
}
+3 -4
View File
@@ -9,7 +9,7 @@ use utf8;
use List::Util qw(min max first);
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl);
use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
use base 'Wx::Panel';
@@ -102,7 +102,7 @@ sub repaint {
}
# draw print center
if (@{$self->{objects}} && $Slic3r::GUI::Settings->{_}{autocenter}) {
if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) {
my $center = $self->unscaled_point_to_pixel($self->{print_center});
$dc->SetPen($self->{print_center_pen});
$dc->DrawLine($center->[X], 0, $center->[X], $size[Y]);
@@ -197,7 +197,6 @@ sub repaint {
sub mouse_event {
my ($self, $event) = @_;
my $pos = $event->GetPosition;
my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]]
if ($event->ButtonDown) {
@@ -257,7 +256,7 @@ sub mouse_event {
}
sub update_bed_size {
my $self = shift;
my ($self) = @_;
# when the canvas is not rendered yet, its GetSize() method returns 0,0
my $canvas_size = $self->GetSize;
+9 -2
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);
+14 -14
View File
@@ -42,9 +42,9 @@ sub new {
{
$self->{tree_icons} = Wx::ImageList->new(16, 16, 1);
$tree->AssignImageList($self->{tree_icons});
$self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
$self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
$self->{tree_icons}->Add(Wx::Bitmap->new($Slic3r::var->("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
$tree->SetPlData($rootId, { type => 'object' });
@@ -58,13 +58,13 @@ sub new {
$self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
$self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
$self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
$self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG));
$self->{btn_split}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG));
$self->{btn_move_up}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG));
$self->{btn_move_down}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG));
$self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
$self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG));
$self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG));
$self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG));
$self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG));
# buttons sizer
my $buttons_sizer = Wx::GridSizer->new(2, 3);
@@ -312,7 +312,7 @@ sub selection_changed {
$config = $self->{model_object}->config;
}
# get default values
my $default_config = Slic3r::Config->new_from_defaults(@opt_keys);
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
# append default extruder
push @opt_keys, 'extruder';
@@ -490,12 +490,12 @@ sub CanClose {
# validate options before allowing user to dismiss the dialog
# the validate method only works on full configs so we have
# to merge our settings with the default ones
my $config = Slic3r::Config->merge($self->GetParent->GetParent->GetParent->GetParent->GetParent->config, $self->model_object->config);
my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone;
eval {
$config->apply($self->model_object->config);
$config->validate;
};
return 0 if Slic3r::GUI::catch_error($self);
return 1;
return ! Slic3r::GUI::catch_error($self);
}
sub PartsChanged {
@@ -46,7 +46,7 @@ sub new {
# option selector
{
# create the button
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
EVT_LEFT_DOWN($btn, sub {
my $menu = Wx::Menu->new;
@@ -146,7 +146,7 @@ sub update_optgroup {
# disallow deleting fixed options
return undef if $self->{fixed_options}{$opt_key};
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG),
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
EVT_BUTTON($self, $btn, sub {
$self->{config}->erase($opt_key);
+20 -11
View File
@@ -10,6 +10,7 @@ sub new {
my $self = $class->SUPER::new($parent, -1, "Preferences", wxDefaultPosition, wxDefaultSize);
$self->{values} = {};
my $app_config = wxTheApp->{app_config};
my $optgroup;
$optgroup = Slic3r::GUI::OptionsGroup->new(
parent => $self,
@@ -25,7 +26,7 @@ sub new {
# type => 'bool',
# label => 'Check for updates',
# tooltip => 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.',
# default => $Slic3r::GUI::Settings->{_}{version_check} // 1,
# default => $app_config->get("version_check") // 1,
# readonly => !wxTheApp->have_version_check,
# ));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
@@ -33,36 +34,43 @@ sub new {
type => 'bool',
label => 'Remember output directory',
tooltip => 'If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files.',
default => $Slic3r::GUI::Settings->{_}{remember_output_path},
default => $app_config->get("remember_output_path"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'autocenter',
type => 'bool',
label => 'Auto-center parts',
tooltip => 'If this is enabled, Slic3r will auto-center objects around the print bed center.',
default => $Slic3r::GUI::Settings->{_}{autocenter},
default => $app_config->get("autocenter"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'background_processing',
type => 'bool',
label => 'Background processing',
tooltip => 'If this is enabled, Slic3r will pre-process objects as soon as they\'re loaded in order to save time when exporting G-code.',
default => $Slic3r::GUI::Settings->{_}{background_processing},
readonly => !$Slic3r::have_threads,
default => $app_config->get("background_processing"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_controller',
type => 'bool',
label => 'Disable USB/serial connection',
tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.',
default => $Slic3r::GUI::Settings->{_}{no_controller},
default => $app_config->get("no_controller"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'no_defaults',
type => 'bool',
label => 'Suppress "- default -" presets',
tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.',
default => $Slic3r::GUI::Settings->{_}{no_defaults},
default => $app_config->get("no_defaults"),
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'show_incompatible_presets',
type => 'bool',
label => 'Show incompatible print and filament presets',
tooltip => 'When checked, the print and filament presets are shown in the preset editor even ' .
'if they are marked as incompatible with the active printer',
default => $app_config->get("show_incompatible_presets"),
));
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
@@ -79,14 +87,15 @@ sub new {
}
sub _accept {
my $self = shift;
my ($self) = @_;
if (defined($self->{values}{no_controller})) {
if (defined($self->{values}{no_controller}) ||
defined($self->{values}{no_defaults})) {
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
}
$Slic3r::GUI::Settings->{_}{$_} = $self->{values}{$_} for keys %{$self->{values}};
wxTheApp->save_settings;
my $app_config = wxTheApp->{app_config};
$app_config->set($_, $self->{values}{$_}) for keys %{$self->{values}};
$self->EndModal(wxID_OK);
$self->Close; # needed on Linux
+431 -484
View File
File diff suppressed because it is too large. Load diff
+8 -11
View File
@@ -65,6 +65,9 @@ sub process {
}
# G-code export process, running at a background thread.
# The export_gcode may die for various reasons (fails to process output_filename_format,
# write error into the G-code, cannot execute post-processing scripts).
# It is up to the caller to show an error message.
sub export_gcode {
my $self = shift;
my %params = @_;
@@ -73,11 +76,12 @@ sub export_gcode {
$self->process;
# output everything to a G-code file
# The following call may die if the output_filename_format template substitution fails.
my $output_file = $self->output_filepath($params{output_file} // '');
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
die "G-code export to " . $output_file . " failed\n"
if ! Slic3r::GCode->new->do_export($self, $output_file);
# The following line may die for multiple reasons.
Slic3r::GCode->new->do_export($self, $output_file);
# run post-processing scripts
if (@{$self->config->post_process}) {
@@ -99,6 +103,7 @@ sub export_gcode {
}
# Export SVG slices for the offline SLA printing.
# The export_svg is expected to be executed inside an eval block.
sub export_svg {
my $self = shift;
my %params = @_;
@@ -107,6 +112,7 @@ sub export_svg {
my $fh = $params{output_fh};
if (!$fh) {
# The following line may die if the output_filename_format template substitution fails.
my $output_file = $self->output_filepath($params{output_file});
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
@@ -255,13 +261,4 @@ sub make_wipe_tower {
$self->set_step_done(STEP_WIPE_TOWER);
}
# Wrapper around the C++ Slic3r::Print::validate()
# to produce a Perl exception without a hang-up on some Strawberry perls.
sub validate
{
my $self = shift;
my $err = $self->_validate;
die $err . "\n" if (defined($err) && $err ne '');
}
1;
-3
View File
@@ -11,9 +11,6 @@ use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union
use Slic3r::Print::State ':steps';
use Slic3r::Surface ':types';
# If enabled, phases of prepare_infill will be written into SVG files to an "out" directory.
our $SLIC3R_DEBUG_SLICE_PROCESSING = 0;
sub layers {
my $self = shift;
return [ map $self->get_layer($_), 0..($self->layer_count - 1) ];
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
View File
File renamed without changes.
View File
File renamed without changes.
File diff suppressed because it is too large. Load diff
+12 -16
View File
@@ -31,6 +31,7 @@ my %cli_options = ();
'debug' => \$Slic3r::debug,
'gui' => \$opt{gui},
'no-gui' => \$opt{no_gui},
'o|output=s' => \$opt{output},
'save=s' => \$opt{save},
@@ -92,20 +93,20 @@ if ($opt{save}) {
if (@{$cli_config->get_keys} > 0) {
$cli_config->save($opt{save});
} else {
Slic3r::Config->new_from_defaults->save($opt{save});
Slic3r::Config::new_from_defaults->save($opt{save});
}
}
# apply command line config on top of default config
my $config = Slic3r::Config->new_from_defaults;
my $config = Slic3r::Config::new_from_defaults;
$config->apply($cli_config);
# launch GUI
my $gui;
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3r::GUI; 1") {
{
no warnings 'once';
$Slic3r::GUI::datadir = $opt{datadir} // '';
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
$Slic3r::GUI::no_controller = $opt{no_controller};
$Slic3r::GUI::no_plater = $opt{no_plater};
$Slic3r::GUI::autosave = $opt{autosave};
@@ -218,6 +219,8 @@ if (@ARGV) { # slicing from command line
$sprint->export_svg;
} else {
my $t0 = [gettimeofday];
# The following call may die if the output_filename_format template substitution fails,
# if the file cannot be written into, or if the post processing scripts cannot be executed.
$sprint->export_gcode;
# output some statistics
@@ -236,16 +239,7 @@ if (@ARGV) { # slicing from command line
sub usage {
my ($exit_code) = @_;
my $config = Slic3r::Config->new_from_defaults->as_hash;
my $j = '';
if ($Slic3r::have_threads) {
$j = <<"EOF";
-j, --threads <num> Number of threads to use (1+, default: $config->{threads})
EOF
}
my $config = Slic3r::Config::new_from_defaults->as_hash;
print <<"EOF";
Slic3r $Slic3r::VERSION is a STL-to-GCODE translator for RepRap 3D printers
written by Alessandro Ranellucci <aar\@cpan.org> - http://slic3r.org/
@@ -270,12 +264,14 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
them as <name>_upper.stl and <name>_lower.stl
--split Split the shells contained in given STL file into several STL files
--info Output information about the supplied file(s) and exit
$j
-j, --threads <num> Number of threads to use (1+, default: $config->{threads})
GUI options:
--gui Forces the GUI launch instead of command line slicing (if you
supply a model file, it will be loaded into the plater)
--no-plater Disable the plater tab
--no-gui Forces the command line slicing instead of gui.
This takes precedence over --gui if both are present.
--autosave <file> Automatically export current configuration to the specified file
Output options:
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff. Show more