Merge branch 'master' into wipe_tower_improvements

This commit is contained in:
Lukas Matena 2018-05-23 19:34:33 +02:00
commit 1d1c69f300
490 changed files with 201883 additions and 5792 deletions

View file

@ -646,29 +646,40 @@ void GLVolumeCollection::render_legacy() const
glDisable(GL_BLEND);
}
void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, bool all_inside)
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config)
{
if (config == nullptr)
return;
return false;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
if (opt == nullptr)
return;
return false;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
print_volume.min.z = -1e10;
bool contained = true;
for (GLVolume* volume : this->volumes)
{
if (all_inside)
if (volume != nullptr)
{
volume->is_outside = false;
continue;
bool state = print_volume.contains(volume->transformed_bounding_box());
contained &= state;
volume->is_outside = !state;
}
}
volume->is_outside = !print_volume.contains(volume->transformed_bounding_box());
return contained;
}
void GLVolumeCollection::reset_outside_state()
{
for (GLVolume* volume : this->volumes)
{
if (volume != nullptr)
volume->is_outside = false;
}
}
@ -749,13 +760,13 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
}
}
std::vector<double> GLVolumeCollection::get_current_print_zs() const
std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const
{
// Collect layer top positions of all volumes.
std::vector<double> print_zs;
for (GLVolume *vol : this->volumes)
{
if (vol->is_active)
if (!active_only || vol->is_active)
append(print_zs, vol->print_zs);
}
std::sort(print_zs.begin(), print_zs.end());
@ -1762,12 +1773,13 @@ void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* pr
else
{
_generate_legend_texture(*preview_data, tool_colors);
_load_shells(*print, *volumes, use_VBOs);
// removes empty volumes
volumes->volumes.erase(std::remove_if(volumes->volumes.begin(), volumes->volumes.end(),
[](const GLVolume *volume) { return volume->print_zs.empty(); }),
volumes->volumes.end());
_load_shells(*print, *volumes, use_VBOs);
}
}
@ -2204,7 +2216,7 @@ void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data,
return 0.0f;
}
static const GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
{
switch (data.extrusion.view_type)
{
@ -2220,7 +2232,7 @@ void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data,
return data.get_volumetric_rate_color(value);
case GCodePreviewData::Extrusion::Tool:
{
static GCodePreviewData::Color color;
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
return color;
}
@ -2445,7 +2457,7 @@ bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVol
TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
if (type != types.end())
{
type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
@ -2511,7 +2523,7 @@ bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, G
FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
if (feedrate != feedrates.end())
{
feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
@ -2577,7 +2589,7 @@ bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVol
ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
if (tool != tools.end())
{
tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z));
tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
@ -2601,7 +2613,10 @@ void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLV
{
volumes.volumes.emplace_back(volume);
for (const GCodePreviewData::Retraction::Position& position : preview_data.retraction.positions)
GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
for (const GCodePreviewData::Retraction::Position& position : copy)
{
volume->print_zs.push_back(unscale(position.position.z));
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
@ -2629,7 +2644,10 @@ void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, G
{
volumes.volumes.emplace_back(volume);
for (const GCodePreviewData::Retraction::Position& position : preview_data.unretraction.positions)
GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
for (const GCodePreviewData::Retraction::Position& position : copy)
{
volume->print_zs.push_back(unscale(position.position.z));
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());

View file

@ -422,11 +422,13 @@ public:
print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
}
void update_outside_state(const DynamicPrintConfig* config, bool all_inside);
bool check_outside_state(const DynamicPrintConfig* config);
void reset_outside_state();
void update_colors_by_extruder(const DynamicPrintConfig* config);
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
std::vector<double> get_current_print_zs() const;
std::vector<double> get_current_print_zs(bool active_only) const;
private:
GLVolumeCollection(const GLVolumeCollection &other);

View file

@ -29,20 +29,27 @@ void AboutDialogLogo::onRepaint(wxEvent &event)
}
AboutDialog::AboutDialog()
: wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxSize(600, 340), wxCAPTION)
: wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxDefaultSize, wxCAPTION)
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)/**wxWHITE*/);
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
SetBackgroundColour(bgr_clr);
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
this->SetSizer(hsizer);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20);
// logo
// AboutDialogLogo* logo = new AboutDialogLogo(this);
wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
hsizer->Add(logo, 1, wxALIGN_CENTRE_VERTICAL | wxEXPAND | wxTOP | wxBOTTOM, 35);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
hsizer->Add(vsizer, 1, wxEXPAND, 0);
#ifdef __WXMSW__
int proportion = 2;
#else
int proportion = 3;
#endif
hsizer->Add(vsizer, proportion, wxEXPAND|wxLEFT, 20);
// title
{
@ -52,7 +59,7 @@ AboutDialog::AboutDialog()
title_font.SetFamily(wxFONTFAMILY_ROMAN);
title_font.SetPointSize(24);
title->SetFont(title_font);
vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 30);
vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 10);
}
// version
@ -70,34 +77,34 @@ AboutDialog::AboutDialog()
}
// text
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER);
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO/*NEVER*/);
{
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
#ifdef __WXMSW__
int size[] = {8,8,8,8,8,8,8};
#else
int size[] = {11,11,11,11,11,11,11};
#endif
const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
const int fs = font.GetPointSize()-1;
int size[] = {fs,fs,fs,fs,fs,fs,fs};
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
html->SetHTMLBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
html->SetBorders(2);
const char* text =
const auto text = wxString::Format(
"<html>"
"<body bgcolor=\"#ffffff\" link=\"#808080\">"
"<font color=\"#808080\">"
"<body bgcolor= %s link= %s>"
"<font color=%s>"
"Copyright &copy; 2016-2018 Prusa Research. <br />"
"Copyright &copy; 2011-2017 Alessandro Ranellucci. <br />"
"<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the "
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>."
"<br /><br /><br />"
"<br /><br />"
"Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. "
"Manual by Gary Hodgson. Inspired by the RepRap community. <br />"
"Slic3r logo designed by Corey Daniels, <a href=\"http://www.famfamfam.com/lab/icons/silk/\">Silk Icon Set</a> designed by Mark James. "
"</font>"
"</body>"
"</html>";
"</html>", bgr_clr_str, text_clr_str, text_clr_str);
html->SetPage(text);
vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
vsizer->Add(html, 1, wxEXPAND | wxBOTTOM, 10);
html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this);
}
@ -108,6 +115,9 @@ AboutDialog::AboutDialog()
this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
}
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)

View file

@ -57,6 +57,9 @@ void AppConfig::set_defaults()
// https://github.com/prusa3d/Slic3r/issues/233
if (get("use_legacy_opengl").empty())
set("use_legacy_opengl", "0");
if (get("remember_output_path").empty())
set("remember_output_path", "1");
}
void AppConfig::load()
@ -100,6 +103,7 @@ void AppConfig::load()
auto ini_ver = Semver::parse(get("version"));
m_legacy_datadir = false;
if (ini_ver) {
m_orig_version = *ini_ver;
// Make 1.40.0 alphas compare well
ini_ver->set_metadata(boost::none);
ini_ver->set_prerelease(boost::none);

View file

@ -13,7 +13,13 @@ namespace Slic3r {
class AppConfig
{
public:
AppConfig() : m_dirty(false), m_legacy_datadir(false) { this->reset(); }
AppConfig() :
m_dirty(false),
m_orig_version(Semver::invalid()),
m_legacy_datadir(false)
{
this->reset();
}
// Clear and reset to defaults.
void reset();
@ -95,11 +101,16 @@ public:
// Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating)
bool legacy_datadir() const { return m_legacy_datadir; }
void set_legacy_datadir(bool value) { m_legacy_datadir = value; }
// Get the Slic3r version check url.
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
std::string version_check_url() const;
// Returns the original Slic3r version found in the ini file before it was overwritten
// by the current version
Semver orig_version() const { return m_orig_version; }
// Does the config file exist?
static bool exists();
@ -110,6 +121,8 @@ private:
VendorMap m_vendors;
// Has any value been modified since the config.ini has been last saved or loaded?
bool m_dirty;
// Original version found in the ini file before it was overwritten
Semver m_orig_version;
// Whether the existing version is before system profiles & configuration updating
bool m_legacy_datadir;
};

View file

@ -835,7 +835,9 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater
{
BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason;
if (ShowModal() == wxID_OK) {
p->apply_config(GUI::get_app_config(), preset_bundle, updater);
auto *app_config = GUI::get_app_config();
p->apply_config(app_config, preset_bundle, updater);
app_config->set_legacy_datadir(false);
BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied";
return true;
} else {

View file

@ -106,7 +106,7 @@ namespace Slic3r { namespace GUI {
}
double val;
str.ToCDouble(&val);
if (m_opt.min > val && val > m_opt.max)
if (m_opt.min > val || val > m_opt.max)
{
show_error(m_parent, _(L("Input value is out of range")));
if (m_opt.min > val) val = m_opt.min;

View file

@ -0,0 +1,402 @@
#include "FirmwareDialog.hpp"
#include <numeric>
#include <algorithm>
#include <boost/format.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/log/trivial.hpp>
#include <wx/app.h>
#include <wx/event.h>
#include <wx/sizer.h>
#include <wx/settings.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/filepicker.h>
#include <wx/textctrl.h>
#include <wx/stattext.h>
#include <wx/combobox.h>
#include <wx/gauge.h>
#include <wx/collpane.h>
#include <wx/msgdlg.h>
#include "libslic3r/Utils.hpp"
#include "avrdude/avrdude-slic3r.hpp"
#include "GUI.hpp"
#include "../Utils/Serial.hpp"
namespace fs = boost::filesystem;
namespace Slic3r {
// This enum discriminates the kind of information in EVT_AVRDUDE,
// it's stored in the ExtraLong field of wxCommandEvent.
enum AvrdudeEvent
{
AE_MESSAGE,
AE_PRORGESS,
AE_EXIT,
};
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
wxDEFINE_EVENT(EVT_AVRDUDE, wxCommandEvent);
// Private
struct FirmwareDialog::priv
{
enum AvrDudeComplete
{
AC_NONE,
AC_SUCCESS,
AC_FAILURE,
AC_CANCEL,
};
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
wxComboBox *port_picker;
std::vector<Utils::SerialPortInfo> ports;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxStaticText *txt_progress;
wxGauge *progressbar;
wxCollapsiblePane *spoiler;
wxTextCtrl *txt_stdout;
wxButton *btn_rescan;
wxButton *btn_close;
wxButton *btn_flash;
wxString btn_flash_label_ready;
wxString btn_flash_label_flashing;
// This is a shared pointer holding the background AvrDude task
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
AvrDude::Ptr avrdude;
std::string avrdude_config;
unsigned progress_tasks_done;
bool cancelled;
priv(FirmwareDialog *q) :
q(q),
btn_flash_label_ready(_(L("Flash!"))),
btn_flash_label_flashing(_(L("Cancel"))),
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
progress_tasks_done(0),
cancelled(false)
{}
void find_serial_ports();
void flashing_status(bool flashing, AvrDudeComplete complete = AC_NONE);
void perform_upload();
void cancel();
void on_avrdude(const wxCommandEvent &evt);
};
void FirmwareDialog::priv::find_serial_ports()
{
auto new_ports = Utils::scan_serial_ports_extended();
if (new_ports != this->ports) {
this->ports = new_ports;
port_picker->Clear();
for (const auto &port : this->ports)
port_picker->Append(port.friendly_name);
if (ports.size() > 0) {
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
for (int i = 0; i < (int)this->ports.size(); ++ i)
if (this->ports[i].is_printer) {
idx = i;
break;
}
if (idx != -1)
port_picker->SetSelection(idx);
}
}
}
void FirmwareDialog::priv::flashing_status(bool value, AvrDudeComplete complete)
{
if (value) {
txt_stdout->Clear();
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
txt_status->SetForegroundColour(GUI::get_label_clr_modified());
port_picker->Disable();
btn_rescan->Disable();
hex_picker->Disable();
btn_close->Disable();
btn_flash->SetLabel(btn_flash_label_flashing);
progressbar->SetRange(200); // See progress callback below
progressbar->SetValue(0);
progress_tasks_done = 0;
cancelled = false;
} else {
auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
port_picker->Enable();
btn_rescan->Enable();
hex_picker->Enable();
btn_close->Enable();
btn_flash->SetLabel(btn_flash_label_ready);
txt_status->SetForegroundColour(text_color);
progressbar->SetValue(200);
switch (complete) {
case AC_SUCCESS: txt_status->SetLabel(_(L("Flashing succeeded!"))); break;
case AC_FAILURE: txt_status->SetLabel(_(L("Flashing failed. Please see the avrdude log below."))); break;
case AC_CANCEL: txt_status->SetLabel(_(L("Flashing cancelled."))); break;
}
}
}
void FirmwareDialog::priv::perform_upload()
{
auto filename = hex_picker->GetPath();
std::string port = port_picker->GetValue().ToStdString();
int selection = port_picker->GetSelection();
if (selection != -1) {
// Verify whether the combo box list selection equals to the combo box edit value.
if (this->ports[selection].friendly_name == port)
port = this->ports[selection].port;
}
if (filename.IsEmpty() || port.empty()) { return; }
flashing_status(true);
std::vector<std::string> args {{
"-v",
"-p", "atmega2560",
"-c", "wiring",
"-P", port,
"-b", "115200", // XXX: is this ok to hardcode?
"-D",
"-U", (boost::format("flash:w:%1%:i") % filename.ToStdString()).str()
}};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
return a + ' ' + b;
});
// It is ok here to use the q-pointer to the FirmwareDialog
// because the dialog ensures it doesn't exit before the background thread is done.
auto q = this->q;
avrdude = AvrDude()
.sys_config(avrdude_config)
.args(args)
.on_message(std::move([q](const char *msg, unsigned /* size */) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
evt->SetExtraLong(AE_MESSAGE);
evt->SetString(msg);
wxQueueEvent(q, evt);
}))
.on_progress(std::move([q](const char * /* task */, unsigned progress) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
evt->SetExtraLong(AE_PRORGESS);
evt->SetInt(progress);
wxQueueEvent(q, evt);
}))
.on_complete(std::move([q](int status) {
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
evt->SetExtraLong(AE_EXIT);
evt->SetInt(status);
wxQueueEvent(q, evt);
}))
.run();
}
void FirmwareDialog::priv::cancel()
{
if (avrdude) {
cancelled = true;
txt_status->SetLabel(_(L("Cancelling...")));
avrdude->cancel();
}
}
void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
{
AvrDudeComplete complete_kind;
switch (evt.GetExtraLong()) {
case AE_MESSAGE:
txt_stdout->AppendText(evt.GetString());
break;
case AE_PRORGESS:
// We try to track overall progress here.
// When uploading the firmware, avrdude first reads a littlebit of status data,
// then performs write, then reading (verification).
// We Pulse() during the first read and combine progress of the latter two tasks.
if (progress_tasks_done == 0) {
progressbar->Pulse();
} else {
progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
}
if (evt.GetInt() == 100) {
progress_tasks_done += 100;
}
break;
case AE_EXIT:
BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt();
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
flashing_status(false, complete_kind);
// Make sure the background thread is collected and the AvrDude object reset
if (avrdude) { avrdude->join(); }
avrdude.reset();
break;
default:
break;
}
}
// Public
FirmwareDialog::FirmwareDialog(wxWindow *parent) :
wxDialog(parent, wxID_ANY, _(L("Firmware flasher")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
p(new priv(this))
{
enum {
DIALOG_MARGIN = 15,
SPACING = 10,
MIN_WIDTH = 600,
MIN_HEIGHT = 200,
MIN_HEIGHT_EXPANDED = 500,
};
wxFont status_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
status_font.MakeBold();
wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
mono_font.MakeSmaller();
auto *panel = new wxPanel(this);
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
panel->SetSizer(vsizer);
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
p->port_picker = new wxComboBox(panel, wxID_ANY);
p->btn_rescan = new wxButton(panel, wxID_ANY, _(L("Rescan")));
auto *port_sizer = new wxBoxSizer(wxHORIZONTAL);
port_sizer->Add(p->port_picker, 1, wxEXPAND | wxRIGHT, SPACING);
port_sizer->Add(p->btn_rescan, 0);
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
auto *label_status = new wxStaticText(panel, wxID_ANY, _(L("Status:")));
p->txt_status = new wxStaticText(panel, wxID_ANY, _(L("Ready")));
p->txt_status->SetFont(status_font);
auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
auto *grid = new wxFlexGridSizer(2, SPACING, SPACING);
grid->AddGrowableCol(1);
grid->Add(label_port_picker, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(port_sizer, 0, wxEXPAND);
grid->Add(label_hex_picker, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(p->hex_picker, 0, wxEXPAND);
grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(p->txt_status, 0, wxEXPAND);
grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, SPACING);
p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")));
auto *spoiler_pane = p->spoiler->GetPane();
auto *spoiler_sizer = new wxBoxSizer(wxVERTICAL);
p->txt_stdout = new wxTextCtrl(spoiler_pane, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY);
p->txt_stdout->SetFont(mono_font);
spoiler_sizer->Add(p->txt_stdout, 1, wxEXPAND);
spoiler_pane->SetSizer(spoiler_sizer);
// The doc says proportion need to be 0 for wxCollapsiblePane.
// Experience says it needs to be 1, otherwise things won't get sized properly.
vsizer->Add(p->spoiler, 1, wxEXPAND | wxBOTTOM, SPACING);
p->btn_close = new wxButton(panel, wxID_CLOSE);
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);
bsizer->Add(p->btn_close);
bsizer->AddStretchSpacer();
bsizer->Add(p->btn_flash);
vsizer->Add(bsizer, 0, wxEXPAND);
auto *topsizer = new wxBoxSizer(wxVERTICAL);
topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
SetSizerAndFit(topsizer);
const auto size = GetSize();
SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT)));
Layout();
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) {
// Dialog size gets screwed up by wxCollapsiblePane, we need to fix it here
if (evt.GetCollapsed()) {
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
} else {
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED));
}
this->Fit();
this->Layout();
});
p->btn_close->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->Close(); });
p->btn_rescan->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->p->find_serial_ports(); });
p->btn_flash->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
if (this->p->avrdude) {
// Flashing is in progress, ask the user if they're really sure about canceling it
wxMessageDialog dlg(this,
_(L("Are you sure you want to cancel firmware flashing?\nThis could leave your printer in an unusable state!")),
_(L("Confirmation")),
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() == wxID_YES) {
this->p->cancel();
}
} else {
// Start a flashing task
this->p->perform_upload();
}
});
Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); });
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) {
if (this->p->avrdude) {
evt.Veto();
} else {
evt.Skip();
}
});
p->find_serial_ports();
}
FirmwareDialog::~FirmwareDialog()
{
// Needed bacuse of forward defs
}
void FirmwareDialog::run(wxWindow *parent)
{
FirmwareDialog dialog(parent);
dialog.ShowModal();
}
}

View file

@ -0,0 +1,31 @@
#ifndef slic3r_FirmwareDialog_hpp_
#define slic3r_FirmwareDialog_hpp_
#include <memory>
#include <wx/dialog.h>
namespace Slic3r {
class FirmwareDialog: public wxDialog
{
public:
FirmwareDialog(wxWindow *parent);
FirmwareDialog(FirmwareDialog &&) = delete;
FirmwareDialog(const FirmwareDialog &) = delete;
FirmwareDialog &operator=(FirmwareDialog &&) = delete;
FirmwareDialog &operator=(const FirmwareDialog &) = delete;
~FirmwareDialog();
static void run(wxWindow *parent);
private:
struct priv;
std::unique_ptr<priv> p;
};
}
#endif

View file

@ -4,11 +4,8 @@
#include <assert.h>
#include <cmath>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#if __APPLE__
@ -25,7 +22,6 @@
#undef max
#endif
#include "boost/nowide/convert.hpp"
#pragma comment(lib, "user32.lib")
#endif
#include <wx/app.h>
@ -55,10 +51,12 @@
#include "Preferences.hpp"
#include "PresetBundle.hpp"
#include "UpdateDialogs.hpp"
#include "FirmwareDialog.hpp"
#include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp"
namespace Slic3r { namespace GUI {
#if __APPLE__
@ -86,83 +84,6 @@ void enable_screensaver()
#endif
}
std::vector<std::string> scan_serial_ports()
{
std::vector<std::string> out;
#ifdef _WIN32
// 1) Open the registry key SERIALCOM.
HKEY hKey;
LONG lRes = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey);
assert(lRes == ERROR_SUCCESS);
if (lRes == ERROR_SUCCESS) {
// 2) Get number of values of SERIALCOM key.
DWORD cValues; // number of values for key
{
TCHAR achKey[255]; // buffer for subkey name
DWORD cbName; // size of name string
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys=0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
// Get the class name and the value count.
lRes = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
assert(lRes == ERROR_SUCCESS);
}
// 3) Read the SERIALCOM values.
{
DWORD dwIndex = 0;
for (int i = 0; i < cValues; ++ i, ++ dwIndex) {
wchar_t valueName[2048];
DWORD valNameLen = 2048;
DWORD dataType;
wchar_t data[2048];
DWORD dataSize = 4096;
lRes = ::RegEnumValueW(hKey, dwIndex, valueName, &valNameLen, nullptr, &dataType, (BYTE*)&data, &dataSize);
if (lRes == ERROR_SUCCESS && dataType == REG_SZ && valueName[0] != 0)
out.emplace_back(boost::nowide::narrow(data));
}
}
::RegCloseKey(hKey);
}
#else
// UNIX and OS X
std::initializer_list<const char*> prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" };
for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) {
std::string name = dir_entry.path().filename().string();
for (const char *prefix : prefixes) {
if (boost::starts_with(name, prefix)) {
out.emplace_back(dir_entry.path().string());
break;
}
}
}
#endif
out.erase(std::remove_if(out.begin(), out.end(),
[](const std::string &key){
return boost::starts_with(key, "Bluetooth") || boost::starts_with(key, "FireFly");
}),
out.end());
return out;
}
bool debugged()
{
#ifdef _WIN32
@ -383,6 +304,7 @@ enum ConfigMenuIDs {
ConfigMenuUpdate,
ConfigMenuPreferences,
ConfigMenuLanguage,
ConfigMenuFlashFirmware,
ConfigMenuCnt,
};
@ -396,11 +318,15 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
local_menu->Append(config_id_base + ConfigMenuWizard, ConfigWizard::name() + "\u2026", config_wizard_tooltip);
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+"\u2026", _(L("Inspect / activate configuration snapshots")));
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+"\u2026\tCtrl+,", _(L("Application preferences")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
local_menu->AppendSeparator();
local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer")));
// TODO: for when we're able to flash dictionaries
// local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer")));
local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){
switch (event.GetId() - config_id_base) {
case ConfigMenuWizard:
@ -455,12 +381,22 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
}
}
break;
}
}
case ConfigMenuFlashFirmware:
FirmwareDialog::run(g_wxMainFrame);
break;
default:
break;
}
});
menu->Append(local_menu, _(L("&Configuration")));
}
void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change)
{
add_config_menu(menu, event_language_change, event_language_change);
}
// 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.
bool check_unsaved_changes()
@ -671,7 +607,7 @@ void show_error(wxWindow* parent, const wxString& message) {
void show_error_id(int id, const std::string& message) {
auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr;
show_error(parent, message);
show_error(parent, wxString::FromUTF8(message.data()));
}
void show_info(wxWindow* parent, const wxString& message, const wxString& title){
@ -809,8 +745,8 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
{
DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config;
m_optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config);
// const wxArrayInt& ar = preset_sizer->GetColWidths();
// m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front(); // doesn't work
const wxArrayInt& ar = preset_sizer->GetColWidths();
m_optgroup->label_width = ar.IsEmpty() ? 100 : ar.front()-4; // doesn't work
m_optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){
TabPrint* tab_print = nullptr;
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
@ -992,4 +928,35 @@ void about()
dlg.Destroy();
}
void desktop_open_datadir_folder()
{
// Execute command to open a file explorer, platform dependent.
std::string cmd =
#ifdef _WIN32
"explorer "
#elif __APPLE__
"open "
#else
"xdg-open "
#endif
;
// Escape the path, platform dependent.
std::string path = data_dir();
#ifdef _WIN32
// Enclose the path into double quotes on Windows. A quote character is forbidden in file names,
// therefore it does not need to be escaped.
cmd += '"';
cmd += path;
cmd += '"';
#else
// Enclose the path into single quotes on Unix / OSX. All single quote characters need to be escaped
// inside a file name.
cmd += '\'';
boost::replace_all(path, "'", "\\'");
cmd += path;
cmd += '\'';
#endif
::wxExecute(wxString::FromUTF8(cmd.c_str()), wxEXEC_ASYNC, nullptr);
}
} }

View file

@ -68,7 +68,6 @@ inline t_file_wild_card& get_file_wild_card() {
void disable_screensaver();
void enable_screensaver();
std::vector<std::string> scan_serial_ports();
bool debugged();
void break_to_debugger();
@ -91,7 +90,7 @@ unsigned get_colour_approx_luma(const wxColour &colour);
void set_label_clr_modified(const wxColour& clr);
void set_label_clr_sys(const wxColour& clr);
extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
// 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.
@ -159,7 +158,9 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
int get_export_option(wxFileDialog* dlg);
// Display an About dialog
void about();
extern void about();
// Ask the destop to open the datadir using the default file explorer.
extern void desktop_open_datadir_folder();
} // namespace GUI
} // namespace Slic3r

View file

@ -71,11 +71,12 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
text->Wrap(CONTENT_WIDTH);
p_sizer->Add(text, 1, wxEXPAND);
panel->SetMinSize(wxSize(CONTENT_WIDTH, CONTENT_HEIGHT));
panel->SetMinSize(wxSize(CONTENT_WIDTH, 0));
panel->SetScrollRate(0, 5);
content_sizer->Add(panel, 1, wxEXPAND);
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT));
Fit();
}

View file

@ -31,7 +31,7 @@ struct MsgDialog : wxDialog
protected:
enum {
CONTENT_WIDTH = 500,
CONTENT_HEIGHT = 300,
CONTENT_MAX_HEIGHT = 600,
BORDER = 30,
VERT_SPACING = 15,
HORIZ_SPACING = 5,

View file

@ -150,11 +150,11 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// Build a label if we have it
wxStaticText* label=nullptr;
if (label_width != 0) {
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
wxDefaultPosition, staticbox ? wxSize(label_width, -1) : wxDefaultSize);
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"),
wxDefaultPosition, wxSize(label_width, -1), staticbox ? 0 : wxALIGN_RIGHT);
label->SetFont(label_font);
label->Wrap(label_width); // avoid a Linux/GTK bug
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT) | wxALIGN_CENTER_VERTICAL, 0);
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
if (line.label_tooltip.compare("") != 0)
label->SetToolTip(line.label_tooltip);
}
@ -170,7 +170,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// if we have a single option with no sidetext just add it directly to the grid sizer
auto sizer = new wxBoxSizer(wxHORIZONTAL);
grid_sizer->Add(sizer, 0, wxEXPAND | wxALL, 0);
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM|wxTOP|wxLEFT), staticbox ? 0 : 1);
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
const auto& option = option_set.front();
@ -180,7 +180,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
if (is_window_field(field))
sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) |
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, wxOSX ? 0 : 2);
wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, (wxOSX||!staticbox) ? 0 : 2);
if (is_sizer_field(field))
sizer->Add(field->getSizer(), 0, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
return;

View file

@ -35,8 +35,8 @@ void PreferencesDialog::build()
def.type = coBool;
def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory "
"instead of the one containing the input files.");
def.default_value = new ConfigOptionBool{ app_config->get("remember_output_path")[0] == '1' }; // 1;
Option option(def, "remember_output_path");
def.default_value = new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path")[0] == '1' : true }; // 1;
Option option(def, "remember_output_path");
m_optgroup->append_single_option_line(option);
def.label = L("Auto-center parts");

View file

@ -188,6 +188,14 @@ void Preset::normalize(DynamicPrintConfig &config)
if (opt != nullptr && opt->is_vector())
static_cast<ConfigOptionVectorBase*>(opt)->resize(n, defaults.option(key));
}
// The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
for (const std::string &key : { "filament_settings_id" }) {
auto *opt = config.option(key, false);
assert(opt != nullptr);
assert(opt->type() == coStrings);
if (opt != nullptr && opt->type() == coStrings)
static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string());
}
}
}

View file

@ -244,6 +244,7 @@ public:
const std::string& get_suffix_modified();
// Return a preset possibly with modifications.
Preset& default_preset() { return m_presets.front(); }
const Preset& default_preset() const { return m_presets.front(); }
// Return a preset by an index. If the preset is active, a temporary copy is returned.
Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; }

View file

@ -53,27 +53,29 @@ PresetBundle::PresetBundle() :
wxImage::AddHandler(new wxPNGHandler);
// Create the ID config keys, as they are not part of the Static print config classes.
this->prints.preset(0).config.opt_string("print_settings_id", true);
this->filaments.preset(0).config.opt_string("filament_settings_id", true);
this->printers.preset(0).config.opt_string("print_settings_id", true);
// Create the "compatible printers" keys, as they are not part of the Static print config classes.
this->filaments.preset(0).config.optptr("compatible_printers", true);
this->filaments.preset(0).config.optptr("compatible_printers_condition", true);
this->prints.preset(0).config.optptr("compatible_printers", true);
this->prints.preset(0).config.optptr("compatible_printers_condition", true);
// Create the "inherits" keys.
this->prints.preset(0).config.optptr("inherits", true);
this->filaments.preset(0).config.optptr("inherits", true);
this->printers.preset(0).config.optptr("inherits", true);
this->prints.default_preset().config.opt_string("print_settings_id", true);
this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values.assign(1, std::string());
this->printers.default_preset().config.opt_string("printer_settings_id", true);
// "compatible printers" are not mandatory yet.
//FIXME Rename "compatible_printers" and "compatible_printers_condition", as they are defined in both print and filament profiles,
// therefore they are clashing when generating a a config file, G-code or AMF/3MF.
// this->filaments.default_preset().config.optptr("compatible_printers", true);
// this->filaments.default_preset().config.optptr("compatible_printers_condition", true);
// this->prints.default_preset().config.optptr("compatible_printers", true);
// this->prints.default_preset().config.optptr("compatible_printers_condition", true);
// Create the "printer_vendor", "printer_model" and "printer_variant" keys.
this->printers.preset(0).config.optptr("printer_vendor", true);
this->printers.preset(0).config.optptr("printer_model", true);
this->printers.preset(0).config.optptr("printer_variant", true);
this->printers.default_preset().config.optptr("printer_vendor", true);
this->printers.default_preset().config.optptr("printer_model", true);
this->printers.default_preset().config.optptr("printer_variant", true);
// Load the default preset bitmaps.
this->prints .load_bitmap_default("cog.png");
this->filaments.load_bitmap_default("spool.png");
this->printers .load_bitmap_default("printer_empty.png");
this->load_compatible_bitmaps();
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
this->prints .select_preset(0);
this->filaments.select_preset(0);
this->printers .select_preset(0);
this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
}
@ -104,6 +106,9 @@ void PresetBundle::reset(bool delete_files)
this->printers .reset(delete_files);
this->filament_presets.clear();
this->filament_presets.emplace_back(this->filaments.get_selected_preset().name);
this->obsolete_presets.prints.clear();
this->obsolete_presets.filaments.clear();
this->obsolete_presets.printers.clear();
}
void PresetBundle::setup_directories()
@ -210,6 +215,10 @@ std::string PresetBundle::load_system_presets()
errors_cummulative += "\n";
}
}
if (first) {
// No config bundle loaded, reset.
this->reset(false);
}
return errors_cummulative;
}
@ -220,7 +229,10 @@ std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
std::vector<std::string> duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors);
std::vector<std::string> duplicate_filaments = this->filaments.merge_presets(std::move(other.filaments), this->vendors);
std::vector<std::string> duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors);
append(duplicate_prints, std::move(duplicate_filaments));
append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints));
append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments));
append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers));
append(duplicate_prints, std::move(duplicate_filaments));
append(duplicate_prints, std::move(duplicate_printers));
return duplicate_prints;
}
@ -350,7 +362,9 @@ DynamicPrintConfig PresetBundle::full_config() const
DynamicPrintConfig out;
out.apply(FullPrintConfig());
out.apply(this->prints.get_edited_preset().config);
out.apply(this->printers.get_edited_preset().config);
// Add the default filament preset to have the "filament_preset_id" defined.
out.apply(this->filaments.default_preset().config);
out.apply(this->printers.get_edited_preset().config);
out.apply(this->project_config);
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter"));
@ -361,6 +375,7 @@ DynamicPrintConfig PresetBundle::full_config() const
} else {
// Retrieve filament presets and build a single config object for them.
// First collect the filament configurations based on the user selection of this->filament_presets.
// Here this->filaments.find_preset() and this->filaments.first_visible() return the edited copy of the preset if active.
std::vector<const DynamicPrintConfig*> filament_configs;
for (const std::string &filament_preset_name : this->filament_presets)
filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config);
@ -370,7 +385,7 @@ DynamicPrintConfig PresetBundle::full_config() const
std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr);
// loop through options and apply them to the resulting config.
for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
if (key == "compatible_printers")
if (key == "compatible_printers" || key == "compatible_printers_condition")
continue;
// Get a destination option.
ConfigOption *opt_dst = out.option(key, false);
@ -388,7 +403,9 @@ DynamicPrintConfig PresetBundle::full_config() const
}
}
//FIXME These two value types clash between the print and filament profiles. They should be renamed.
out.erase("compatible_printers");
out.erase("compatible_printers_condition");
static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" };
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) {
@ -756,6 +773,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
flatten_configbundle_hierarchy(tree);
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
// Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
std::vector<std::string> loaded_prints;
std::vector<std::string> loaded_filaments;
std::vector<std::string> loaded_printers;
@ -795,6 +813,20 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
active_printer = kvp.second.data();
}
}
} else if (section.first == "obsolete_presets") {
// Parse the names of obsolete presets. These presets will be deleted from user's
// profile directory on installation of this vendor preset.
for (auto &kvp : section.second) {
std::vector<std::string> *dst = nullptr;
if (kvp.first == "print")
dst = &this->obsolete_presets.prints;
else if (kvp.first == "filament")
dst = &this->obsolete_presets.filaments;
else if (kvp.first == "printer")
dst = &this->obsolete_presets.printers;
if (dst)
unescape_strings_cstyle(kvp.second.data(), *dst);
}
} else if (section.first == "settings") {
// Load the settings.
for (auto &kvp : section.second) {
@ -806,10 +838,32 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
continue;
if (presets != nullptr) {
// Load the print, filament or printer preset.
DynamicPrintConfig config(presets->default_preset().config);
const DynamicPrintConfig &default_config = presets->default_preset().config;
DynamicPrintConfig config(default_config);
std::vector<std::string> config_keys = config.keys();
// The following two keys are valid, but they are not mandatory.
config_keys.emplace_back("compatible_printers");
config_keys.emplace_back("compatible_printers_condition");
for (auto &kvp : section.second)
config.set_deserialize(kvp.first, kvp.second.data());
Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys;
size_t n_incorrect_keys = 0;
for (const std::string &key : config_keys)
if (! default_config.has(key)) {
if (incorrect_keys.empty())
incorrect_keys = key;
else {
incorrect_keys += ", ";
incorrect_keys += key;
}
config.erase(key);
++ n_incorrect_keys;
}
if (! incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
if ((flags & LOAD_CFGBNDLE_SYSTEM) && presets == &printers) {
// Filter out printer presets, which are not mentioned in the vendor profile.
// These presets are considered not installed.

View file

@ -54,6 +54,13 @@ public:
// and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors.
std::set<VendorProfile> vendors;
struct ObsoletePresets {
std::vector<std::string> prints;
std::vector<std::string> filaments;
std::vector<std::string> printers;
};
ObsoletePresets obsolete_presets;
bool has_defauls_only() const
{ return prints.size() <= 1 && filaments.size() <= 1 && printers.size() <= 1; }

View file

@ -6,6 +6,7 @@
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/OctoPrint.hpp"
#include "slic3r/Utils/Serial.hpp"
#include "BonjourDialog.hpp"
#include "WipeTowerDialog.hpp"
#include "ButtonsDescription.hpp"
@ -1697,7 +1698,7 @@ void TabPrinter::build()
void TabPrinter::update_serial_ports(){
Field *field = get_field("serial_port");
Choice *choice = static_cast<Choice *>(field);
choice->set_values(scan_serial_ports());
choice->set_values(Utils::scan_serial_ports());
}
void TabPrinter::extruders_count_changed(size_t extruders_count){
@ -1713,7 +1714,8 @@ void TabPrinter::build_extruder_pages(){
size_t n_before_extruders = 2; // Count of pages before Extruder pages
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
if (m_extruders_count_old == m_extruders_count || m_extruders_count <= 2)
if (m_extruders_count_old == m_extruders_count ||
(m_has_single_extruder_MM_page && m_extruders_count == 1))
{
// if we have a single extruder MM setup, add a page with configuration options:
for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already
@ -1721,17 +1723,20 @@ void TabPrinter::build_extruder_pages(){
m_pages.erase(m_pages.begin() + i);
break;
}
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material")) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
optgroup->append_single_option_line("extra_loading_move");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
}
m_has_single_extruder_MM_page = false;
}
if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true);
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
optgroup->append_single_option_line("extra_loading_move");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
m_has_single_extruder_MM_page = true;
}
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){
//# build page
@ -2129,7 +2134,7 @@ void Tab::save_preset(std::string name /*= ""*/)
return;
}
if (existing && (existing->is_external)) {
show_error(this, _(L("Cannot overwrite an external.")));
show_error(this, _(L("Cannot overwrite an external profile.")));
return;
}
}

View file

@ -312,6 +312,7 @@ public:
//Slic3r::GUI::Tab::Printer;
class TabPrinter : public Tab
{
bool m_has_single_extruder_MM_page = false;
public:
wxButton* m_serial_test_btn;
wxButton* m_octoprint_host_test_btn;

View file

@ -82,15 +82,27 @@ void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
{
// forwards the checklistbox event to the owner wxComboCtrl
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr)
if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed )
{
wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId());
event.SetEventObject(cmb);
cmb->ProcessWindowEvent(event);
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr) {
wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId());
event.SetEventObject(cmb);
cmb->ProcessWindowEvent(event);
}
}
evt.Skip();
#ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file)
if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed )
// this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should
// explicitly change the state on the checkbox
m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime;
else
// if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it
m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed;
#endif
}
void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
@ -100,9 +112,14 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
int selId = GetSelection();
if (selId != wxNOT_FOUND)
{
Check((unsigned int)selId, !IsChecked((unsigned int)selId));
SetSelection(wxNOT_FOUND);
#ifndef _WIN32
if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed)
#endif
Check((unsigned int)selId, !IsChecked((unsigned int)selId));
m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time
SetSelection(wxNOT_FOUND);
wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId());
event.SetInt(selId);
event.SetEventObject(this);

View file

@ -13,6 +13,21 @@ class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
wxString m_text;
// Events sent on mouseclick are quite complex. Function OnListBoxSelection is supposed to pass the event to the checkbox, which works fine on
// Win. On OSX and Linux the events are generated differently - clicking on the checkbox square generates the event twice (and the square
// therefore seems not to respond).
// This enum is meant to save current state of affairs, i.e., if the event forwarding is ok to do or not. It is only used on Linux
// and OSX by some #ifdefs. It also stores information whether OnListBoxSelection is supposed to change the checkbox status,
// or if it changed status on its own already (which happens when the square is clicked). More comments in OnCheckListBox(...)
// There indeed is a better solution, maybe making a custom event used for the event passing to distinguish the original and passed message
// and blocking one of them on OSX and Linux. Feel free to refactor, but carefully test on all platforms.
enum class OnCheckListBoxFunction{
FreeToProceed,
RefuseToProceed,
WasRefusedLastTime
} m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed;
public:
virtual bool Create(wxWindow* parent);
virtual wxWindow* GetControl();

View file

@ -136,7 +136,7 @@ void PresetUpdater::priv::set_download_prefs(AppConfig *app_config)
{
enabled_version_check = app_config->get("version_check") == "1";
version_check_url = app_config->version_check_url();
enabled_config_update = app_config->get("preset_update") == "1";
enabled_config_update = app_config->get("preset_update") == "1" && !app_config->legacy_datadir();
}
// Downloads a file (http get operation). Cancels if the Updater is being destroyed.
@ -402,13 +402,33 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
PresetBundle bundle;
bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% conflicting presets")
% (bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
auto preset_remover = [](const Preset &preset) {
BOOST_LOG_TRIVIAL(info) << '\t' << preset.file;
fs::remove(preset.file);
};
for (const auto &preset : bundle.prints) { preset_remover(preset); }
for (const auto &preset : bundle.filaments) { preset_remover(preset); }
for (const auto &preset : bundle.printers) { preset_remover(preset); }
// Also apply the `obsolete_presets` property, removing obsolete ini files
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% obsolete presets")
% (bundle.obsolete_presets.prints.size() + bundle.obsolete_presets.filaments.size() + bundle.obsolete_presets.printers.size());
auto obsolete_remover = [](const char *subdir, const std::string &preset) {
auto path = fs::path(Slic3r::data_dir()) / subdir / preset;
path += ".ini";
BOOST_LOG_TRIVIAL(info) << '\t' << path.string();
fs::remove(path);
};
for (const auto &name : bundle.obsolete_presets.prints) { obsolete_remover("print", name); }
for (const auto &name : bundle.obsolete_presets.filaments) { obsolete_remover("filament", name); }
for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); }
}
}
}

View file

@ -0,0 +1,193 @@
#include "Serial.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <fstream>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#if _WIN32
#include <Windows.h>
#include <Setupapi.h>
#include <initguid.h>
#include <devguid.h>
// Undefine min/max macros incompatible with the standard library
// For example, std::numeric_limits<std::streamsize>::max()
// produces some weird errors
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "boost/nowide/convert.hpp"
#pragma comment(lib, "user32.lib")
#elif __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/ioss.h>
#include <sys/syslimits.h>
#endif
namespace Slic3r {
namespace Utils {
static bool looks_like_printer(const std::string &friendly_name)
{
return friendly_name.find("Original Prusa") != std::string::npos;
}
#ifdef __linux__
static std::string get_tty_friendly_name(const std::string &path, const std::string &name)
{
const auto sysfs_product = (boost::format("/sys/class/tty/%1%/device/../product") % name).str();
std::ifstream file(sysfs_product);
std::string product;
std::getline(file, product);
return file.good() ? (boost::format("%1% (%2%)") % product % path).str() : path;
}
#endif
std::vector<SerialPortInfo> scan_serial_ports_extended()
{
std::vector<SerialPortInfo> output;
#ifdef _WIN32
SP_DEVINFO_DATA devInfoData = { 0 };
devInfoData.cbSize = sizeof(devInfoData);
// Get the tree containing the info for the ports.
HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, nullptr, DIGCF_PRESENT);
if (hDeviceInfo != INVALID_HANDLE_VALUE) {
// Iterate over all the devices in the tree.
for (int nDevice = 0; SetupDiEnumDeviceInfo(hDeviceInfo, nDevice, &devInfoData); ++ nDevice) {
SerialPortInfo port_info;
// Get the registry key which stores the ports settings.
HKEY hDeviceKey = SetupDiOpenDevRegKey(hDeviceInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (hDeviceKey) {
// Read in the name of the port.
wchar_t pszPortName[4096];
DWORD dwSize = sizeof(pszPortName);
DWORD dwType = 0;
if (RegQueryValueEx(hDeviceKey, L"PortName", NULL, &dwType, (LPBYTE)pszPortName, &dwSize) == ERROR_SUCCESS)
port_info.port = boost::nowide::narrow(pszPortName);
RegCloseKey(hDeviceKey);
if (port_info.port.empty())
continue;
}
// Find the size required to hold the device info.
DWORD regDataType;
DWORD reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, nullptr, nullptr, 0, &reqSize);
std::vector<wchar_t> hardware_id(reqSize > 1 ? reqSize : 1);
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
continue;
port_info.hardware_id = boost::nowide::narrow(hardware_id.data());
// Find the size required to hold the friendly name.
reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
std::vector<wchar_t> friendly_name;
friendly_name.reserve(reqSize > 1 ? reqSize : 1);
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, (BYTE*)friendly_name.data(), reqSize, nullptr)) {
port_info.friendly_name = port_info.port;
} else {
port_info.friendly_name = boost::nowide::narrow(friendly_name.data());
port_info.is_printer = looks_like_printer(port_info.friendly_name);
}
output.emplace_back(std::move(port_info));
}
}
#elif __APPLE__
// inspired by https://sigrok.org/wiki/Libserialport
CFMutableDictionaryRef classes = IOServiceMatching(kIOSerialBSDServiceValue);
if (classes != 0) {
io_iterator_t iter;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes, &iter) == KERN_SUCCESS) {
io_object_t port;
while ((port = IOIteratorNext(iter)) != 0) {
CFTypeRef cf_property = IORegistryEntryCreateCFProperty(port, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_property) {
char path[PATH_MAX];
Boolean result = CFStringGetCString((CFStringRef)cf_property, path, sizeof(path), kCFStringEncodingUTF8);
CFRelease(cf_property);
if (result) {
SerialPortInfo port_info;
port_info.port = path;
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntryCreateCFProperty(port,
CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0))) {
// Description limited to 127 char, anything longer would not be user friendly anyway.
char description[128];
if (CFStringGetCString((CFStringRef)cf_property, description, sizeof(description), kCFStringEncodingUTF8)) {
port_info.friendly_name = std::string(description) + " (" + port_info.port + ")";
port_info.is_printer = looks_like_printer(port_info.friendly_name);
}
CFRelease(cf_property);
}
if (port_info.friendly_name.empty())
port_info.friendly_name = port_info.port;
output.emplace_back(std::move(port_info));
}
}
IOObjectRelease(port);
}
}
}
#else
// UNIX / Linux
std::initializer_list<const char*> prefixes { "ttyUSB" , "ttyACM", "tty.", "cu.", "rfcomm" };
for (auto &dir_entry : boost::filesystem::directory_iterator(boost::filesystem::path("/dev"))) {
std::string name = dir_entry.path().filename().string();
for (const char *prefix : prefixes) {
if (boost::starts_with(name, prefix)) {
const auto path = dir_entry.path().string();
SerialPortInfo spi;
spi.port = path;
spi.hardware_id = path;
#ifdef __linux__
spi.friendly_name = get_tty_friendly_name(path, name);
#else
spi.friendly_name = path;
#endif
output.emplace_back(std::move(spi));
break;
}
}
}
#endif
output.erase(std::remove_if(output.begin(), output.end(),
[](const SerialPortInfo &info) {
return boost::starts_with(info.port, "Bluetooth") || boost::starts_with(info.port, "FireFly");
}),
output.end());
return output;
}
std::vector<std::string> scan_serial_ports()
{
std::vector<SerialPortInfo> ports = scan_serial_ports_extended();
std::vector<std::string> output;
output.reserve(ports.size());
for (const SerialPortInfo &spi : ports)
output.emplace_back(std::move(spi.port));
return output;
}
} // namespace Utils
} // namespace Slic3r

View file

@ -0,0 +1,32 @@
#ifndef slic3r_GUI_Utils_Serial_hpp_
#define slic3r_GUI_Utils_Serial_hpp_
#include <memory>
#include <vector>
#include <string>
#include <vector>
namespace Slic3r {
namespace Utils {
struct SerialPortInfo {
std::string port;
std::string hardware_id;
std::string friendly_name;
bool is_printer = false;
};
inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
{
return sp1.port == sp2.port &&
sp1.hardware_id == sp2.hardware_id &&
sp1.is_printer == sp2.is_printer;
}
extern std::vector<std::string> scan_serial_ports();
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
} // Utils
} // Slic3r
#endif /* slic3r_GUI_Utils_Serial_hpp_ */