mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2026-03-09 13:05:00 -06:00
Replace all with stl (#11145)
* Add "Replace all from STL" command this operates on the currently selected plate or on the selection * Add more translations for "Replace all from STL" * build fix * "Replace all with STL" also works with multiple selected plates * fix build * replace all from stl: better error handling and a nicer status message box --------- Co-authored-by: Nils Hasler <hasler@thecaptury.com>
This commit is contained in:
parent
e3f049829b
commit
dedfd9d4ed
6 changed files with 219 additions and 4 deletions
|
|
@ -1949,9 +1949,15 @@ msgstr ""
|
|||
msgid "Replace with STL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Replace all with STL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Replace the selected part with new STL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Replace all selected parts with STL from folder"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change filament"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -2271,6 +2277,18 @@ msgid ""
|
|||
"cut information first."
|
||||
msgstr ""
|
||||
|
||||
msgid "✖ Skipped %1%: %2%, same file\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "✖ Skipped %1%: %2% does not exist.\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "✖ Skipped %1%: failed to replace.\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "✔ Replaced %1% with %2%\n"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all connectors"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6697,6 +6715,12 @@ msgstr ""
|
|||
msgid "File for the replace wasn't selected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select folder to replace from"
|
||||
msgstr ""
|
||||
|
||||
msgid "Directory for the replace wasn't selected"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please select a file"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -2065,6 +2065,12 @@ msgstr "Durch STL Datei austauschen"
|
|||
msgid "Replace the selected part with new STL"
|
||||
msgstr "Ausgewähltes Teil durch eine neue STL ersetzen."
|
||||
|
||||
msgid "Replace all with STL"
|
||||
msgstr "Alle durch STL Dateien austauschen"
|
||||
|
||||
msgid "Replace all selected parts with STL from folder"
|
||||
msgstr "Ausgewählte Teile durch neue STL aus Ordner ersetzen."
|
||||
|
||||
msgid "Change filament"
|
||||
msgstr "Filament wechseln"
|
||||
|
||||
|
|
@ -2432,6 +2438,18 @@ msgstr ""
|
|||
"Um mit massiven Teilen oder negativen Volumen zu arbeiten, \n"
|
||||
"müssen Sie zuerst die Schnittinformationen ungültig machen."
|
||||
|
||||
msgid "✖ Skipped %1%: %2%, same file\n"
|
||||
msgstr "✖ %1% übersprungen: %2%, gleiche Datei\n"
|
||||
|
||||
msgid "✖ Skipped %1%: %2% does not exist.\n"
|
||||
msgstr "✖ %1% übersprungen: %2% existiert nicht.\n"
|
||||
|
||||
msgid "✔ Replaced %1% with %2%\n"
|
||||
msgstr "✔ %1% durch %2% ersetzt\n"
|
||||
|
||||
msgid "✖ Skipped %1%: failed to replace.\n"
|
||||
msgstr "✖ %1% übersprungen: Ersetzen fehlgeschlagen.\n"
|
||||
|
||||
msgid "Delete all connectors"
|
||||
msgstr "Lösche alle Verbinder"
|
||||
|
||||
|
|
@ -7400,6 +7418,12 @@ msgstr "Wählen Sie eine neue Datei aus"
|
|||
msgid "File for the replace wasn't selected"
|
||||
msgstr "Datei für das Ersetzen wurde nicht ausgewählt"
|
||||
|
||||
msgid "Select folder to replace from"
|
||||
msgstr "Wählen Sie ein Verzeichnis aus um daraus zu ersetzen"
|
||||
|
||||
msgid "Directory for the replace wasn't selected"
|
||||
msgstr "Verzeichnis um daraus zu ersetzen wurde nicht ausgewählt"
|
||||
|
||||
msgid "Please select a file"
|
||||
msgstr "Bitte wählen Sie eine Datei"
|
||||
|
||||
|
|
|
|||
|
|
@ -869,6 +869,13 @@ void MenuFactory::append_menu_item_replace_with_stl(wxMenu *menu)
|
|||
[]() { return plater()->can_replace_with_stl(); }, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_replace_all_with_stl(wxMenu *menu)
|
||||
{
|
||||
append_menu_item(menu, wxID_ANY, _L("Replace all with STL"), _L("Replace all selected parts with STL from folder"),
|
||||
[](wxCommandEvent &) { plater()->replace_all_with_stl(); }, "", menu,
|
||||
[]() { return plater()->can_replace_all_with_stl(); }, m_parent);
|
||||
}
|
||||
|
||||
void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
|
||||
{
|
||||
// BBS
|
||||
|
|
@ -1350,6 +1357,7 @@ void MenuFactory::create_extra_object_menu()
|
|||
m_object_menu.AppendSeparator();
|
||||
append_menu_item_reload_from_disk(&m_object_menu);
|
||||
append_menu_item_replace_with_stl(&m_object_menu);
|
||||
append_menu_item_replace_all_with_stl(&m_object_menu);
|
||||
append_menu_item_export_stl(&m_object_menu);
|
||||
}
|
||||
|
||||
|
|
@ -1465,6 +1473,7 @@ void MenuFactory::create_bbl_part_menu()
|
|||
append_menu_item_change_type(menu);
|
||||
append_menu_item_reload_from_disk(menu);
|
||||
append_menu_item_replace_with_stl(menu);
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
}
|
||||
|
||||
void MenuFactory::create_bbl_assemble_part_menu()
|
||||
|
|
@ -1615,6 +1624,7 @@ void MenuFactory::create_plate_menu()
|
|||
[](wxCommandEvent&) { plater()->add_file(); }, "", menu,
|
||||
[]() {return wxGetApp().plater()->can_add_model(); }, m_parent);
|
||||
#endif
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
|
||||
|
||||
return;
|
||||
|
|
@ -1721,14 +1731,26 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||
wxDataViewItemArray sels;
|
||||
obj_list()->GetSelections(sels);
|
||||
bool multi_volume = true;
|
||||
bool undefined_type = false;
|
||||
bool all_plates = true;
|
||||
|
||||
for (const wxDataViewItem& item : sels) {
|
||||
multi_volume = list_model()->GetItemType(item) & itVolume;
|
||||
if (!(list_model()->GetItemType(item) & (itVolume | itObject | itInstance)))
|
||||
Slic3r::GUI::ItemType item_type = list_model()->GetItemType(item);
|
||||
if ((item_type & itPlate) == 0)
|
||||
all_plates = false;
|
||||
multi_volume = item_type & itVolume;
|
||||
if (!(item_type & (itVolume | itObject | itInstance)))
|
||||
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
|
||||
return nullptr;
|
||||
undefined_type = true;
|
||||
}
|
||||
|
||||
if (all_plates) {
|
||||
wxMenu* menu = new MenuWithSeparators();
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
return menu;
|
||||
}
|
||||
if (undefined_type)
|
||||
return nullptr;
|
||||
wxMenu* menu = new MenuWithSeparators();
|
||||
if (!multi_volume) {
|
||||
int index = 0;
|
||||
|
|
@ -1747,6 +1769,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||
append_menu_item_per_object_process(menu);
|
||||
menu->AppendSeparator();
|
||||
append_menu_items_convert_unit(menu);
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
//BBS
|
||||
append_menu_item_change_filament(menu);
|
||||
menu->AppendSeparator();
|
||||
|
|
@ -1759,6 +1782,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||
//append_menu_item_simplify(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_items_convert_unit(menu);
|
||||
append_menu_item_replace_all_with_stl(menu);
|
||||
append_menu_item_change_filament(menu);
|
||||
wxMenu* split_menu = new wxMenu();
|
||||
if (split_menu) {
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ private:
|
|||
void append_menu_item_export_stl(wxMenu* menu, bool is_mulity_menu = false);
|
||||
void append_menu_item_reload_from_disk(wxMenu* menu);
|
||||
void append_menu_item_replace_with_stl(wxMenu* menu);
|
||||
void append_menu_item_replace_all_with_stl(wxMenu* menu);
|
||||
void append_menu_item_change_extruder(wxMenu* menu);
|
||||
void append_menu_item_set_visible(wxMenu* menu);
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
|
|
|
|||
|
|
@ -4171,6 +4171,7 @@ struct Plater::priv
|
|||
void reload_from_disk();
|
||||
bool replace_volume_with_stl(int object_idx, int volume_idx, const fs::path& new_path, const std::string& snapshot = "");
|
||||
void replace_with_stl();
|
||||
void replace_all_with_stl();
|
||||
void reload_all_from_disk();
|
||||
|
||||
//BBS: add no_slice option
|
||||
|
|
@ -4284,6 +4285,7 @@ struct Plater::priv
|
|||
bool can_fillcolor() const;
|
||||
bool has_assemble_view() const;
|
||||
bool can_replace_with_stl() const;
|
||||
bool can_replace_all_with_stl() const;
|
||||
bool can_split(bool to_objects) const;
|
||||
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
bool can_scale_to_print_volume() const;
|
||||
|
|
@ -7634,6 +7636,132 @@ void Plater::priv::replace_with_stl()
|
|||
}
|
||||
}
|
||||
|
||||
void Plater::priv::replace_all_with_stl()
|
||||
{
|
||||
if (! q->get_view3D_canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::EType::Undefined))
|
||||
return;
|
||||
|
||||
const Selection& selection = get_selection();
|
||||
|
||||
if (selection.is_wipe_tower())
|
||||
return;
|
||||
|
||||
fs::path input_path;
|
||||
Selection::IndicesList volume_idxs = selection.get_volume_idxs();
|
||||
|
||||
// when plates are selected instead of volumes
|
||||
// then selection is inaccurate, we need to
|
||||
// find volumes contained in selected plates
|
||||
|
||||
if (selection.is_empty() || volume_idxs.empty()) {
|
||||
std::vector<int> selected_plate_idxs;
|
||||
|
||||
wxDataViewItemArray sels;
|
||||
wxGetApp().obj_list()->GetSelections(sels);
|
||||
for (const wxDataViewItem& item : sels) {
|
||||
Slic3r::GUI::ItemType item_type = wxGetApp().obj_list()->GetModel()->GetItemType(item);
|
||||
if (item_type & itPlate) {
|
||||
if (item.IsOk()) {
|
||||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode *>(item.GetID());
|
||||
selected_plate_idxs.push_back(node->GetPlateIdx());
|
||||
}
|
||||
}
|
||||
}
|
||||
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
|
||||
for (int obj_idx = 0; obj_idx < selection.get_model()->objects.size(); obj_idx++) {
|
||||
for (int plate_idx : selected_plate_idxs) {
|
||||
PartPlate* plate = plate_list.get_plate(plate_idx);
|
||||
if (plate && plate->contain_instance_totally(obj_idx, 0)) {
|
||||
std::vector<unsigned int> indices = selection.get_volume_idxs_from_object(obj_idx);
|
||||
volume_idxs.insert(indices.begin(), indices.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find path for initializing the file selection dialog
|
||||
|
||||
for (unsigned int idx : volume_idxs) {
|
||||
const GLVolume* v = selection.get_volume(idx);
|
||||
int object_idx = v->object_idx();
|
||||
int volume_idx = v->volume_idx();
|
||||
|
||||
const ModelObject* object = model.objects[object_idx];
|
||||
const ModelVolume* volume = object->volumes[volume_idx];
|
||||
|
||||
if (!volume->source.input_file.empty() && fs::exists(volume->source.input_file)) {
|
||||
input_path = volume->source.input_file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wxString title = _L("Select folder to replace from");
|
||||
title += ":";
|
||||
wxDirDialog dialog(q, title, from_u8(input_path.parent_path().string()), wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
||||
if (dialog.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
fs::path out_path = dialog.GetPath().ToUTF8().data();
|
||||
if (out_path.empty()) {
|
||||
MessageDialog dlg(q, _L("Directory for the replace wasn't selected"), _L("Error during replace"), wxOK | wxOK_DEFAULT | wxICON_WARNING);
|
||||
dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string status = _L("Replaced with STLs from directory:\n").ToStdString() + out_path.string() + "\n\n";
|
||||
|
||||
for (unsigned int idx : volume_idxs) {
|
||||
const GLVolume* v = selection.get_volume(idx);
|
||||
int object_idx = v->object_idx();
|
||||
int volume_idx = v->volume_idx();
|
||||
|
||||
const ModelObject* object = model.objects[object_idx];
|
||||
const ModelVolume* volume = object->volumes[volume_idx];
|
||||
|
||||
if (volume->source.input_file.empty())
|
||||
continue;
|
||||
|
||||
input_path = volume->source.input_file;
|
||||
|
||||
fs::path new_path = out_path / input_path.filename();
|
||||
|
||||
std::string volume_name = volume->name;
|
||||
|
||||
if (new_path == input_path) {
|
||||
status += boost::str(boost::format(_L("✖ Skipped %1%: same file.\n").ToStdString()) % volume_name);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " skipping replace volume : same filename " << new_path;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fs::exists(new_path)) {
|
||||
status += boost::str(boost::format(_L("✖ Skipped %1%: file does not exist.\n").ToStdString()) % volume_name);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cannot replace volume : filen does not exist " << new_path;
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " replacing volume : " << input_path << " with " << new_path;
|
||||
|
||||
if (!replace_volume_with_stl(object_idx, volume_idx, new_path, "Replace with STL")) {
|
||||
status += boost::str(boost::format(_L("✖ Skipped %1%: failed to replace.\n").ToStdString()) % volume_name);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cannot replace volume : failed to replace with " << new_path;
|
||||
continue;
|
||||
}
|
||||
|
||||
status += boost::str(boost::format(_L("✔ Replaced %1%.\n").ToStdString()) % volume_name);
|
||||
}
|
||||
|
||||
// update 3D scene
|
||||
update();
|
||||
|
||||
// new GLVolumes have been created at this point, so update their printable state
|
||||
for (size_t i = 0; i < model.objects.size(); ++i) {
|
||||
view3D->get_canvas3d()->update_instance_printable_state_for_object(i);
|
||||
}
|
||||
|
||||
MessageDialog dlg(q, status, _L("Replaced volumes"), wxOK | wxOK_DEFAULT | wxICON_INFORMATION);
|
||||
dlg.ShowModal();
|
||||
}
|
||||
|
||||
#if ENABLE_RELOAD_FROM_DISK_REWORK
|
||||
static std::vector<std::pair<int, int>> reloadable_volumes(const Model &model, const Selection &selection)
|
||||
{
|
||||
|
|
@ -10198,6 +10326,12 @@ bool Plater::priv::can_replace_with_stl() const
|
|||
&& get_selection().get_volume_idxs().size() == 1;
|
||||
}
|
||||
|
||||
bool Plater::priv::can_replace_all_with_stl() const
|
||||
{
|
||||
return !sidebar->obj_list()->has_selected_cut_object()
|
||||
&& get_selection().get_volume_idxs().size() != 1;
|
||||
}
|
||||
|
||||
bool Plater::priv::can_reload_from_disk() const
|
||||
{
|
||||
if (sidebar->obj_list()->has_selected_cut_object())
|
||||
|
|
@ -14036,7 +14170,7 @@ bool Plater::check_printer_initialized(MachineObject *obj, bool only_warning, bo
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (extruder.GetNozzleFlowType() == NozzleType::ntUndefine) {
|
||||
if (extruder.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE) {
|
||||
has_been_initialized = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -14721,6 +14855,11 @@ void Plater::replace_with_stl()
|
|||
p->replace_with_stl();
|
||||
}
|
||||
|
||||
void Plater::replace_all_with_stl()
|
||||
{
|
||||
p->replace_all_with_stl();
|
||||
}
|
||||
|
||||
void Plater::reload_all_from_disk()
|
||||
{
|
||||
p->reload_all_from_disk();
|
||||
|
|
@ -17317,6 +17456,7 @@ bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
|
|||
bool Plater::can_fillcolor() const { return p->can_fillcolor(); }
|
||||
bool Plater::has_assmeble_view() const { return p->has_assemble_view(); }
|
||||
bool Plater::can_replace_with_stl() const { return p->can_replace_with_stl(); }
|
||||
bool Plater::can_replace_all_with_stl() const { return p->can_replace_all_with_stl(); }
|
||||
bool Plater::can_mirror() const { return p->can_mirror(); }
|
||||
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
|
||||
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
|
|
|
|||
|
|
@ -491,6 +491,7 @@ public:
|
|||
|
||||
void reload_from_disk();
|
||||
void replace_with_stl();
|
||||
void replace_all_with_stl();
|
||||
void reload_all_from_disk();
|
||||
bool has_toolpaths_to_export() const;
|
||||
void export_toolpaths_to_obj() const;
|
||||
|
|
@ -667,6 +668,7 @@ public:
|
|||
bool can_redo() const;
|
||||
bool can_reload_from_disk() const;
|
||||
bool can_replace_with_stl() const;
|
||||
bool can_replace_all_with_stl() const;
|
||||
bool can_mirror() const;
|
||||
bool can_split(bool to_objects) const;
|
||||
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue