From 2c49dcb9bed0fc49b3c24f33d4cecf01fa7e8b9a Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 12 Mar 2019 17:41:43 +0100 Subject: [PATCH 1/8] Bonjour: Add txt key-val extraction, filtering based on printer tech --- src/slic3r/GUI/BonjourDialog.cpp | 50 ++++++--- src/slic3r/GUI/BonjourDialog.hpp | 5 +- src/slic3r/GUI/Tab.cpp | 15 +-- src/slic3r/Utils/Bonjour.cpp | 176 +++++++++++++++++++------------ src/slic3r/Utils/Bonjour.hpp | 24 ++++- 5 files changed, 174 insertions(+), 96 deletions(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 68e353ebe..41e9d5392 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" @@ -49,14 +50,16 @@ struct LifetimeGuard LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {} }; +// FIXME: use em, resizable -BonjourDialog::BonjourDialog(wxWindow *parent) : - wxDialog(parent, wxID_ANY, _(L("Network lookup"))), - list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))), - replies(new ReplySet), - label(new wxStaticText(this, wxID_ANY, "")), - timer(new wxTimer()), - timer_state(0) +BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech) + : wxDialog(parent, wxID_ANY, _(L("Network lookup"))) + , list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))) + , replies(new ReplySet) + , label(new wxStaticText(this, wxID_ANY, "")) + , timer(new wxTimer()) + , timer_state(0) + , tech(tech) { wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); @@ -67,7 +70,9 @@ BonjourDialog::BonjourDialog(wxWindow *parent) : list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50); list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100); list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200); - list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50); + if (tech == ptFFF) { + list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50); + } vsizer->Add(list, 1, wxEXPAND | wxALL, 10); @@ -110,7 +115,11 @@ bool BonjourDialog::show_and_lookup() // so that both threads can access it safely. auto dguard = std::make_shared(this); + // Note: More can be done here when we support discovery of hosts other than Octoprint and SL1 + Bonjour::TxtKeys txt_keys { "version", "model" }; + bonjour = std::move(Bonjour("octoprint") + .set_txt_keys(std::move(txt_keys)) .set_retries(3) .set_timeout(4) .on_reply([dguard](BonjourReply &&reply) { @@ -157,9 +166,20 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e) return; } + // Filter replies based on selected technology + const auto model = e.reply.txt_data.find("model"); + const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1"; + if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) { + return; + } + replies->insert(std::move(e.reply)); auto selected = get_selected(); + + wxWindowUpdateLocker freeze_guard(this); + (void)freeze_guard; + list->DeleteAllItems(); // The whole list is recreated so that we benefit from it already being sorted in the set. @@ -168,12 +188,18 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e) auto item = list->InsertItem(0, reply.full_address); list->SetItem(item, 1, reply.hostname); list->SetItem(item, 2, reply.service_name); - list->SetItem(item, 3, reply.version); + + if (tech == ptFFF) { + const auto it = reply.txt_data.find("version"); + if (it != reply.txt_data.end()) { + list->SetItem(item, 3, GUI::from_u8(it->second)); + } + } } - for (int i = 0; i < 4; i++) { - this->list->SetColumnWidth(i, wxLIST_AUTOSIZE); - if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); } + for (int i = 0; i < list->GetColumnCount(); i++) { + list->SetColumnWidth(i, wxLIST_AUTOSIZE); + if (list->GetColumnWidth(i) < 100) { list->SetColumnWidth(i, 100); } } if (!selected.IsEmpty()) { diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp index e3f53790b..a9a33d522 100644 --- a/src/slic3r/GUI/BonjourDialog.hpp +++ b/src/slic3r/GUI/BonjourDialog.hpp @@ -5,6 +5,8 @@ #include +#include "libslic3r/PrintConfig.hpp" + class wxListView; class wxStaticText; class wxTimer; @@ -21,7 +23,7 @@ class ReplySet; class BonjourDialog: public wxDialog { public: - BonjourDialog(wxWindow *parent); + BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology); BonjourDialog(BonjourDialog &&) = delete; BonjourDialog(const BonjourDialog &) = delete; BonjourDialog &operator=(BonjourDialog &&) = delete; @@ -37,6 +39,7 @@ private: std::shared_ptr bonjour; std::unique_ptr timer; unsigned timer_state; + Slic3r::PrinterTechnology tech; void on_reply(BonjourReplyEvent &); void on_timer(wxTimerEvent &); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6c7f246a6..bd0adcab0 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1612,25 +1612,21 @@ bool Tab::current_preset_is_dirty() void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) { - const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA; + const PrinterTechnology tech = m_presets->get_selected_preset().printer_technology(); // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) - if (! sla) { + if (tech == ptFFF) { optgroup->append_single_option_line("host_type"); } - auto printhost_browse = [this, optgroup] (wxWindow* parent) { - - // TODO: SLA Bonjour - + auto printhost_browse = [=](wxWindow* parent) { 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)); btn->SetBitmap(create_scaled_bitmap("zoom.png")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); - btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent &e) { - BonjourDialog dialog(parent); + btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { + BonjourDialog dialog(parent, tech); if (dialog.show_and_lookup()) { optgroup->set_value("print_host", std::move(dialog.get_selected()), true); optgroup->get_field("print_host")->field_changed(); @@ -1643,7 +1639,6 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) auto print_host_test = [this](wxWindow* parent) { auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); -// btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); btn->SetBitmap(create_scaled_bitmap("wrench.png")); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(btn); diff --git a/src/slic3r/Utils/Bonjour.cpp b/src/slic3r/Utils/Bonjour.cpp index 4953cfc64..28b3b2228 100644 --- a/src/slic3r/Utils/Bonjour.cpp +++ b/src/slic3r/Utils/Bonjour.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,9 @@ namespace Slic3r { // the implementations has been tested with AFL. -// Relevant RFC: https://www.ietf.org/rfc/rfc6762.txt +// Relevant RFCs: +// https://tools.ietf.org/html/rfc6762.txt +// https://tools.ietf.org/html/rfc6763.txt struct DnsName: public std::string @@ -156,9 +159,9 @@ struct DnsQuestion uint16_t type; uint16_t qclass; - DnsQuestion() : - type(0), - qclass(0) + DnsQuestion() + : type(0) + , qclass(0) {} static optional decode(const std::vector &buffer, size_t &offset) @@ -187,10 +190,10 @@ struct DnsResource uint32_t ttl; std::vector data; - DnsResource() : - type(0), - rclass(0), - ttl(0) + DnsResource() + : type(0) + , rclass(0) + , ttl(0) {} static optional decode(const std::vector &buffer, size_t &offset, size_t &dataoffset) @@ -310,9 +313,9 @@ struct DnsRR_TXT TAG = 0x10, }; - std::vector values; + BonjourReply::TxtData data; - static optional decode(const DnsResource &rr) + static optional decode(const DnsResource &rr, const Bonjour::TxtKeys &txt_keys) { const size_t size = rr.data.size(); if (size < 2) { @@ -328,11 +331,21 @@ struct DnsRR_TXT } ++it; - std::string value(val_size, ' '); - std::copy(it, it + val_size, value.begin()); - res.values.push_back(std::move(value)); + const auto it_end = it + val_size; + const auto it_eq = std::find(it, it_end, '='); + if (it_eq > it && it_eq < it_end - 1) { + std::string key(it_eq - it, ' '); + std::copy(it, it_eq, key.begin()); - it += val_size; + if (txt_keys.find(key) != txt_keys.end() || key == "path") { + // This key-value has been requested for + std::string value(it_end - it_eq - 1, ' '); + std::copy(it_eq + 1, it_end, value.begin()); + res.data.insert(std::make_pair(std::move(key), std::move(value))); + } + } + + it = it_end; } return std::move(res); @@ -389,7 +402,7 @@ struct DnsMessage DnsSDMap sdmap; - static optional decode(const std::vector &buffer) + static optional decode(const std::vector &buffer, const Bonjour::TxtKeys &txt_keys) { const auto size = buffer.size(); if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) { @@ -414,14 +427,15 @@ struct DnsMessage if (!rr) { return boost::none; } else { - res.parse_rr(buffer, std::move(*rr), dataoffset); + res.parse_rr(buffer, std::move(*rr), dataoffset, txt_keys); } } return std::move(res); } + private: - void parse_rr(const std::vector &buffer, DnsResource &&rr, size_t dataoffset) + void parse_rr(const std::vector &buffer, DnsResource &&rr, size_t dataoffset, const Bonjour::TxtKeys &txt_keys) { switch (rr.type) { case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break; @@ -432,7 +446,7 @@ private: break; } case DnsRR_TXT::TAG: { - auto txt = DnsRR_TXT::decode(rr); + auto txt = DnsRR_TXT::decode(rr, txt_keys); if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); } break; } @@ -442,26 +456,28 @@ private: std::ostream& operator<<(std::ostream &os, const DnsMessage &msg) { - os << "DnsMessage(ID: " << msg.header.id << ", " - << "Q: " << (msg.question ? msg.question->name.c_str() : "none") << ", " - << "A: " << (msg.rr_a ? msg.rr_a->ip.to_string() : "none") << ", " - << "AAAA: " << (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none") << ", " - << "services: ["; + os << boost::format("DnsMessage(ID: %1%, Q: %2%, A: %3%, AAAA: %4%, services: [") + % msg.header.id + % (msg.question ? msg.question->name.c_str() : "none") + % (msg.rr_a ? msg.rr_a->ip.to_string() : "none") + % (msg.rr_aaaa ? msg.rr_aaaa->ip.to_string() : "none"); - enum { SRV_PRINT_MAX = 3 }; - unsigned i = 0; - for (const auto &sdpair : msg.sdmap) { - os << sdpair.first << ", "; + enum { SRV_PRINT_MAX = 3 }; + unsigned i = 0; + for (const auto &sdpair : msg.sdmap) { + if (i > 0) { os << ", "; } - if (++i >= SRV_PRINT_MAX) { - os << "..."; - break; - } + if (i < SRV_PRINT_MAX) { + os << sdpair.first; + } else { + os << "..."; + break; } - os << "])"; + i++; + } - return os; + return os << "])"; } @@ -525,8 +541,9 @@ optional BonjourRequest::make(const std::string &service, const struct Bonjour::priv { const std::string service; - const std::string protocol; - const std::string service_dn; + std::string protocol; + std::string service_dn; + TxtKeys txt_keys; unsigned timeout; unsigned retries; @@ -535,19 +552,18 @@ struct Bonjour::priv Bonjour::ReplyFn replyfn; Bonjour::CompleteFn completefn; - priv(std::string service, std::string protocol); + priv(std::string &&service); std::string strip_service_dn(const std::string &service_name) const; void udp_receive(udp::endpoint from, size_t bytes); void lookup_perform(); }; -Bonjour::priv::priv(std::string service, std::string protocol) : - service(std::move(service)), - protocol(std::move(protocol)), - service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()), - timeout(10), - retries(1) +Bonjour::priv::priv(std::string &&service) + : service(std::move(service)) + , protocol("tcp") + , timeout(10) + , retries(1) { buffer.resize(DnsMessage::MAX_SIZE); } @@ -573,13 +589,13 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) } buffer.resize(bytes); - const auto dns_msg = DnsMessage::decode(buffer); + auto dns_msg = DnsMessage::decode(buffer, txt_keys); if (dns_msg) { asio::ip::address ip = from.address(); if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; } else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; } - for (const auto &sdpair : dns_msg->sdmap) { + for (auto &sdpair : dns_msg->sdmap) { if (! sdpair.second.srv) { continue; } @@ -590,20 +606,12 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) std::string path; std::string version; + BonjourReply::TxtData txt_data; if (sdpair.second.txt) { - static const std::string tag_path = "path="; - static const std::string tag_version = "version="; - - for (const auto &value : sdpair.second.txt->values) { - if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) { - path = std::move(value.substr(tag_path.size())); - } else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) { - version = std::move(value.substr(tag_version.size())); - } - } + txt_data = std::move(sdpair.second.txt->data); } - BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(path), std::move(version)); + BonjourReply reply(ip, srv.port, std::move(service_name), srv.hostname, std::move(txt_data)); replyfn(std::move(reply)); } } @@ -611,6 +619,8 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes) void Bonjour::priv::lookup_perform() { + service_dn = (boost::format("_%1%._%2%.local") % service % protocol).str(); + const auto brq = BonjourRequest::make(service, protocol); if (!brq) { return; @@ -671,21 +681,29 @@ void Bonjour::priv::lookup_perform() // API - public part -BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version) : - ip(std::move(ip)), - port(port), - service_name(std::move(service_name)), - hostname(std::move(hostname)), - path(path.empty() ? std::move(std::string("/")) : std::move(path)), - version(version.empty() ? std::move(std::string("Unknown")) : std::move(version)) +BonjourReply::BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, BonjourReply::TxtData txt_data) + : ip(std::move(ip)) + , port(port) + , service_name(std::move(service_name)) + , hostname(std::move(hostname)) + , txt_data(std::move(txt_data)) { std::string proto; std::string port_suffix; if (port == 443) { proto = "https://"; } if (port != 443 && port != 80) { port_suffix = std::to_string(port).insert(0, 1, ':'); } - if (this->path[0] != '/') { this->path.insert(0, 1, '/'); } + + std::string path = this->path(); + if (path[0] != '/') { path.insert(0, 1, '/'); } full_address = proto + ip.to_string() + port_suffix; - if (this->path != "/") { full_address += path; } + if (path != "/") { full_address += path; } + txt_data["path"] = std::move(path); +} + +std::string BonjourReply::path() const +{ + const auto it = txt_data.find("path"); + return it != txt_data.end() ? it->second : std::string("/"); } bool BonjourReply::operator==(const BonjourReply &other) const @@ -707,14 +725,22 @@ bool BonjourReply::operator<(const BonjourReply &other) const std::ostream& operator<<(std::ostream &os, const BonjourReply &reply) { - os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", " - << reply.hostname << ", " << reply.path << ", " << reply.version << ")"; - return os; + os << boost::format("BonjourReply(%1%, %2%, %3%, %4%") + % reply.ip.to_string() + % reply.service_name + % reply.hostname + % reply.full_address; + + for (const auto &kv : reply.txt_data) { + os << boost::format(", %1%=%2%") % kv.first % kv.second; + } + + return os << ')'; } -Bonjour::Bonjour(std::string service, std::string protocol) : - p(new priv(std::move(service), std::move(protocol))) +Bonjour::Bonjour(std::string service) + : p(new priv(std::move(service))) {} Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {} @@ -726,6 +752,18 @@ Bonjour::~Bonjour() } } +Bonjour& Bonjour::set_protocol(std::string protocol) +{ + if (p) { p->protocol = std::move(protocol); } + return *this; +} + +Bonjour& Bonjour::set_txt_keys(TxtKeys txt_keys) +{ + if (p) { p->txt_keys = std::move(txt_keys); } + return *this; +} + Bonjour& Bonjour::set_timeout(unsigned timeout) { if (p) { p->timeout = timeout; } diff --git a/src/slic3r/Utils/Bonjour.hpp b/src/slic3r/Utils/Bonjour.hpp index 63f34638c..e61cd1833 100644 --- a/src/slic3r/Utils/Bonjour.hpp +++ b/src/slic3r/Utils/Bonjour.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -13,16 +15,24 @@ namespace Slic3r { struct BonjourReply { + typedef std::unordered_map TxtData; + boost::asio::ip::address ip; uint16_t port; std::string service_name; std::string hostname; std::string full_address; - std::string path; - std::string version; + + TxtData txt_data; BonjourReply() = delete; - BonjourReply(boost::asio::ip::address ip, uint16_t port, std::string service_name, std::string hostname, std::string path, std::string version); + BonjourReply(boost::asio::ip::address ip, + uint16_t port, + std::string service_name, + std::string hostname, + TxtData txt_data); + + std::string path() const; bool operator==(const BonjourReply &other) const; bool operator<(const BonjourReply &other) const; @@ -39,11 +49,17 @@ public: typedef std::shared_ptr Ptr; typedef std::function ReplyFn; typedef std::function CompleteFn; + typedef std::set TxtKeys; - Bonjour(std::string service, std::string protocol = "tcp"); + Bonjour(std::string service); Bonjour(Bonjour &&other); ~Bonjour(); + // Set requested service protocol, "tcp" by default + Bonjour& set_protocol(std::string protocol); + // Set which TXT key-values should be collected + // Note that "path" is always collected + Bonjour& set_txt_keys(TxtKeys txt_keys); Bonjour& set_timeout(unsigned timeout); Bonjour& set_retries(unsigned retries); // ^ Note: By default there is 1 retry (meaning 1 broadcast is sent). From 5c9cede9bfc2a38eb3aa83ad0429793ec4287200 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 14 Mar 2019 14:46:01 +0100 Subject: [PATCH 2/8] Fix HiDPI in Bonjour Dialog --- src/slic3r/GUI/BonjourDialog.cpp | 30 +++++++++++++++++------------- src/slic3r/GUI/MsgDialog.cpp | 2 -- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 41e9d5392..ec6b2f0c9 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -13,6 +13,7 @@ #include #include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/Utils/Bonjour.hpp" @@ -50,35 +51,36 @@ struct LifetimeGuard LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {} }; -// FIXME: use em, resizable - BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech) - : wxDialog(parent, wxID_ANY, _(L("Network lookup"))) - , list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))) + : wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER) + , list(new wxListView(this, wxID_ANY)) , replies(new ReplySet) , label(new wxStaticText(this, wxID_ANY, "")) , timer(new wxTimer()) , timer_state(0) , tech(tech) { + const int em = GUI::wxGetApp().em_unit(); + list->SetMinSize(wxSize(80 * em, 30 * em)); + wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); - vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); + vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em); list->SetSingleStyle(wxLC_SINGLE_SEL); list->SetSingleStyle(wxLC_SORT_DESCENDING); - list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50); - list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100); - list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200); + list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 5 * em); + list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 10 * em); + list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 20 * em); if (tech == ptFFF) { - list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50); + list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 5 * em); } - vsizer->Add(list, 1, wxEXPAND | wxALL, 10); + vsizer->Add(list, 1, wxEXPAND | wxALL, em); wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL); - button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10); - button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10); + button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, em); + button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, em); // ^ Note: The Ok/Cancel labels are translated by wxWidgets vsizer->Add(button_sizer, 0, wxALIGN_CENTER); @@ -197,9 +199,11 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e) } } + const int em = GUI::wxGetApp().em_unit(); + for (int i = 0; i < list->GetColumnCount(); i++) { list->SetColumnWidth(i, wxLIST_AUTOSIZE); - if (list->GetColumnWidth(i) < 100) { list->SetColumnWidth(i, 100); } + if (list->GetColumnWidth(i) < 10 * em) { list->SetColumnWidth(i, 10 * em); } } if (!selected.IsEmpty()) { diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 176d19fb4..7cf70840d 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -24,7 +24,6 @@ namespace GUI { MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) : -// MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id) MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png"), button_id) {} @@ -70,7 +69,6 @@ MsgDialog::~MsgDialog() {} ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), -// wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), create_scaled_bitmap("Slic3r_192px_grayscale.png"), wxID_NONE) , msg(msg) From aaf5c6c663b1c37c15c62071a45cd778d31fcb38 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 14 Mar 2019 14:54:12 +0100 Subject: [PATCH 3/8] MsgDialog: Make resizable --- src/slic3r/GUI/MsgDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 7cf70840d..961888455 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -28,7 +28,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he {} MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) : - wxDialog(parent, wxID_ANY, title), + wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER), boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)), content_sizer(new wxBoxSizer(wxVERTICAL)), btn_sizer(new wxBoxSizer(wxHORIZONTAL)) From d6b409d0d98bacffb67e9f1f81731726f144a283 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 14 Mar 2019 15:11:27 +0100 Subject: [PATCH 4/8] Update the SLA slices at the print preview once the index is calculated. --- src/libslic3r/PrintBase.hpp | 1 + src/libslic3r/SLAPrint.cpp | 5 ++++- src/slic3r/GUI/Plater.cpp | 10 ++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index e01b678a5..2d47d3567 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -272,6 +272,7 @@ public: NO_RELOAD_SCENE = 0, RELOAD_SCENE = 1 << 1, RELOAD_SLA_SUPPORT_POINTS = 1 << 2, + RELOAD_SLA_PREVIEW = 1 << 3, }; // Bitmap of FlagBits unsigned int flags; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index b6c8aad13..52f1e032b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -821,7 +821,7 @@ void SLAPrint::process() // We have the layer polygon collection but we need to unite them into // an index where the key is the height level in discrete levels (clipper) - auto index_slices = [ilhd](SLAPrintObject& po) { + auto index_slices = [this, ilhd](SLAPrintObject& po) { po.m_slice_index.clear(); auto sih = LevelID(scale_(ilhd)); @@ -890,6 +890,9 @@ void SLAPrint::process() sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i); } } + + // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices. + report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); }; // Rasterizing the model objects, and their supports diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 64d189859..601c2ddb6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2434,8 +2434,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) { - this->statusbar()->set_progress(evt.status.percent); - this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…")); + if (evt.status.percent >= -1) { + this->statusbar()->set_progress(evt.status.percent); + this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…")); + } if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) { switch (this->printer_technology) { case ptFFF: @@ -2453,6 +2455,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) // Update SLA gizmo (reload_scene calls update_gizmos_data) q->canvas3D()->reload_scene(true); } + if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { + // Update the SLA preview + this->preview->reload_print(); + } } void Plater::priv::on_slicing_completed(wxCommandEvent &) From 639b641722c21cb6aae12ced9535be64c1c7e349 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 14 Mar 2019 15:45:52 +0100 Subject: [PATCH 5/8] Disabled DoubleBuffered for Tabs + set helper functions like a static (GUI_ObjectList.cpp) --- src/slic3r/GUI/GUI_ObjectList.cpp | 4 ++-- src/slic3r/GUI/Tab.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index f6ccbbd7d..7eef82063 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -44,12 +44,12 @@ static PrinterTechnology printer_technology() } // Config from current edited printer preset -DynamicPrintConfig& printer_config() +static DynamicPrintConfig& printer_config() { return wxGetApp().preset_bundle->printers.get_edited_preset().config; } -int extruders_count() +static int extruders_count() { return printer_technology() == ptSLA ? 1 : printer_config().option("nozzle_diameter")->values.size(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index bd0adcab0..ae33443c3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -74,7 +74,7 @@ void Tab::set_type() void Tab::create_preset_tab() { #ifdef __WINDOWS__ - SetDoubleBuffered(true); +// SetDoubleBuffered(true); #endif //__WINDOWS__ m_preset_bundle = wxGetApp().preset_bundle; From 6928284cc38e3ab6a21f898b812bd805534c9dcd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 14 Mar 2019 16:31:55 +0100 Subject: [PATCH 6/8] Loading of FFF print preview when switching to the print preview screen: always refresh from the Print with the exception of finished G-code export, where the final G-code preview is cached. --- src/slic3r/GUI/GUI_Preview.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6f177d8b4..e2539473b 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -746,10 +746,11 @@ void Preview::load_print_as_fff() if (IsShown()) { - if (gcode_preview_data_valid) + if (gcode_preview_data_valid) { // Load the real G-code preview. m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); - else + m_loaded = true; + } else // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); @@ -761,7 +762,6 @@ void Preview::load_print_as_fff() m_canvas_widget->Refresh(); } else update_sliders(zs); - m_loaded = true; } } From 60260c26be31c6a68165f33a56749ddde4d9483d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 14 Mar 2019 18:47:26 +0100 Subject: [PATCH 7/8] Fixed command line slicing due to some missing config keys --- src/libslic3r/Print.cpp | 9 ++++++--- src/libslic3r/SLAPrint.cpp | 9 ++++++--- src/slic3r.cpp | 8 ++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index bd7772f80..794390133 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -661,6 +661,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Make a copy of the config, normalize it. DynamicPrintConfig config(config_in); + config.option("print_settings_id", true); + config.option("filament_settings_id", true); + config.option("printer_settings_id", true); config.normalize(); // Collect changes to print config. t_config_option_keys print_diff = m_config.diff(config); @@ -688,9 +691,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co PlaceholderParser &pp = this->placeholder_parser(); pp.apply_only(config, placeholder_parser_diff); // Set the profile aliases for the PrintBase::output_filename() - pp.set("print_preset", config_in.option("print_settings_id" )->clone()); - pp.set("filament_preset", config_in.option("filament_settings_id")->clone()); - pp.set("printer_preset", config_in.option("printer_settings_id" )->clone()); + pp.set("print_preset", config.option("print_settings_id")->clone()); + pp.set("filament_preset", config.option("filament_settings_id")->clone()); + pp.set("printer_preset", config.option("printer_settings_id")->clone()); } // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 52f1e032b..0e8e717cc 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -118,6 +118,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf // Make a copy of the config, normalize it. DynamicPrintConfig config(config_in); + config.option("sla_print_settings_id", true); + config.option("sla_material_settings_id", true); + config.option("printer_settings_id", true); config.normalize(); // Collect changes to print config. t_config_option_keys print_diff = m_print_config.diff(config); @@ -151,9 +154,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf PlaceholderParser &pp = this->placeholder_parser(); pp.apply_config(config); // Set the profile aliases for the PrintBase::output_filename() - pp.set("print_preset", config_in.option("sla_print_settings_id")->clone()); - pp.set("material_preset", config_in.option("sla_material_settings_id")->clone()); - pp.set("printer_preset", config_in.option("printer_settings_id")->clone()); + pp.set("print_preset", config.option("sla_print_settings_id")->clone()); + pp.set("material_preset", config.option("sla_material_settings_id")->clone()); + pp.set("printer_preset", config.option("printer_settings_id")->clone()); } // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. diff --git a/src/slic3r.cpp b/src/slic3r.cpp index f88b84ce7..b60c5b1dd 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -68,7 +68,7 @@ int CLI::run(int argc, char **argv) // load config files supplied via --load for (auto const &file : load_configs) { if (! boost::filesystem::exists(file)) { - if (m_config.opt_bool("ignore_nonexistent_file")) { + if (m_config.opt_bool("ignore_nonexistent_config")) { continue; } else { boost::nowide::cerr << "No such file: " << file << std::endl; @@ -132,9 +132,9 @@ int CLI::run(int argc, char **argv) // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; - SLAFullPrintConfig sla_print_config; +// SLAFullPrintConfig sla_print_config; fff_print_config.apply(m_print_config, true); - sla_print_config.apply(m_print_config, true); +// sla_print_config.apply(m_print_config, true); // Loop through transform options. for (auto const &opt_key : m_transforms) { @@ -530,7 +530,7 @@ bool CLI::setup(int argc, char **argv) for (auto const &opt_key : opt_order) { if (cli_actions_config_def.has(opt_key)) m_actions.emplace_back(opt_key); - if (cli_transform_config_def.has(opt_key)) + else if (cli_transform_config_def.has(opt_key)) m_transforms.emplace_back(opt_key); } From 5b01eb30047e3a67896ee02bd7dd6fc606a8dc40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 15 Mar 2019 09:13:15 +0100 Subject: [PATCH 8/8] 2nd fix for x position of gizmos' imgui dialogs --- src/slic3r/GUI/GLCanvas3D.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8178c9cf6..33f460802 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3039,6 +3039,7 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = get_total_overlay_height(); + float width = get_total_overlay_width(); #if ENABLE_SVG_ICONS float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; #else @@ -3050,7 +3051,7 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan float left = top_x; float top = top_y; - float right = left + get_total_overlay_width() * inv_zoom; + float right = left + width * inv_zoom; float bottom = top - height * inv_zoom; // renders background @@ -3158,24 +3159,24 @@ void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCan #if ENABLE_SVG_ICONS float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; - float top = sprite_id * v_icon_size; - float left = state * u_icon_size; - float bottom = top + v_icon_size; - float right = left + u_icon_size; + float v_top = sprite_id * v_icon_size; + float u_left = state * u_icon_size; + float v_bottom = v_top + v_icon_size; + float u_right = u_left + u_icon_size; #else float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size; - float top = sprite_id * uv_icon_size; - float left = state * uv_icon_size; - float bottom = top + uv_icon_size; - float right = left + uv_icon_size; + float v_top = sprite_id * uv_icon_size; + float u_left = state * uv_icon_size; + float v_bottom = v_top + uv_icon_size; + float u_right = u_left + uv_icon_size; #endif // ENABLE_SVG_ICONS - GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { left, bottom }, { right, bottom }, { right, top }, { left, top } }); + GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); #if ENABLE_IMGUI if (it->second->get_state() == GLGizmoBase::On) { float toolbar_top = (float)cnv_h - canvas.m_view_toolbar.get_height(); #if ENABLE_SVG_ICONS - it->second->render_input_window(2.0f * m_overlay_border + m_overlay_icons_size, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); + it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); #else it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); #endif // ENABLE_SVG_ICONS