merge with master
2
Build.PL
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
wxTheApp->{app_config}->update_config_dir(dirname($file));
|
||||
|
||||
# load .ini file
|
||||
my $ini = Slic3r::Config->read_ini($file);
|
||||
my $presets_imported = 0;
|
||||
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
# 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;
|
||||
|
@ -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) = @_;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
$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;
|
||||
});
|
||||
# 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;
|
||||
} 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);
|
||||
}
|
||||
# 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;
|
||||
}
|
||||
|
||||
|
@ -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,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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) ];
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
0
var/add.png → resources/icons/add.png
Executable file → Normal file
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 873 B |
0
var/application_view_tile.png → resources/icons/application_view_tile.png
Executable file → Normal file
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
0
var/arrow_out.png → resources/icons/arrow_out.png
Executable file → Normal file
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 715 B |
0
var/arrow_refresh.png → resources/icons/arrow_refresh.png
Executable file → Normal file
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 685 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
0
var/arrow_rotate_anticlockwise.png → resources/icons/arrow_rotate_anticlockwise.png
Executable file → Normal file
Before Width: | Height: | Size: 716 B After Width: | Height: | Size: 716 B |
0
var/arrow_rotate_clockwise.png → resources/icons/arrow_rotate_clockwise.png
Executable file → Normal file
Before Width: | Height: | Size: 706 B After Width: | Height: | Size: 706 B |
0
var/arrow_up.png → resources/icons/arrow_up.png
Executable file → Normal file
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 503 B |
0
var/box.png → resources/icons/box.png
Executable file → Normal file
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
0
var/brick.png → resources/icons/brick.png
Executable file → Normal file
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 525 B |
0
var/brick_add.png → resources/icons/brick_add.png
Executable file → Normal file
Before Width: | Height: | Size: 835 B After Width: | Height: | Size: 835 B |
0
var/brick_delete.png → resources/icons/brick_delete.png
Executable file → Normal file
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 865 B |
0
var/brick_go.png → resources/icons/brick_go.png
Executable file → Normal file
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 915 B |
0
var/bricks.png → resources/icons/bricks.png
Executable file → Normal file
Before Width: | Height: | Size: 960 B After Width: | Height: | Size: 960 B |
0
var/building.png → resources/icons/building.png
Executable file → Normal file
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 728 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 201 B After Width: | Height: | Size: 201 B |
0
var/bullet_black.png → resources/icons/bullet_black.png
Executable file → Normal file
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
0
var/bullet_blue.png → resources/icons/bullet_blue.png
Executable file → Normal file
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
0
var/bullet_green.png → resources/icons/bullet_green.png
Executable file → Normal file
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
0
var/bullet_red.png → resources/icons/bullet_red.png
Executable file → Normal file
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
0
var/bullet_white.png → resources/icons/bullet_white.png
Executable file → Normal file
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 276 B |
0
var/cog.png → resources/icons/cog.png
Executable file → Normal file
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
0
var/cog_go.png → resources/icons/cog_go.png
Executable file → Normal file
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 717 B After Width: | Height: | Size: 717 B |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 695 B After Width: | Height: | Size: 695 B |
0
var/cross.png → resources/icons/cross.png
Executable file → Normal file
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 846 B |
0
var/delete.png → resources/icons/delete.png
Executable file → Normal file
Before Width: | Height: | Size: 844 B After Width: | Height: | Size: 844 B |
0
var/disk.png → resources/icons/disk.png
Executable file → Normal file
Before Width: | Height: | Size: 974 B After Width: | Height: | Size: 974 B |
0
var/error.png → resources/icons/error.png
Executable file → Normal file
Before Width: | Height: | Size: 808 B After Width: | Height: | Size: 808 B |
BIN
resources/icons/flag-green-icon.png
Normal file
After Width: | Height: | Size: 672 B |
BIN
resources/icons/flag-red-icon.png
Normal file
After Width: | Height: | Size: 665 B |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 641 B |
0
var/hourglass.png → resources/icons/hourglass.png
Executable file → Normal file
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 806 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
0
var/joystick.png → resources/icons/joystick.png
Executable file → Normal file
Before Width: | Height: | Size: 559 B After Width: | Height: | Size: 559 B |
0
var/layers.png → resources/icons/layers.png
Executable file → Normal file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
var/lorry_add.png → resources/icons/lorry_add.png
Executable file → Normal file
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 689 B |
0
var/lorry_go.png → resources/icons/lorry_go.png
Executable file → Normal file
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
0
var/note.png → resources/icons/note.png
Executable file → Normal file
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |
0
var/package.png → resources/icons/package.png
Executable file → Normal file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
var/package_green.png → resources/icons/package_green.png
Executable file → Normal file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
var/page_white_go.png → resources/icons/page_white_go.png
Executable file → Normal file
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 702 B |
0
var/plugin.png → resources/icons/plugin.png
Executable file → Normal file
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 778 B |
0
var/plugin_add.png → resources/icons/plugin_add.png
Executable file → Normal file
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 691 B |
0
var/plugin_go.png → resources/icons/plugin_go.png
Executable file → Normal file
Before Width: | Height: | Size: 694 B After Width: | Height: | Size: 694 B |
0
var/printer_empty.png → resources/icons/printer_empty.png
Executable file → Normal file
Before Width: | Height: | Size: 424 B After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 665 B |
0
var/shape_flip_horizontal.png → resources/icons/shape_flip_horizontal.png
Executable file → Normal file
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
0
var/shape_handles.png → resources/icons/shape_handles.png
Executable file → Normal file
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 538 B |
0
var/shape_ungroup.png → resources/icons/shape_ungroup.png
Executable file → Normal file
Before Width: | Height: | Size: 803 B After Width: | Height: | Size: 803 B |
Before Width: | Height: | Size: 545 B After Width: | Height: | Size: 545 B |
0
var/tag_blue.png → resources/icons/tag_blue.png
Executable file → Normal file
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
0
var/textfield.png → resources/icons/textfield.png
Executable file → Normal file
Before Width: | Height: | Size: 153 B After Width: | Height: | Size: 153 B |
0
var/time.png → resources/icons/time.png
Executable file → Normal file
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 570 B |
0
var/wrench.png → resources/icons/wrench.png
Executable file → Normal file
Before Width: | Height: | Size: 742 B After Width: | Height: | Size: 742 B |
0
var/zoom.png → resources/icons/zoom.png
Executable file → Normal file
Before Width: | Height: | Size: 692 B After Width: | Height: | Size: 692 B |
3629
resources/profiles/Original Prusa i3 MK3, MK2S, MK2 and MK2S-MMU.ini
Normal file
26
slic3r.pl
@ -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, --threads <num> Number of threads to use (1+, default: $config->{threads})
|
||||
|
||||
$j
|
||||
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:
|
||||
|