diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp index e796e3cc1..d271a2505 100644 --- a/src/libslic3r/Exception.hpp +++ b/src/libslic3r/Exception.hpp @@ -2,6 +2,7 @@ #define _libslic3r_Exception_h_ #include +#include namespace Slic3r { @@ -35,6 +36,16 @@ public: private: size_t objectId_ = 0; }; + +class SlicingErrors : public Exception +{ +public: + using Exception::Exception; + SlicingErrors(const std::vector &errors) : Exception("Errors"), errors_(errors) {} + + std::vector errors_; +}; + #undef SLIC3R_DERIVE_EXCEPTION } // namespace Slic3r diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a4d9f7ee9..7d00701f4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -808,7 +808,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec // first layer may result in skirt/brim in the air and maybe other issues. if (layers_to_print.size() == 1u) { if (!has_extrusions) - throw Slic3r::SlicingError(_(L("One object has empty initial layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id); + throw Slic3r::SlicingError(_(L("The following object(s) have empty initial layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id); } // In case there are extrusions on this layer, check there is a layer to lay it on. @@ -863,8 +863,16 @@ std::vector>> GCode::collec std::vector> per_object(print.objects().size(), std::vector()); std::vector ordering; + + std::vector errors; + for (size_t i = 0; i < print.objects().size(); ++i) { - per_object[i] = collect_layers_to_print(*print.objects()[i]); + try { + per_object[i] = collect_layers_to_print(*print.objects()[i]); + } catch (const Slic3r::SlicingError &e) { + errors.push_back(e); + continue; + } OrderingItem ordering_item; ordering_item.object_idx = i; ordering.reserve(ordering.size() + per_object[i].size()); @@ -875,6 +883,8 @@ std::vector>> GCode::collec ordering.emplace_back(ordering_item); } } + + if (!errors.empty()) { throw Slic3r::SlicingErrors(errors); } std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { return oi1.print_z < oi2.print_z; }); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 5124aa8ea..5d20d5fea 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -46,9 +46,10 @@ bool SlicingProcessCompletedEvent::critical_error() const } catch (const Slic3r::SlicingError &) { // Exception derived from SlicingError is non-critical. return false; - } catch (...) { - } - return true; + } catch (const Slic3r::SlicingErrors &) { + return false; + } catch (...) {} + return true; } bool SlicingProcessCompletedEvent::invalidate_plater() const @@ -69,7 +70,7 @@ bool SlicingProcessCompletedEvent::invalidate_plater() const return false; } -std::pair SlicingProcessCompletedEvent::format_error_message() const +std::pair> SlicingProcessCompletedEvent::format_error_message() const { std::string error; size_t monospace = 0; @@ -88,12 +89,20 @@ std::pair SlicingProcessCompletedEvent::format_error_messag } catch (SlicingError &ex) { error = ex.what(); monospace = ex.objectId(); + } catch (SlicingErrors &exs) { + std::vector ids; + for (auto &ex : exs.errors_) { + error = ex.what(); + monospace = ex.objectId(); + ids.push_back(monospace); + } + return std::make_pair(std::move(error), ids); } catch (std::exception &ex) { - error = ex.what(); - } catch (...) { - error = "Unknown C++ exception."; - } - return std::make_pair(std::move(error), monospace); + error = ex.what(); + } catch (...) { + error = "Unknown C++ exception."; + } + return std::make_pair(std::move(error), std::vector{monospace}); } BackgroundSlicingProcess::BackgroundSlicingProcess() diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index c2a1cf280..3222aece7 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -60,7 +60,7 @@ public: void rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); } // Produce a human readable message to be displayed by a notification or a message box. // 2nd parameter defines whether the output should be displayed with a monospace font. - std::pair format_error_message() const; + std::pair> format_error_message() const; private: StatusType m_status; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 37b8ee3b1..cf20dde46 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -8945,7 +8945,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) break; case SLICING_ERROR: if (state) - notification_manager.push_slicing_error_notification(text, conflictObj); + notification_manager.push_slicing_error_notification(text, {conflictObj}); else notification_manager.close_slicing_error_notification(text); break; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 12d9f9b6e..05ef45a0c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1630,19 +1630,33 @@ void NotificationManager::push_validate_error_notification(StringObjectException set_slicing_progress_hidden(); } -void NotificationManager::push_slicing_error_notification(const std::string &text, ModelObject const *obj) +void NotificationManager::push_slicing_error_notification(const std::string &text, std::vector objs) { - auto callback = obj ? [id = obj->id()](wxEvtHandler *) { + std::vector ids; + for (auto optr : objs) { ids.push_back(optr->id()); } + auto callback = !objs.empty() ? [ids](wxEvtHandler *) { auto & objects = wxGetApp().model().objects; - auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); - if (iter != objects.end()) { - wxGetApp().obj_list()->select_items({{*iter, nullptr}}); + std::vector ovs; + for (auto id : ids) { + auto iter = std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }); + if (iter != objects.end()) { ovs.push_back({*iter, nullptr}); } + } + if (!ovs.empty()) { + wxGetApp().obj_list()->select_items(ovs); wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); } return false; } : std::function(); auto link = callback ? _u8L("Jump to") : ""; - if (obj) link += std::string(" [") + obj->name + "]"; + if (!objs.empty()) { + link += " ["; + for (auto obj : objs) { link += obj->name + ", "; } + if (!objs.empty()) { + link.pop_back(); + link.pop_back(); + } + link += "] "; + } set_all_slicing_errors_gray(false); push_notification_data({ NotificationType::SlicingError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("Error:") + "\n" + text, link, callback }, 0); set_slicing_progress_hidden(); diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 9016a3df2..7fb3fcdf1 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -192,7 +192,7 @@ public: void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host); void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host); // Creates Slicing Error notification with a custom text and no fade out. - void push_slicing_error_notification(const std::string &text, ModelObject const *obj); + void push_slicing_error_notification(const std::string &text, std::vector objs); // Creates Slicing Warning notification with a custom text and no fade out. void push_slicing_warning_notification(const std::string &text, bool gray, ModelObject const *obj, ObjectID oid, int warning_step, int warning_msg_id); // marks slicing errors as gray diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ce426b2cb..66cb495e0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5878,21 +5878,24 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt) // This bool stops showing export finished notification even when process_completed_with_error is false bool has_error = false; if (evt.error()) { - std::pair message = evt.format_error_message(); + auto message = evt.format_error_message(); if (evt.critical_error()) { if (q->m_tracking_popup_menu) { // We don't want to pop-up a message box when tracking a pop-up menu. // We postpone the error message instead. q->m_tracking_popup_menu_error_message = message.first; } else { - show_error(q, message.first, message.second != 0); + show_error(q, message.first, message.second.size() != 0 && message.second[0] != 0); notification_manager->set_slicing_progress_hidden(); } } else { - ModelObject const *model_object = nullptr; - const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(message.second)); - if (print_object) model_object = print_object->model_object(); - notification_manager->push_slicing_error_notification(message.first, model_object); + std::vector ptrs; + for (auto oid : message.second) + { + const PrintObject *print_object = this->background_process.m_fff_print->get_object(ObjectID(oid)); + if (print_object) { ptrs.push_back(print_object->model_object()); } + } + notification_manager->push_slicing_error_notification(message.first, ptrs); } if (evt.invalidate_plater()) {