From 918cb48d452d5a9eb555a8cdc668ec980104f87e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 14:50:56 +0100 Subject: [PATCH 1/8] Fixed OSX build & language selection (SPE-696) --- src/slic3r/GUI/GUI_App.cpp | 7 ++++--- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 88c77c1937..2998ea7f3f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -434,7 +434,7 @@ bool GUI_App::select_language( wxArrayString & names, m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[index]); m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); Preset::update_suffix_modified(); return true; @@ -461,7 +461,7 @@ bool GUI_App::load_language() m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[i]); m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); Preset::update_suffix_modified(); return true; @@ -504,7 +504,8 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident { auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + filename + wxFileName::GetPathSeparator() + - GetAppName() + wxT(".mo"); + /*GetAppName()*/"Slic3rPE" + + wxT(".mo"); if (wxFileExists(full_file_name)) { names.Add(langinfo->Description); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 87010f0817..b3edbc9a8b 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -23,7 +23,7 @@ KBShortcutsDialog::KBShortcutsDialog() wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); font.SetPointSize(10); - const wxFont bold_font = font.Bold(); + wxFont bold_font = font.Bold(); #ifdef __WXOSX__ font.SetPointSize(12); bold_font.SetPointSize(14); From 2548253d59054330c11479d56421e55e90cf6430 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 15:03:49 +0100 Subject: [PATCH 2/8] Another fix on volumes translation --- src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++++++-------- src/slic3r/GUI/GLCanvas3D.hpp | 5 +++-- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7facc36232..3c4debba04 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1549,7 +1549,7 @@ void GLCanvas3D::Selection::start_dragging() _set_caches(); } -void GLCanvas3D::Selection::translate(const Vec3d& displacement) +void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) { if (!m_valid) return; @@ -1559,7 +1559,7 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement) #if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { - if (_requires_local_axes()) + if (local) (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); else { @@ -1614,7 +1614,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { - if (_requires_local_axes()) + if (requires_local_axes()) (*m_volumes)[i]->set_volume_rotation(rotation); else { @@ -2118,6 +2118,11 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel } #endif // ENABLE_SIDEBAR_VISUAL_HINTS +bool GLCanvas3D::Selection::requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -2746,11 +2751,6 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING -bool GLCanvas3D::Selection::_requires_local_axes() const -{ - return (m_mode == Volume) && is_from_single_instance(); -} - const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 54e546c78c..413d625f1d 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -578,7 +578,7 @@ public: void start_dragging(); - void translate(const Vec3d& displacement); + void translate(const Vec3d& displacement, bool local = false); void rotate(const Vec3d& rotation, bool local); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, bool local); @@ -597,6 +597,8 @@ public: void render_sidebar_hints(const std::string& sidebar_field) const; #endif // ENABLE_SIDEBAR_VISUAL_HINTS + bool requires_local_axes() const; + private: void _update_valid(); void _update_type(); @@ -626,7 +628,6 @@ public: #if ENABLE_ENSURE_ON_BED_WHILE_SCALING void _ensure_on_bed(); #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING - bool _requires_local_axes() const; }; class ClippingPlane diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ddf699c2cd..8cc2362e82 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -361,8 +361,9 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_position_value(const Vec3d& position) { auto canvas = wxGetApp().plater()->canvas3D(); - canvas->get_selection().start_dragging(); - canvas->get_selection().translate(position - cache_position); + GLCanvas3D::Selection& selection = canvas->get_selection(); + selection.start_dragging(); + selection.translate(position - cache_position, selection.requires_local_axes()); canvas->do_move(); cache_position = position; From 0ac4d13015dc93918be8ef563c435a8aaec8ca3b Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 15:00:30 +0100 Subject: [PATCH 3/8] Print host: Check OctoPrint vs SLA --- src/slic3r/GUI/Tab.cpp | 3 +- src/slic3r/Utils/Duet.hpp | 12 ++++---- src/slic3r/Utils/OctoPrint.cpp | 53 +++++++++++++++++++++++++++------- src/slic3r/Utils/OctoPrint.hpp | 25 +++++++++------- src/slic3r/Utils/PrintHost.cpp | 2 +- 5 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0b59a21aba..5a212d4ee2 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1551,7 +1551,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) auto printhost_browse = [this, optgroup] (wxWindow* parent) { - // TODO: SLA + // TODO: SLA Bonjour auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); @@ -1562,6 +1562,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) BonjourDialog dialog(parent); if (dialog.show_and_lookup()) { optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + // FIXME: emit killfocus on the edit widget } }); diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index 0608f85a58..d0f5b30095 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -19,12 +19,12 @@ public: Duet(DynamicPrintConfig *config); virtual ~Duet(); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; virtual std::string get_host() const { return host; } private: std::string host; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index cbb81c54f8..cf5fc2f541 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -1,8 +1,12 @@ #include "OctoPrint.hpp" #include +#include #include #include +#include +#include +#include #include @@ -12,6 +16,7 @@ namespace fs = boost::filesystem; +namespace pt = boost::property_tree; namespace Slic3r { @@ -41,11 +46,29 @@ bool OctoPrint::test(wxString &msg) const res = false; msg = format_error(body, error, status); }) - .on_complete([&](std::string body, unsigned) { + .on_complete([&, this](std::string body, unsigned) { BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - // TODO: parse body, call validate_version_text + try { + std::stringstream ss(body); + pt::ptree ptree; + pt::read_json(ss, ptree); + if (! ptree.get_optional("api")) { + res = false; + return; + } + + const auto text = ptree.get_optional("text"); + res = validate_version_text(text); + if (! res) { + msg = wxString::Format("Mismatched type of print host: %s", text ? *text : "OctoPrint"); + } + } + catch (...) { + res = false; + msg = "Could not parse server response"; + } }) .perform_sync(); @@ -71,7 +94,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn wxString test_msg; if (! test(test_msg)) { - // TODO: + // FIXME: // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); // GUI::show_error(&progress_dialog, std::move(errormsg)); @@ -125,10 +148,9 @@ bool OctoPrint::can_test() const return true; } -bool OctoPrint::validate_version_text(const std::string &version_text) +bool OctoPrint::validate_version_text(const boost::optional &version_text) const { - // FIXME - return true; + return version_text ? boost::starts_with(*version_text, "OctoPrint") : true; } void OctoPrint::set_auth(Http &http) const @@ -164,14 +186,23 @@ wxString OctoPrint::format_error(const std::string &body, const std::string &err } -// SL1 +// SLAHost -SL1Host::~SL1Host() {} +SLAHost::~SLAHost() {} -bool SL1Host::validate_version_text(const std::string &version_text) +wxString SLAHost::get_test_ok_msg () const { - // FIXME - return true; + return wxString::Format("%s", _(L("Connection to Prusa SLA works correctly."))); +} + +wxString SLAHost::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg); +} + +bool SLAHost::validate_version_text(const boost::optional &version_text) const +{ + return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 4d6555e13f..57aae672a9 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "PrintHost.hpp" @@ -19,16 +20,16 @@ public: OctoPrint(DynamicPrintConfig *config); virtual ~OctoPrint(); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; virtual std::string get_host() const { return host; } protected: - virtual bool validate_version_text(const std::string &version_text); + virtual bool validate_version_text(const boost::optional &version_text) const; private: std::string host; @@ -41,14 +42,16 @@ private: }; -class SL1Host: public OctoPrint +class SLAHost: public OctoPrint { public: - SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} - virtual ~SL1Host(); + SLAHost(DynamicPrintConfig *config) : OctoPrint(config) {} + virtual ~SLAHost(); + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; protected: - virtual bool validate_version_text(const std::string &version_text); + virtual bool validate_version_text(const boost::optional &version_text) const; }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index cdd0c107ef..5c45078164 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -30,8 +30,8 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) switch (opt->value) { case htOctoPrint: return new OctoPrint(config); - case htSL1: return new SL1Host(config); case htDuet: return new Duet(config); + case htSL1: return new SLAHost(config); default: return nullptr; } } From 17c2f3d81306d0d1f280821ae49ee128e9edf683 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 15:40:07 +0100 Subject: [PATCH 4/8] Http: Set a sane default connection timeout --- src/slic3r/Utils/Http.cpp | 9 +++++++++ src/slic3r/Utils/Http.hpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 6e6c9ed44b..27f7131277 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -32,6 +32,7 @@ class CurlGlobalInit struct Http::priv { enum { + DEFAULT_TIMEOUT = 10, DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024, }; @@ -84,6 +85,7 @@ Http::priv::priv(const std::string &url) throw std::runtime_error(std::string("Could not construct Curl object")); } + ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_TIMEOUT); ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); @@ -293,6 +295,13 @@ Http::~Http() } +Http& Http::timeout(long timeout) +{ + if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; } + if (p) { ::curl_easy_setopt(p->curl, CURLOPT_CONNECTTIMEOUT, timeout); } + return *this; +} + Http& Http::size_limit(size_t sizeLimit) { if (p) { p->limit = sizeLimit; } diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index fd3f8830dd..9406a82f2e 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -55,6 +55,8 @@ public: Http& operator=(const Http &) = delete; Http& operator=(Http &&) = delete; + // Sets a maximum connection timeout in seconds + Http& timeout(long timeout); // Sets a maximum size of the data that can be received. // A value of zero sets the default limit, which is is 5MB. Http& size_limit(size_t sizeLimit); From 4c55f1ce9e57537abf9502ef9f4af9b7bf8da579 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 15:58:01 +0100 Subject: [PATCH 5/8] DoubleSlider issues (1 & 3 from SPE-686) + added icon for "Keyboard shortcuts" dialog --- resources/icons/Slic3r_32px.png | Bin 0 -> 1771 bytes src/slic3r/GUI/GUI_Preview.cpp | 3 ++- src/slic3r/GUI/wxExtensions.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 resources/icons/Slic3r_32px.png diff --git a/resources/icons/Slic3r_32px.png b/resources/icons/Slic3r_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf5a9cd144b3059b70071bc978ca369b7b54778 GIT binary patch literal 1771 zcmV;P)~26#zC zK~z}7?U!9_99I>`f9Kwro!zy)j@ORkq)wbP2~r3&n5L+OBtj*53QdAai^@k4>O&w3Mrvomw={!Q8F$XGZ5|iW&1q;7`DWjJaCJAa_6?hRCr5Bx~*IwQ(u~Y)$p>p|j@-eZQM39O>=t!t=Zp zo-3u6L6#kH4uB*{$Y;IeE1&H9%M;%cIdLHV+n4rS{ubwxe*!TF(49s^0|USZ<|5tu!iBC8ixe`M%nAst zHJ<0Kf@y_#WdG7XzOeVLZ}9Go3o_;sH!fZ~U9JTZq#(QhFd1a8mHmCqj1Uo$Bq0a_ zeBWOM(*)7jzPgI!^@F~qKJGxkLP*# zzK`d5c%HW`R9~z;bRf3TI2gZxt!-7ecspHF;-lDH4u!0<-joO3HGsMfg0 z?!P*Dq<21+w}-MArAX?m1aSqDV067`v;#?+DK?Zd^UcX9il|g7IOo#J>Gj?yNWC1S+S?$AFo$sIK!05R_Wg^O%jGiFYLzeydFJ7F7LNDM8}+82r5bCJ zaCH=*F;_1`Q~?uJQxMxOGHZ*~Fb7=lwf*&HKN!AxbXWP-?CdOOJ~37P+=#E#RI!Ix z>9+1!3t<_OumNMu^(I8el~u-^bEl<>$1OC~Y>vs7pB4|)DUzXW*nuvbYM%i~y`dEo zLAcoXRs4OuXteJ~4q$*PbwXkmfC@5l7+bivErf|74jUlOKz$M7DnKO#X&a688Y2NL z0gjPR*O-`mjGBz%oVV%~m^vg8*yMeyFF{gkq^|G_G!u8rn!9)LKfLc*BK0h>&Eagr zTWiuyO+%E;`MB1w_cLe##W6N>PM&eLW02+tsTo4|UHK;U%vmJpLV2LmzDA_sZ4=1E zVB^OBmC^qi@M6a&JvT10y!m)~q`b=W)c7ivEdSkj9ylXouG-1X%8iR|P2&_uk=fXh zv{kmicYxh+u^S1WuUUT6>0;%G*M5Ipp8ELD)vvtot1hi| zQ7P4}l=6MwSDxp!bxhp26o9Hc>5>$rM=3R$B*}K5d(S3{4{vwFr}l8)!6EkbXX$TU zV4=>!>(g9$_$mMZ N002ovPDHLkV1i@hUjYCB literal 0 HcmV?d00001 diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 735b55125c..2885a3d163 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -587,7 +587,8 @@ void Preview::create_double_slider() auto& config = wxGetApp().preset_bundle->project_config; ((config.option("colorprint_heights"))->values) = (m_slider->GetTicksValues()); m_schedule_background_process(); - int type = m_choice_view_type->FindString(_(L("Color Print"))); + bool color_print = !config.option("colorprint_heights")->values.empty(); + int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) ); if (m_choice_view_type->GetSelection() != type) { m_choice_view_type->SetSelection(type); if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 2daba5df41..b8b76f049d 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -2043,7 +2043,7 @@ void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter) // - value decrease (if wxSL_HORIZONTAL) void PrusaDoubleSlider::move_current_thumb(const bool condition) { - m_is_one_layer = wxGetKeyState(WXK_CONTROL); +// m_is_one_layer = wxGetKeyState(WXK_CONTROL); int delta = condition ? -1 : 1; if (is_horizontal()) delta *= -1; From c40b8aba2410e09984d9025c383e10287955e7a6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 17:38:41 +0100 Subject: [PATCH 6/8] Fixed recreate_GUI() after language change. --- src/slic3r/GUI/GUI_App.cpp | 20 +++++++++++++++++--- src/slic3r/GUI/GUI_App.hpp | 1 - src/slic3r/GUI/MainFrame.cpp | 12 ++++-------- src/slic3r/GUI/MainFrame.hpp | 8 ++------ 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 2998ea7f3f..bbba1d93ba 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -137,7 +137,7 @@ bool GUI_App::OnInit() std::cerr << "Creating main frame..." << std::endl; if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); - mainframe = new MainFrame(no_plater, false); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list update_mode(); SetTopWindow(mainframe); @@ -277,8 +277,8 @@ void GUI_App::recreate_GUI() { std::cerr << "recreate_GUI" << std::endl; - auto topwindow = GetTopWindow(); - mainframe = new MainFrame(no_plater,false); + MainFrame* topwindow = dynamic_cast(GetTopWindow()); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list update_mode(); @@ -287,6 +287,20 @@ void GUI_App::recreate_GUI() topwindow->Destroy(); } + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + + CallAfter([this]() { + // temporary workaround for the correct behavior of the Scrolled sidebar panel + auto& panel = sidebar(); + if (panel.obj_list()->GetMinHeight() > 200) { + wxWindowUpdateLocker noUpdates_sidebar(&panel); + panel.obj_list()->SetMinSize(wxSize(-1, 200)); + panel.Layout(); + } + }); + + mainframe->Show(true); + // On OSX the UI was not initialized correctly if the wizard was called // before the UI was up and running. CallAfter([]() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 81175b7cac..bd64a3ac57 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -71,7 +71,6 @@ static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { - bool no_plater{ false }; bool app_conf_exists{ false }; // Lock to guard the callback stack diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2211023f06..871d50a3d0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -28,10 +28,8 @@ namespace Slic3r { namespace GUI { -MainFrame::MainFrame(const bool no_plater, const bool loaded) : +MainFrame::MainFrame() : wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), - m_no_plater(no_plater), - m_loaded(loaded), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. @@ -125,11 +123,9 @@ void MainFrame::init_tabpanel() } }); - if (!m_no_plater) { - m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); - wxGetApp().plater_ = m_plater; - m_tabpanel->AddPage(m_plater, _(L("Plater"))); - } + m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); + wxGetApp().plater_ = m_plater; + m_tabpanel->AddPage(m_plater, _(L("Plater"))); // The following event is emited by Tab implementation on config value change. Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index fab6aea908..e0411b6dad 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -42,10 +42,7 @@ struct PresetTab { class MainFrame : public wxFrame { - bool m_no_plater; - bool m_loaded; - int m_lang_ch_event; - int m_preferences_event; + bool m_loaded {false}; wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; @@ -71,8 +68,7 @@ class MainFrame : public wxFrame bool can_delete_all() const; public: - MainFrame() {} - MainFrame(const bool no_plater, const bool loaded); + MainFrame(); ~MainFrame() {} Plater* plater() { return m_plater; } From 3b2c28fa89994230b86a0eb6e681d3ab670e3e6e Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 18:43:03 +0100 Subject: [PATCH 7/8] Printhost: Polish error handling, bugfixes --- src/slic3r/GUI/PrintHostDialogs.cpp | 10 ++++++++-- src/slic3r/Utils/Duet.cpp | 4 ++-- src/slic3r/Utils/Duet.hpp | 2 +- src/slic3r/Utils/Http.cpp | 15 +++++++++++---- src/slic3r/Utils/OctoPrint.cpp | 21 +++++++++------------ src/slic3r/Utils/OctoPrint.hpp | 2 +- src/slic3r/Utils/PrintHost.cpp | 26 +++++++++++++++----------- src/slic3r/Utils/PrintHost.hpp | 6 +++++- 8 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 8ac8615a84..586fe3d835 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -102,7 +102,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); - auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); // TODO: enable based on status ("show error" for failed jobs) auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); btnsizer->AddStretchSpacer(); @@ -140,7 +140,13 @@ void PrintHostQueueDialog::on_error(Event &evt) { wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); - // TODO + job_list->SetValue(wxVariant(0), evt.job_id, 1); + job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, 2); + + // TODO: keep the error for repeated display + + auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); + GUI::show_error(nullptr, std::move(errormsg)); } diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 1772ae8ef4..fd77fc1300 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -46,7 +46,7 @@ bool Duet::test(wxString &msg) const wxString Duet::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to Duet works correctly."))); + return _(L("Connection to Duet works correctly.")); } wxString Duet::get_test_failed_msg (wxString &msg) const @@ -135,7 +135,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const // return res; // } -bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const +bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { // XXX: TODO throw "unimplemented"; diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index d0f5b30095..e053f91eff 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -22,7 +22,7 @@ public: virtual bool test(wxString &curl_msg) const; virtual wxString get_test_ok_msg () const; virtual wxString get_test_failed_msg (wxString &msg) const; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; virtual bool has_auto_discovery() const; virtual bool can_test() const; virtual std::string get_host() const { return host; } diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 27f7131277..30478cb01b 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -64,6 +64,7 @@ struct Http::priv static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow); static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp); + void set_timeout(long timeout); void form_add_file(const char *name, const fs::path &path, const char* filename); void set_post_body(const fs::path &path); @@ -85,7 +86,7 @@ Http::priv::priv(const std::string &url) throw std::runtime_error(std::string("Could not construct Curl object")); } - ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_TIMEOUT); + set_timeout(DEFAULT_TIMEOUT); ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); @@ -169,6 +170,12 @@ size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, v return stream->gcount(); } +void Http::priv::set_timeout(long timeout) +{ + ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout); + ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); +} + void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename) { // We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows @@ -205,10 +212,10 @@ void Http::priv::set_post_body(const fs::path &path) std::string Http::priv::curl_error(CURLcode curlcode) { - return (boost::format("%1% (%2%): %3%") + return (boost::format("%1%:\n%2%\n[Error %3%]") % ::curl_easy_strerror(curlcode) + % error_buffer.c_str() % curlcode - % error_buffer ).str(); } @@ -298,7 +305,7 @@ Http::~Http() Http& Http::timeout(long timeout) { if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; } - if (p) { ::curl_easy_setopt(p->curl, CURLOPT_CONNECTTIMEOUT, timeout); } + if (p) { p->set_timeout(timeout); } return *this; } diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index cf5fc2f541..af9d6e4f0e 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -62,7 +62,7 @@ bool OctoPrint::test(wxString &msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (! res) { - msg = wxString::Format("Mismatched type of print host: %s", text ? *text : "OctoPrint"); + msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint"); } } catch (...) { @@ -77,28 +77,24 @@ bool OctoPrint::test(wxString &msg) const wxString OctoPrint::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); + return _(L("Connection to OctoPrint works correctly.")); } wxString OctoPrint::get_test_failed_msg (wxString &msg) const { return wxString::Format("%s: %s\n\n%s", - _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); + _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } -bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const +bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { const auto upload_filename = upload_data.upload_path.filename(); const auto upload_parent_path = upload_data.upload_path.parent_path(); wxString test_msg; if (! test(test_msg)) { - - // FIXME: - - // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - // GUI::show_error(&progress_dialog, std::move(errormsg)); - // return false; + error_fn(std::move(test_msg)); + return false; } bool res = true; @@ -122,7 +118,8 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn }) .on_error([&](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - error_fn(std::move(body), std::move(error), status); + // error_fn(std::move(body), std::move(error), status); + error_fn(format_error(body, error, status)); res = false; }) .on_progress([&](Http::Progress progress, bool &cancel) { @@ -192,7 +189,7 @@ SLAHost::~SLAHost() {} wxString SLAHost::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to Prusa SLA works correctly."))); + return _(L("Connection to Prusa SLA works correctly.")); } wxString SLAHost::get_test_failed_msg (wxString &msg) const diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 57aae672a9..1e739c99d7 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -23,7 +23,7 @@ public: virtual bool test(wxString &curl_msg) const; virtual wxString get_test_ok_msg () const; virtual wxString get_test_failed_msg (wxString &msg) const; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; virtual bool has_auto_discovery() const; virtual bool can_test() const; virtual std::string get_host() const { return host; } diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 5c45078164..934436a190 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #include #include +#include #include #include "libslic3r/PrintConfig.hpp" @@ -58,7 +60,6 @@ struct PrintHostJobQueue::priv void start_bg_thread(); void bg_thread_main(); void progress_fn(Http::Progress progress, bool &cancel); - void error_fn(std::string body, std::string error, unsigned http_status); void perform_job(PrintHostJob the_job); }; @@ -100,6 +101,9 @@ void PrintHostJobQueue::priv::bg_thread_main() } job_id++; } + } catch (const std::exception &e) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, e.what()); + wxQueueEvent(queue_dialog, evt); } catch (...) { wxTheApp->OnUnhandledException(); } @@ -136,28 +140,28 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) } } -void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status) -{ - // TODO -} - void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } - BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`") + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%2%`") % the_job.upload_data.upload_path % the_job.printhost->get_host(); const fs::path gcode_path = the_job.upload_data.source_path; - the_job.printhost->upload(std::move(the_job.upload_data), + bool success = the_job.printhost->upload(std::move(the_job.upload_data), [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, - [this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); } + [this](wxString error) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error)); + wxQueueEvent(queue_dialog, evt); + } ); - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); - wxQueueEvent(queue_dialog, evt); + if (success) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); + wxQueueEvent(queue_dialog, evt); + } boost::system::error_code ec; fs::remove(gcode_path, ec); diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 52ef380589..a6c7a47238 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -28,10 +29,13 @@ class PrintHost public: virtual ~PrintHost(); + typedef Http::ProgressFn ProgressFn; + typedef std::function ErrorFn; + virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; virtual std::string get_host() const = 0; From 64f46999d210f2e63a061c70f9d9d9c831e9727f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 08:38:46 +0100 Subject: [PATCH 8/8] New icons for sla support points gizmo overlay --- .../overlay/sla_support_points_hover.png | Bin 3282 -> 2057 bytes .../icons/overlay/sla_support_points_off.png | Bin 1512 -> 1947 bytes .../icons/overlay/sla_support_points_on.png | Bin 1435 -> 2869 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png index 6303232b24de8cbdc9f930fa33582591d9a8e454..55955f3d6f1d8e1821a1b5f1212f78efb907b1d0 100644 GIT binary patch literal 2057 zcmaJ?d0Z2B79S3|BTx@6!Ex9w!ZMl6LUB;P6S;4@YDHxW`6VDyzhJ8 zcYUTok-m=R6Y2v108N%E&ZN#{=k4V|J^!RH9;c2FQj$YvnY3gXZXp1n#*|NhG6P;f zWD>ZhqWB__003?_y)uW)k#FFuO$Ivdw4s+9%oG{`5|YZzxVn%a!F-}XZxle&XZj#e zuMt4mY&k=277;pqYNdt9s!Ug^D+|>;4V07!CY19j2L^(~!E!^9(aJ9uK+pa1sduLu zfxzb`WT60>CzT^tfFhHH0NHd7tVUUEFpft@IZPIh7Ykwxl#4L92+D*}4xh!~V;Hz_ zK~yx0M$69>OBZ5Mk^s_?q?wN(Wo2dbGA7+*DL_yjkB2ZY1jAs;0=8BdNxU33S|b-d zhzYCOqBoOzlM!@!#PdxhqyVCr&Zl57%jGW|8?6h4qDqF8<7Nb=t~KW)M7G#&nwN>zk-EYo6pnmm^h3xxG2mbI6N3< zYFRLg$6&CyY8D=cVbB6z^HK%pCzaArnV1&|@$>U>7Kg#+!pt}}6UNyr45kzqjA}3~ zhoeSuHHUx@iX!41o|lGcQAJIt^Yz7QP{NDlB8=2VS*X?dtHr7S0QV4?Sg0%?nQU2~ zs@xcS{;sxR1d@BNX?&wQB)VvS)28m!%&Ju4@Qf;_R<+SLH+bg83>Y)mZr`1^cF9UX zQ^PrlP#KUGlCtqTXh2!{f&BOBKb`1pxO98Gr%e0#iDv7~Z$hRIRz>tpzZFrYZK`v$ zNA3H2Y(`y_OEM4uxYvA<-c8$j@%vKW_Uw;c|9MWw28LT)TkAh;FTS>X%ld{wVAbQ= zn|;G>x4SBzj?{!0{}Nu4He3kGe(e3?`iA2AKLd)4!`K8q5HqH@rvg4pqMi1VACm~a z+GdH{U=7zFcde;Dn zV5DdG)}0vJBV+aZY|;Kf_K|Q8_n1?A4%An77QF7?yL06GPk;3paxKG!RUO_OblPP~ z6IZk;y3%{Zf6P{@&dc)C26bn6_D9;1|D8B+LNfj}cy`qD*-9aKb6NN9v1HD)`;Jc= z!v^sKmr4{t-7DN?;WMpyxx(h(eLHfYsPf6GkZl=HTcIRXOUt{QW1WU|ldi0!DT#X1|nW$bqRY3Ddu1wr4I(Bl7#Q!gCFI=tBo_?{(#Y@q3$7f6P8k z3k`T6WRq__yz4HEJ=(L!fqZ!%9X|UNEy}UvVB2}SF0Ik`mTnm`w#bxZ?2Dc z^sg$dPt6}5#RIB$OKj8K=&JBcyBZt#;QF$;_=DB&FFiEd z_KkXQ)seu$s@%CYVmv3vv9$B@tp~$_m!RM$vT76pya3l400e$vo{h8v+Y}kfUIVIg P{+wl!bn)?&yt@AZI_VI& delta 3278 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 M07*qoM6N<$f_b@Sr2qf` diff --git a/resources/icons/overlay/sla_support_points_off.png b/resources/icons/overlay/sla_support_points_off.png index b654366d578df6a7407d9124264fc818deef9423..7bf84a3e647ea4232ed940b3e9609d4707310a52 100644 GIT binary patch literal 1947 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 diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png index 8c1e69565076723113a310ffad661ba0a4376977..879b9b24128b825efd77c29f9d84aa7b0449f1c1 100644 GIT binary patch literal 2869 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