From 5f52aa5901c77bc89d4f082d6f3c2793b59e8223 Mon Sep 17 00:00:00 2001 From: matthiasdanner Date: Mon, 22 Sep 2025 17:54:24 +0200 Subject: [PATCH] Fix "Show only Visible Constraints" option (#23187) Co-authored-by: Matthias Danner <28687794+matthiasdanner@users.noreply.github.com> --- src/Mod/Sketcher/App/Constraint.cpp | 6 ++ src/Mod/Sketcher/App/Constraint.h | 1 + src/Mod/Sketcher/App/SketchObject.cpp | 63 +++++++++++++++++++ src/Mod/Sketcher/App/SketchObject.h | 4 ++ src/Mod/Sketcher/App/SketchObject.pyi | 6 ++ src/Mod/Sketcher/App/SketchObjectPyImp.cpp | 51 +++++++++++++++ .../Gui/EditModeConstraintCoinManager.cpp | 9 +-- .../Sketcher/Gui/TaskSketcherConstraints.cpp | 56 +++++++++++++---- .../Sketcher/Gui/TaskSketcherConstraints.h | 1 + 9 files changed, 182 insertions(+), 15 deletions(-) diff --git a/src/Mod/Sketcher/App/Constraint.cpp b/src/Mod/Sketcher/App/Constraint.cpp index 9734ac9195..916da1bcaa 100644 --- a/src/Mod/Sketcher/App/Constraint.cpp +++ b/src/Mod/Sketcher/App/Constraint.cpp @@ -87,6 +87,7 @@ Constraint* Constraint::copy() const temp->isDriving = this->isDriving; temp->InternalAlignmentIndex = this->InternalAlignmentIndex; temp->isInVirtualSpace = this->isInVirtualSpace; + temp->isVisible = this->isVisible; temp->isActive = this->isActive; temp->elements = this->elements; // Do not copy tag, otherwise it is considered a clone, and a "rename" by the expression engine. @@ -161,6 +162,7 @@ void Constraint::Save(Writer& writer) const << "LabelPosition=\"" << LabelPosition << "\" " << "IsDriving=\"" << (int)isDriving << "\" " << "IsInVirtualSpace=\"" << (int)isInVirtualSpace << "\" " + << "IsVisible=\"" << (int)isVisible << "\" " << "IsActive=\"" << (int)isActive << "\" "; // Save elements @@ -230,6 +232,10 @@ void Constraint::Restore(XMLReader& reader) isInVirtualSpace = reader.getAttribute("IsInVirtualSpace"); } + if (reader.hasAttribute("IsVisible")) { + isVisible = reader.getAttribute("IsVisible"); + } + if (reader.hasAttribute("IsActive")) { isActive = reader.getAttribute("IsActive"); } diff --git a/src/Mod/Sketcher/App/Constraint.h b/src/Mod/Sketcher/App/Constraint.h index 00491e5bd4..c8595845a4 100644 --- a/src/Mod/Sketcher/App/Constraint.h +++ b/src/Mod/Sketcher/App/Constraint.h @@ -216,6 +216,7 @@ public: // index of pole in a bspline). It is not a GeoId!! int InternalAlignmentIndex {-1}; bool isInVirtualSpace {false}; + bool isVisible {true}; bool isActive {true}; diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 186f3729ef..80552a9e9d 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -1344,6 +1344,69 @@ int SketchObject::toggleVirtualSpace(int ConstrId) } +int SketchObject::setVisibility(std::vector constrIds, bool isVisible) +{ + // no need to check input data validity as this is an sketchobject managed operation. + Base::StateLocker lock(managedoperation, true); + + if (constrIds.empty()) + return 0; + + std::sort(constrIds.begin(), constrIds.end()); + + const std::vector& vals = this->Constraints.getValues(); + + if (constrIds.front() < 0 || constrIds.back() >= int(vals.size())) + return -1; + + std::vector newVals(vals); + + for (auto cid : constrIds) { + // clone the changed Constraint + if (vals[cid]->isVisible != isVisible) { + Constraint* constNew = vals[cid]->clone(); + constNew->isVisible = isVisible; + newVals[cid] = constNew; + } + } + + this->Constraints.setValues(std::move(newVals)); + + // Solver didn't actually update, but we need this to inform view provider + // to redraw + signalSolverUpdate(); + + return 0; +} + +int SketchObject::setVisibility(int ConstrId, bool isVisible) +{ + // no need to check input data validity as this is an sketchobject managed operation. + Base::StateLocker lock(managedoperation, true); + + const std::vector& vals = this->Constraints.getValues(); + + if (ConstrId < 0 || ConstrId >= int(vals.size())) + return -1; + + // copy the list + std::vector newVals(vals); + + // clone the changed Constraint + Constraint* constNew = vals[ConstrId]->clone(); + constNew->isVisible = isVisible; + newVals[ConstrId] = constNew; + + this->Constraints.setValues(std::move(newVals)); + + // Solver didn't actually update, but we need this to inform view provider + // to redraw + signalSolverUpdate(); + + return 0; +} + + int SketchObject::setUpSketch() { lastDoF = solvedSketch.setUpSketch( diff --git a/src/Mod/Sketcher/App/SketchObject.h b/src/Mod/Sketcher/App/SketchObject.h index d7dcb6dfc1..10582f9d4a 100644 --- a/src/Mod/Sketcher/App/SketchObject.h +++ b/src/Mod/Sketcher/App/SketchObject.h @@ -384,6 +384,10 @@ public: int getVirtualSpace(int ConstrId, bool& isinvirtualspace) const; /// toggle the driving status of this constraint int toggleVirtualSpace(int ConstrId); + /// set the visibility of this constraint + int setVisibility(int ConstrId, bool isVisible); + /// set the visibility of a group of constraints at once + int setVisibility(std::vector constrIds, bool isVisible); /// move this point to a new location and solve int moveGeometries(const std::vector& geoEltIds, const Base::Vector3d& toPoint, diff --git a/src/Mod/Sketcher/App/SketchObject.pyi b/src/Mod/Sketcher/App/SketchObject.pyi index be0a4b938d..1a7cffefb1 100644 --- a/src/Mod/Sketcher/App/SketchObject.pyi +++ b/src/Mod/Sketcher/App/SketchObject.pyi @@ -465,6 +465,12 @@ class SketchObject(Part2DObject): """ ... + def setVisibility(self) -> None: + """ + Set the visibility of a constraint + """ + ... + def getVirtualSpace(self) -> bool: """ Get the VirtualSpace status of a constraint diff --git a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp index 451751ea33..2cc2ebae23 100644 --- a/src/Mod/Sketcher/App/SketchObjectPyImp.cpp +++ b/src/Mod/Sketcher/App/SketchObjectPyImp.cpp @@ -1051,6 +1051,57 @@ PyObject* SketchObjectPy::setVirtualSpace(PyObject* args) throw Py::TypeError(error); } +PyObject* SketchObjectPy::setVisibility(PyObject* args) +{ + PyObject* isVisible; + PyObject* id_or_ids; + + if (!PyArg_ParseTuple(args, "OO!", &id_or_ids, &PyBool_Type, &isVisible)) { + return nullptr; + } + + if (PyObject_TypeCheck(id_or_ids, &(PyList_Type)) + || PyObject_TypeCheck(id_or_ids, &(PyTuple_Type))) { + std::vector constrIds; + Py::Sequence list(id_or_ids); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + if (PyLong_Check((*it).ptr())) { + constrIds.push_back(PyLong_AsLong((*it).ptr())); + } + } + + try { + int ret = + this->getSketchObjectPtr()->setVisibility(constrIds, Base::asBoolean(isVisible)); + + if (ret == -1) { + throw Py::TypeError("Impossible to set visibility!"); + } + } + catch (const Base::ValueError& e) { + throw Py::ValueError(e.getMessage()); + } + + Py_Return; + } + else if (PyLong_Check(id_or_ids)) { + if (this->getSketchObjectPtr()->setVisibility(PyLong_AsLong(id_or_ids), + Base::asBoolean(isVisible))) { + std::stringstream str; + str << "Not able set visibility for constraint with the given index: " + << PyLong_AsLong(id_or_ids); + PyErr_SetString(PyExc_ValueError, str.str().c_str()); + return nullptr; + } + + Py_Return; + } + + std::string error = std::string("type must be list of Constraint Ids, not "); + error += id_or_ids->ob_type->tp_name; + throw Py::TypeError(error); +} + PyObject* SketchObjectPy::getVirtualSpace(PyObject* args) { int constrid; diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index fa39b6b1fe..667c59e8f7 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -104,8 +104,8 @@ void EditModeConstraintCoinManager::updateVirtualSpace() SbBool* sws = editModeScenegraphNodes.constrGroup->enable.startEditing(); for (size_t i = 0; i < constrlist.size(); i++) { - sws[i] = !(constrlist[i]->isInVirtualSpace - != isshownvirtualspace); // XOR of constraint mode and VP mode + sws[i] = !(constrlist[i]->isInVirtualSpace != isshownvirtualspace) + && constrlist[i]->isVisible; // XOR of constraint mode and VP mode } @@ -2414,8 +2414,9 @@ void EditModeConstraintCoinManager::drawConstraintIcons(const GeoListFacade& geo thisIcon.position = absPos; thisIcon.destination = coinIconPtr; thisIcon.infoPtr = infoPtr; - thisIcon.visible = constraint->isInVirtualSpace - == ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider); + thisIcon.visible = (constraint->isInVirtualSpace + == ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider)) + && constraint->isVisible; if (constraint->Type == Symmetric) { Base::Vector3d startingpoint = diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index 81df3ac6b8..2b8a610d38 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -1472,37 +1472,37 @@ void TaskSketcherConstraints::change3DViewVisibilityToTrackFilter(bool filterEna const Sketcher::SketchObject* sketch = sketchView->getSketchObject(); const std::vector& vals = sketch->Constraints.getValues(); - std::vector constrIdsToVirtualSpace; - std::vector constrIdsToCurrentSpace; + std::vector constrIdsToSetVisible; + std::vector constrIdsToSetHidden; for (std::size_t i = 0; i < vals.size(); ++i) { ConstraintItem* it = static_cast(ui->listWidgetConstraints->item(i)); bool visible = !filterEnabled || !isConstraintFiltered(it); // If the constraint is filteredout and it was previously shown in 3D view - if (!visible && it->isInVirtualSpace() == sketchView->getIsShownVirtualSpace()) { - constrIdsToVirtualSpace.push_back(it->ConstraintNbr); + if (!visible) { + constrIdsToSetHidden.push_back(it->ConstraintNbr); } - else if (visible && it->isInVirtualSpace() != sketchView->getIsShownVirtualSpace()) { - constrIdsToCurrentSpace.push_back(it->ConstraintNbr); + else if (visible) { + constrIdsToSetVisible.push_back(it->ConstraintNbr); } } - if (!constrIdsToVirtualSpace.empty()) { - bool ret = doSetVirtualSpace(constrIdsToVirtualSpace, true); + if (!constrIdsToSetVisible.empty()) { + bool ret = doSetVisible(constrIdsToSetVisible, true); if (!ret) { return; } } - if (!constrIdsToCurrentSpace.empty()) { - bool ret = doSetVirtualSpace(constrIdsToCurrentSpace, false); + if (!constrIdsToSetHidden.empty()) { + bool ret = doSetVisible(constrIdsToSetHidden, false); if (!ret) { return; } } - if (constrIdsToVirtualSpace.empty() && constrIdsToCurrentSpace.empty()) { + if (constrIdsToSetVisible.empty() && constrIdsToSetHidden.empty()) { slotConstraintsChanged(); } } @@ -1541,6 +1541,40 @@ bool TaskSketcherConstraints::doSetVirtualSpace(const std::vector& constrId return true; } +bool TaskSketcherConstraints::doSetVisible(const std::vector& constrIds, bool isVisible) { + assert(sketchView); + const Sketcher::SketchObject* sketch = sketchView->getSketchObject(); + + std::stringstream stream; + + stream << '['; + + for (size_t i = 0; i < constrIds.size() - 1; i++) { + stream << constrIds[i] << ","; + } + stream << constrIds[constrIds.size() - 1] << ']'; + + std::string constrIdList = stream.str(); + + Gui::Command::openCommand( + QT_TRANSLATE_NOOP("Command", "Update constraint's visibility")); + try { + Gui::cmdAppObjectArgs(sketch, + "setVisibility(%s, %s)", + constrIdList, + isVisible ? "True" : "False"); + Gui::Command::commitCommand(); + } + catch (const Base::Exception& e) { + Gui::Command::abortCommand(); + + Gui::TranslatedUserError( + sketch, tr("Error"), tr("Impossible to update visibility:") + QLatin1String(" ") + QLatin1String(e.what())); + return false; + } + return true; +} + bool TaskSketcherConstraints::isConstraintFiltered(QListWidgetItem* item) { assert(sketchView); diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h index b79559201f..dff3ff514e 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h @@ -169,6 +169,7 @@ private: bool isConstraintFiltered(QListWidgetItem* item); void change3DViewVisibilityToTrackFilter(bool filterEnabled); bool doSetVirtualSpace(const std::vector& constrIds, bool isvirtualspace); + bool doSetVisible(const std::vector& constrIds, bool isVisible); void changeFilteredVisibility(bool show, ActionTarget target = ActionTarget::All); void updateSelectionFilter(); void updateAssociatedConstraintsFilter();