diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index bdadb7ad2e..cd46f0b4a2 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -81,6 +81,7 @@ #include "PythonDebugger.h" #include "MainWindowPy.h" #include "MDIViewPy.h" +#include "Placement.h" #include "SoFCDB.h" #include "Selection.h" #include "SelectionFilterPy.h" @@ -359,6 +360,7 @@ struct PyMethodDef FreeCADGui_methods[] = { } // namespace Gui +// clang-format off Application::Application(bool GUIenabled) { // App::GetApplication().Attach(this); @@ -452,6 +454,10 @@ Application::Application(bool GUIenabled) Base::Interpreter().addType(UiLoaderPy::type_object(), module, "UiLoader"); PyResource::init_type(); + Gui::Dialog::TaskPlacementPy::init_type(); + Base::Interpreter().addType(Gui::Dialog::TaskPlacementPy::type_object(), + module, "TaskPlacement"); + // PySide additions PyModule_AddObject(module, "PySideUic", Base::Interpreter().addModule(new PySideUicModule)); @@ -555,6 +561,7 @@ Application::Application(bool GUIenabled) MacroCommand::load(); } } +// clang-format on Application::~Application() { diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index d8bf79af53..60dcd3ecd9 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -1562,6 +1562,7 @@ void StdCmdPlacement::activated(int iMsg) plm->setPropertyName(QLatin1String("Placement")); plm->setSelection(selection); plm->bindObject(); + plm->clearSelection(); } } Gui::Control().showDialog(plm); diff --git a/src/Gui/Placement.cpp b/src/Gui/Placement.cpp index 372a80f3fa..307727c363 100644 --- a/src/Gui/Placement.cpp +++ b/src/Gui/Placement.cpp @@ -32,13 +32,16 @@ #include #include +#include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -77,7 +80,8 @@ public: return false; } - static App::PropertyPlacement* getProperty(App::DocumentObject* obj, const std::string& propertyName) + static App::PropertyPlacement* getProperty(const App::DocumentObject* obj, + const std::string& propertyName) { std::map props; obj->getPropertyMap(props); @@ -101,8 +105,9 @@ public: PlacementHandler::PlacementHandler() : propertyName{"Placement"} , changeProperty{false} + , ignoreTransaction{false} { - + setupDocument(); } void PlacementHandler::openTransactionIfNeeded() @@ -120,6 +125,38 @@ void PlacementHandler::setPropertyName(const std::string& name) changeProperty = (propertyName != "Placement"); } +void PlacementHandler::setIgnoreTransactions(bool value) +{ + ignoreTransaction = value; +} + +void PlacementHandler::setSelection(const std::vector& selection) +{ + selectionObjects = selection; +} + +void PlacementHandler::reselectObjects() +{ + //we have to clear selection and reselect original object(s) + //else later on the rotation is applied twice because there will + //be 2 (vertex) objects in the selection, and even if both are subobjects + //of the same object the rotation still gets applied twice + Gui::Selection().clearSelection(); + //reselect original object that was selected when placement dlg first opened + for (const auto& it : selectionObjects) { + Gui::Selection().addSelection(it); + } +} + +const App::DocumentObject* PlacementHandler::getFirstOfSelection() const +{ + if (!selectionObjects.empty()) { + return selectionObjects.front().getObject(); + } + + return nullptr; +} + const std::string& PlacementHandler::getPropertyName() const { return propertyName; @@ -155,25 +192,43 @@ void PlacementHandler::revertTransformation() revertTransformationOfViewProviders(document); } else { - document->abortCommand(); + abortCommandIfActive(document); } } } } -std::vector PlacementHandler::getObjects(Gui::Document* document) const +std::vector PlacementHandler::getObjects(const Gui::Document* document) const { - return document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId()); + auto objs = document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId()); + std::vector list; + list.insert(list.begin(), objs.begin(), objs.end()); + return list; } -std::vector PlacementHandler::getSelectedObjects(Gui::Document* document) const +std::vector PlacementHandler::getSelectedObjects(const Gui::Document* document) const { - return Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), document->getDocument()->getName()); + App::Document* doc = document->getDocument(); + std::vector list; + list.reserve(selectionObjects.size()); + for (const auto& it : selectionObjects) { + const App::DocumentObject* obj = it.getObject(); + if (obj && obj->getDocument() == doc) { + list.push_back(obj); + } + } + + if (list.empty()) { + auto objs = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), doc->getName()); + list.insert(list.begin(), objs.begin(), objs.end()); + } + + return list; } void PlacementHandler::revertTransformationOfViewProviders(Gui::Document* document) { - std::vector obj = getObjects(document); + std::vector obj = getObjects(document); for (const auto & it : obj) { auto property = find_placement::getProperty(it, this->propertyName); if (property) { @@ -186,13 +241,23 @@ void PlacementHandler::revertTransformationOfViewProviders(Gui::Document* docume } } +void PlacementHandler::setRefPlacement(const Base::Placement& plm) +{ + ref = plm; +} + +const Base::Placement& PlacementHandler::getRefPlacement() const +{ + return ref; +} + void PlacementHandler::applyPlacement(const Base::Placement& p, bool incremental) { Gui::Document* document = Application::Instance->activeDocument(); if (!document) return; - std::vector sel = getSelectedObjects(document); + std::vector sel = getSelectedObjects(document); if (!sel.empty()) { for (const auto & it : sel) { applyPlacement(document, it, p, incremental); @@ -203,7 +268,8 @@ void PlacementHandler::applyPlacement(const Base::Placement& p, bool incremental } } -void PlacementHandler::applyPlacement(Gui::Document* document, App::DocumentObject* obj, const Base::Placement& p, bool incremental) +void PlacementHandler::applyPlacement(const Gui::Document* document, const App::DocumentObject* obj, + const Base::Placement& p, bool incremental) { auto property = find_placement::getProperty(obj, this->propertyName); if (property) { @@ -234,19 +300,18 @@ void PlacementHandler::applyPlacement(const QString& data, bool incremental) // When directly changing the property we now only have to commit the transaction, // do a recompute and open a new transaction if (changeProperty) { - document->commitCommand(); + commitCommandIfActive(document); tryRecompute(document); - document->openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); + openCommandIfActive(document); } else { - std::vector sel = getSelectedObjects(document); + std::vector sel = getSelectedObjects(document); if (!sel.empty()) { - document->openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); + openCommandIfActive(document); for (const auto & it : sel) { applyPlacement(it, data, incremental); } - - document->commitCommand(); + commitCommandIfActive(document); tryRecompute(document); } else { @@ -255,7 +320,7 @@ void PlacementHandler::applyPlacement(const QString& data, bool incremental) } } -void PlacementHandler::applyPlacement(App::DocumentObject* obj, const QString& data, bool incremental) +void PlacementHandler::applyPlacement(const App::DocumentObject* obj, const QString& data, bool incremental) { auto property = find_placement::getProperty(obj, this->propertyName); if (property) { @@ -271,7 +336,7 @@ void PlacementHandler::applyPlacement(App::DocumentObject* obj, const QString& d } } -QString PlacementHandler::getIncrementalPlacement(App::DocumentObject* obj, const QString& data) const +QString PlacementHandler::getIncrementalPlacement(const App::DocumentObject* obj, const QString& data) const { return QString::fromLatin1( R"(App.getDocument("%1").%2.%3=%4.multiply(App.getDocument("%1").%2.%3))") @@ -281,7 +346,7 @@ QString PlacementHandler::getIncrementalPlacement(App::DocumentObject* obj, cons data); } -QString PlacementHandler::getSimplePlacement(App::DocumentObject* obj, const QString& data) const +QString PlacementHandler::getSimplePlacement(const App::DocumentObject* obj, const QString& data) const { return QString::fromLatin1( "App.getDocument(\"%1\").%2.%3=%4") @@ -291,6 +356,52 @@ QString PlacementHandler::getSimplePlacement(App::DocumentObject* obj, const QSt data); } +Base::Vector3d PlacementHandler::computeCenterOfMass() const +{ + Base::Vector3d centerOfMass; + std::vector sel = Gui::Selection().getObjectsOfType + (App::GeoFeature::getClassTypeId()); + if (!sel.empty()) { + for (auto it : sel) { + const App::PropertyComplexGeoData* propgeo = static_cast(it)->getPropertyOfGeometry(); + const Data::ComplexGeoData* geodata = propgeo ? propgeo->getComplexData() : nullptr; + if (geodata && geodata->getCenterOfGravity(centerOfMass)) { + break; + } + } + } + return centerOfMass; +} + +void PlacementHandler::setCenterOfMass(const Base::Vector3d& pnt) +{ + cntOfMass = pnt; +} + +Base::Vector3d PlacementHandler::getCenterOfMass() const +{ + return cntOfMass; +} + +std::tuple> PlacementHandler::getSelectedPoints() const +{ + std::vector selection = Gui::Selection().getSelectionEx(); + std::vector picked; + //combine all pickedpoints into single vector + //even if points are from separate objects + Base::Vector3d firstSelected; //first selected will be central point when 3 points picked + for (auto it = selection.begin(); it != selection.end(); ++it) { + std::vector points = it->getPickedPoints(); + if (it == selection.begin() && !points.empty()) { + firstSelected = points[0]; + } + + picked.insert(picked.begin(), points.begin(), points.end()); + } + + return std::make_tuple(firstSelected, picked); +} + void PlacementHandler::tryRecompute(Gui::Document* document) { try { @@ -300,6 +411,44 @@ void PlacementHandler::tryRecompute(Gui::Document* document) } } +void PlacementHandler::setupDocument() +{ + //NOLINTBEGIN + connectAct = Application::Instance->signalActiveDocument.connect + (std::bind(&PlacementHandler::slotActiveDocument, this, sp::_1)); + //NOLINTEND + App::Document* activeDoc = App::GetApplication().getActiveDocument(); + if (activeDoc) { + appendDocument(activeDoc->getName()); + } +} + +void PlacementHandler::slotActiveDocument(const Gui::Document& doc) +{ + activatedDocument(doc.getDocument()->getName()); +} + +void PlacementHandler::openCommandIfActive(Gui::Document* doc) +{ + if (!ignoreTransaction) { + doc->openCommand(QT_TRANSLATE_NOOP("Command", "Placement")); + } +} + +void PlacementHandler::commitCommandIfActive(Gui::Document* doc) +{ + if (!ignoreTransaction) { + doc->commitCommand(); + } +} + +void PlacementHandler::abortCommandIfActive(Gui::Document* doc) +{ + if (!ignoreTransaction) { + doc->abortCommand(); + } +} + // ---------------------------------------------------------------------------- /* TRANSLATOR Gui::Dialog::Placement */ @@ -312,13 +461,11 @@ Placement::Placement(QWidget* parent, Qt::WindowFlags fl) setupConnections(); setupUnits(); setupSignalMapper(); - setupDocument(); setupRotationMethod(); } Placement::~Placement() { - connectAct.disconnect(); delete ui; } @@ -331,7 +478,8 @@ void Placement::setupUi() void Placement::setupConnections() { - connect(ui->applyButton, &QPushButton::clicked, + QPushButton* applyButton = ui->buttonBox->button(QDialogButtonBox::Apply); + connect(applyButton, &QPushButton::clicked, this, &Placement::onApplyButtonClicked); connect(ui->applyIncrementalPlacement, &QCheckBox::toggled, this, &Placement::onApplyIncrementalPlacementToggled); @@ -391,18 +539,6 @@ void Placement::setupSignalMapper() #endif } -void Placement::setupDocument() -{ - //NOLINTBEGIN - connectAct = Application::Instance->signalActiveDocument.connect - (std::bind(&Placement::slotActiveDocument, this, sp::_1)); - //NOLINTEND - App::Document* activeDoc = App::GetApplication().getActiveDocument(); - if (activeDoc) { - handler.appendDocument(activeDoc->getName()); - } -} - void Placement::setupRotationMethod() { ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement"); @@ -413,9 +549,7 @@ void Placement::setupRotationMethod() void Placement::showDefaultButtons(bool ok) { - ui->oKButton->setVisible(ok); - ui->closeButton->setVisible(ok); - ui->applyButton->setVisible(ok); + ui->buttonBox->setVisible(ok); ui->buttonBoxLayout->invalidate(); if (ok) { ui->buttonBoxLayout->insertSpacerItem(0, ui->buttonBoxSpacer); @@ -430,11 +564,6 @@ void Placement::open() handler.openTransactionIfNeeded(); } -void Placement::slotActiveDocument(const Gui::Document& doc) -{ - handler.activatedDocument(doc.getDocument()->getName()); -} - QWidget* Placement::getInvalidInput() const { QList sb = this->findChildren(); @@ -466,57 +595,31 @@ void Placement::onCenterOfMassToggled(bool on) ui->zCnt->setDisabled(on); if (on) { - cntOfMass = getCenterOfMass(); - ui->xCnt->setValue(cntOfMass.x); - ui->yCnt->setValue(cntOfMass.y); - ui->zCnt->setValue(cntOfMass.z); + Base::Vector3d pnt = handler.computeCenterOfMass(); + handler.setCenterOfMass(pnt); + ui->xCnt->setValue(pnt.x); + ui->yCnt->setValue(pnt.y); + ui->zCnt->setValue(pnt.z); } } void Placement::onSelectedVertexClicked() { - cntOfMass.Set(0,0,0); ui->centerOfMass->setChecked(false); - bool success=false; - std::vector selection = Gui::Selection().getSelectionEx(); - std::vector picked; - //combine all pickedpoints into single vector - //even if points are from separate objects - Base::Vector3d firstSelected; //first selected will be central point when 3 points picked - for (auto it=selection.begin(); it!=selection.end(); ++it){ - std::vector points = it->getPickedPoints(); - if (it==selection.begin() && !points.empty()){ - firstSelected=points[0]; - } - picked.insert(picked.begin(),points.begin(),points.end()); - } - //we have to clear selection and reselect original object(s) - //else later on the rotation is applied twice because there will - //be 2 (vertex) objects in the selection, and even if both are subobjects - //of the same object the rotation still gets applied twice - Gui::Selection().clearSelection(); - //reselect original object that was selected when placement dlg first opened - for (const auto& it : selectionObjects) - Gui::Selection().addSelection(it); + Base::Vector3d center; + bool success = false; + auto [firstSelected, picked] = handler.getSelectedPoints(); + handler.reselectObjects(); if (picked.size() == 1) { - ui->xCnt->setValue(picked[0].x); - ui->yCnt->setValue(picked[0].y); - ui->zCnt->setValue(picked[0].z); - cntOfMass.x=picked[0].x; - cntOfMass.y=picked[0].y; - cntOfMass.z=picked[0].z; - success=true; + center = picked[0]; + success = true; } else if (picked.size() == 2) { //average the coords to get center of rotation - ui->xCnt->setValue((picked[0].x+picked[1].x)/2.0); - ui->yCnt->setValue((picked[0].y+picked[1].y)/2.0); - ui->zCnt->setValue((picked[0].z+picked[1].z)/2.0); - cntOfMass.x=(picked[0].x+picked[1].x)/2.0; - cntOfMass.y=(picked[0].y+picked[1].y)/2.0; - cntOfMass.z=(picked[0].z+picked[1].z)/2.0; + center = (picked[0] + picked[1]) / 2.0; + //setup a customized axis since the user selected 2 points //keep any existing angle, but setup our own axis Base::Placement plm = getPlacement(); @@ -525,11 +628,11 @@ void Placement::onSelectedVertexClicked() double angle; rot.getRawValue(tmp, angle); Base::Vector3d axis; - if (firstSelected==picked[0]){ - axis = Base::Vector3d(picked[1]-picked[0]); + if (firstSelected == picked[0]){ + axis = Base::Vector3d(picked[1] - picked[0]); } else { - axis = Base::Vector3d(picked[0]-picked[1]); + axis = Base::Vector3d(picked[0] - picked[1]); } double length = axis.Length(); Base::Console().Message("Distance: %.8f\n",length); @@ -546,7 +649,7 @@ void Placement::onSelectedVertexClicked() setPlacementData(plm); //creates custom axis, if needed ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler ui->stackedWidget->setCurrentIndex(0); - success=true; + success = true; } else if (picked.size() == 3){ /* User selected 3 points, so we find the plane defined by those @@ -559,7 +662,7 @@ void Placement::onSelectedVertexClicked() a = picked[1]; c = picked[2]; } - else if (picked[1]==firstSelected){ + else if (picked[1] == firstSelected){ a = picked[0]; c = picked[2]; } @@ -570,12 +673,8 @@ void Placement::onSelectedVertexClicked() Base::Vector3d norm((a-b).Cross(c-b)); norm.Normalize(); - ui->xCnt->setValue(b.x); - ui->yCnt->setValue(b.y); - ui->zCnt->setValue(b.z); - cntOfMass.x=b.x; - cntOfMass.y=b.y; - cntOfMass.z=b.z; + center = b; + //setup a customized axis normal to the plane //keep any existing angle, but setup our own axis Base::Placement plm = getPlacement(); @@ -604,10 +703,15 @@ void Placement::onSelectedVertexClicked() setPlacementData(plm); //creates custom axis, if needed ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler ui->stackedWidget->setCurrentIndex(0); - success=true; + success = true; } - if (!success){ + handler.setCenterOfMass(center); + ui->xCnt->setValue(center.x); + ui->yCnt->setValue(center.y); + ui->zCnt->setValue(center.z); + + if (!success) { Base::Console().Warning("Placement selection error. Select either 1 or 2 points.\n"); QMessageBox msgBox; msgBox.setText(tr("Please select 1, 2, or 3 points before clicking this button. A point may be on a vertex, \ @@ -619,10 +723,6 @@ lies on the vector that is normal to the plane defined by the 3 points. Some di information is provided in the report view, which can be useful when aligning objects. For your \ convenience when Shift + click is used the appropriate distance or angle is copied to the clipboard.")); msgBox.exec(); - ui->xCnt->setValue(0); - ui->yCnt->setValue(0); - ui->zCnt->setValue(0); - return; } } @@ -653,12 +753,12 @@ void Placement::onApplyAxialClicked() void Placement::onApplyIncrementalPlacementToggled(bool on) { if (on) { - this->ref = getPlacementData(); + handler.setRefPlacement(getPlacementData()); onResetButtonClicked(); } else { Base::Placement p = getPlacementData(); - p = p * this->ref; + p = p * handler.getRefPlacement(); setPlacementData(p); onPlacementChanged(0); } @@ -767,14 +867,19 @@ void Placement::onResetButtonClicked() */ void Placement::setSelection(const std::vector& selection) { - selectionObjects = selection; + handler.setSelection(selection); } -void Placement::setPropertyName(const std::string& name) +void Placement::setPropertyName(const std::string& name) { handler.setPropertyName(name); } +void Placement::setIgnoreTransactions(bool value) +{ + handler.setIgnoreTransactions(value); +} + /*! * \brief Placement::bindObject * Binds the spin boxes to the placement components of the first object of the selection. @@ -782,10 +887,34 @@ void Placement::setPropertyName(const std::string& name) */ void Placement::bindObject() { - if (!selectionObjects.empty()) { - App::DocumentObject* obj = selectionObjects.front().getObject(); - + if (const App::DocumentObject* obj = handler.getFirstOfSelection()) { std::string propertyName = handler.getPropertyName(); + bindProperty(obj, propertyName); + } +} + +/*! + * \brief Placement::setPlacementAndBindObject + * Sets the placement, binds the spin boxes to the placement components of the passed object and + * sets the name of the placement property. + */ +void Placement::setPlacementAndBindObject(const App::DocumentObject* obj, const std::string& propertyName) +{ + if (obj) { + App::PropertyPlacement* prop = find_placement::getProperty(obj, propertyName); + if (prop) { + setPlacement(prop->getValue()); + handler.setPropertyName(propertyName); + bindProperty(obj, propertyName); + handler.setSelection({SelectionObject{obj}}); + } + } +} + +void Placement::bindProperty(const App::DocumentObject* obj, const std::string& propertyName) +{ + // clang-format off + if (obj) { App::ObjectIdentifier path = App::ObjectIdentifier::parse(obj, propertyName); if (path.getProperty()) { ui->xPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.x"))); @@ -806,6 +935,7 @@ void Placement::bindObject() ui->rollAngle->evaluateExpression(); } } + // clang-format on } Base::Vector3d Placement::getDirection() const @@ -883,30 +1013,14 @@ Base::Vector3d Placement::getAnglesData() const Base::Vector3d Placement::getCenterData() const { - if (ui->centerOfMass->isChecked()) - return this->cntOfMass; + if (ui->centerOfMass->isChecked()) { + return handler.getCenterOfMass(); + } return Base::Vector3d(ui->xCnt->value().getValue(), ui->yCnt->value().getValue(), ui->zCnt->value().getValue()); } -Base::Vector3d Placement::getCenterOfMass() const -{ - Base::Vector3d centerOfMass; - std::vector sel = Gui::Selection().getObjectsOfType - (App::GeoFeature::getClassTypeId()); - if (!sel.empty()) { - for (auto it : sel) { - const App::PropertyComplexGeoData* propgeo = static_cast(it)->getPropertyOfGeometry(); - const Data::ComplexGeoData* geodata = propgeo ? propgeo->getComplexData() : nullptr; - if (geodata && geodata->getCenterOfGravity(centerOfMass)) { - break; - } - } - } - return centerOfMass; -} - Base::Placement Placement::getPlacementData() const { Base::Rotation rot = getRotationData(); @@ -1037,6 +1151,15 @@ void TaskPlacement::setSelection(const std::vector& select widget->setSelection(selection); } +/*! + * \brief TaskPlacement::clearSelection + * Clears the array of selection objects. + */ +void TaskPlacement::clearSelection() +{ + widget->setSelection({}); +} + /*! * \brief TaskPlacement::bindObject * Binds the spin boxes to the placement components of the first object of the selection. @@ -1047,6 +1170,12 @@ void TaskPlacement::bindObject() widget->bindObject(); } +void TaskPlacement::setPlacementAndBindObject(const App::DocumentObject* obj, + const std::string& propertyName) +{ + widget->setPlacementAndBindObject(obj, propertyName); +} + void TaskPlacement::open() { widget->open(); @@ -1093,4 +1222,270 @@ void TaskPlacement::clicked(int id) } } +// ---------------------------------------------- + +void TaskPlacementPy::init_type() +{ + behaviors().name("TaskPlacement"); + behaviors().doc("TaskPlacement"); + behaviors().set_tp_new(PyMake); + // you must have overwritten the virtual functions + behaviors().supportRepr(); + behaviors().supportGetattr(); + behaviors().supportSetattr(); + // clang-format off + add_varargs_method("setPropertyName", &TaskPlacementPy::setPropertyName, + "setPropertyName(string)"); + add_varargs_method("setPlacement", &TaskPlacementPy::setPlacement, + "setPlacement(Placement)"); + add_varargs_method("setSelection", &TaskPlacementPy::setSelection, + "setSelection(list)"); + add_varargs_method("bindObject", &TaskPlacementPy::bindObject, + "bindObject()"); + add_varargs_method("setPlacementAndBindObject", &TaskPlacementPy::setPlacementAndBindObject, + "setPlacementAndBindObject(obj, string)"); + add_varargs_method("setIgnoreTransactions", &TaskPlacementPy::setIgnoreTransactions, + "setIgnoreTransactions(bool)"); + add_varargs_method("showDefaultButtons", &TaskPlacementPy::showDefaultButtons, + "showDefaultButtons(bool)"); + add_varargs_method("accept", &TaskPlacementPy::accept, + "accept()"); + add_varargs_method("reject", &TaskPlacementPy::reject, + "reject()"); + add_varargs_method("clicked", &TaskPlacementPy::clicked, + "clicked()"); + add_varargs_method("open", &TaskPlacementPy::open, + "open()"); + add_varargs_method("isAllowedAlterDocument", &TaskPlacementPy::isAllowedAlterDocument, + "isAllowedAlterDocument()"); + add_varargs_method("isAllowedAlterView", &TaskPlacementPy::isAllowedAlterView, + "isAllowedAlterView()"); + add_varargs_method("isAllowedAlterSelection", &TaskPlacementPy::isAllowedAlterSelection, + "isAllowedAlterSelection()"); + add_varargs_method("getStandardButtons", &TaskPlacementPy::getStandardButtons, + "getStandardButtons()"); + // clang-format on +} + +PyObject* TaskPlacementPy::PyMake(struct _typeobject * type, PyObject * args, PyObject * kwds) +{ + Q_UNUSED(type) + Q_UNUSED(kwds) + if (!PyArg_ParseTuple(args, "")) { + return nullptr; + } + return new TaskPlacementPy(); +} + +TaskPlacementPy::TaskPlacementPy() + : widget{new Placement} +{ +} + +TaskPlacementPy::~TaskPlacementPy() = default; + +Py::Object TaskPlacementPy::repr() +{ + return Py::String("TaskPlacement"); +} + +Py::Object TaskPlacementPy::getattr(const char * name) +{ + if (strcmp(name, "form") == 0) { + Gui::PythonWrapper wrap; + wrap.loadWidgetsModule(); + return wrap.fromQWidget(widget, "QDialog"); + } + return BaseType::getattr(name); +} + +int TaskPlacementPy::setattr(const char* name, const Py::Object& attr) +{ + if (strcmp(name, "form") == 0 && attr.isNone()) { + delete widget; + widget = nullptr; + return {}; + } + return BaseType::setattr(name, attr); +} + +Py::Object TaskPlacementPy::setPropertyName(const Py::Tuple& args) +{ + const char* propname {}; + if (!PyArg_ParseTuple(args.ptr(), "s", &propname)) { + throw Py::Exception(); + } + + if (widget) { + widget->setPropertyName(propname); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::setPlacement(const Py::Tuple& args) +{ + PyObject* plm {}; + if (!PyArg_ParseTuple(args.ptr(), "O!", &Base::PlacementPy::Type, &plm)) { + throw Py::Exception(); + } + + if (widget) { + widget->setPlacement(*static_cast(plm)->getPlacementPtr()); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::setSelection(const Py::Tuple& args) +{ + std::vector sel; + Py::Sequence list(args[0]); + + for (const auto& obj : list) { + if (PyObject_TypeCheck(obj.ptr(), &App::DocumentObjectPy::Type)) { + auto doc = static_cast(obj.ptr()); + sel.emplace_back(doc->getDocumentObjectPtr()); + } + } + + if (widget) { + widget->setSelection(sel); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::bindObject(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + + if (widget) { + widget->bindObject(); + } + + return Py::None(); +} + +Py::Object TaskPlacementPy::setPlacementAndBindObject(const Py::Tuple& args) +{ + Py::Object object = args[0]; + Py::String name = args[1]; + std::string propName = static_cast(name); + + if (PyObject_TypeCheck(object.ptr(), &App::DocumentObjectPy::Type)) { + auto py = static_cast(object.ptr()); + auto obj = py->getDocumentObjectPtr(); + + if (widget) { + widget->setPlacementAndBindObject(obj, propName); + } + } + + return Py::None(); +} + +Py::Object TaskPlacementPy::setIgnoreTransactions(const Py::Tuple& args) +{ + if (widget) { + widget->setIgnoreTransactions(Py::Boolean(args[0])); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::showDefaultButtons(const Py::Tuple& args) +{ + if (widget) { + widget->showDefaultButtons(Py::Boolean(args[0])); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::accept(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + + bool res = true; + if (widget) { + widget->accept(); + res = widget->result() == QDialog::Accepted; + } + return Py::Boolean(res); +} + +Py::Object TaskPlacementPy::reject(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + + bool res = true; + if (widget) { + widget->reject(); + res = widget->result() == QDialog::Rejected; + } + return Py::Boolean(res); +} + +Py::Object TaskPlacementPy::clicked(const Py::Tuple& args) +{ + int index {}; + if (!PyArg_ParseTuple(args.ptr(), "i", &index)) { + throw Py::Exception(); + } + + if (widget && index == QDialogButtonBox::Apply) { + widget->onApplyButtonClicked(); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::open(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + + if (widget) { + widget->open(); + } + return Py::None(); +} + +Py::Object TaskPlacementPy::isAllowedAlterDocument(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + return Py::Boolean(true); +} + +Py::Object TaskPlacementPy::isAllowedAlterView(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + return Py::Boolean(true); +} + +Py::Object TaskPlacementPy::isAllowedAlterSelection(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + return Py::Boolean(true); +} + +Py::Object TaskPlacementPy::getStandardButtons(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) { + throw Py::Exception(); + } + auto buttons = QDialogButtonBox::Ok| + QDialogButtonBox::Cancel| + QDialogButtonBox::Apply; + return Py::Long(static_cast(buttons)); +} + #include "moc_Placement.cpp" diff --git a/src/Gui/Placement.h b/src/Gui/Placement.h index 4306dc61d1..bf6af51282 100644 --- a/src/Gui/Placement.h +++ b/src/Gui/Placement.h @@ -51,33 +51,62 @@ public: PlacementHandler(); void openTransactionIfNeeded(); void setPropertyName(const std::string&); + void setIgnoreTransactions(bool value); + void setSelection(const std::vector&); + void reselectObjects(); + const App::DocumentObject* getFirstOfSelection() const; const std::string& getPropertyName() const; void appendDocument(const std::string&); void activatedDocument(const std::string&); void revertTransformation(); + void setRefPlacement(const Base::Placement& plm); + const Base::Placement& getRefPlacement() const; void applyPlacement(const Base::Placement& p, bool incremental); void applyPlacement(const QString& p, bool incremental); + Base::Vector3d computeCenterOfMass() const; + void setCenterOfMass(const Base::Vector3d& pnt); + Base::Vector3d getCenterOfMass() const; + std::tuple> getSelectedPoints() const; private: - std::vector getObjects(Gui::Document*) const; - std::vector getSelectedObjects(Gui::Document*) const; + std::vector getObjects(const Gui::Document*) const; + std::vector getSelectedObjects(const Gui::Document*) const; void revertTransformationOfViewProviders(Gui::Document*); void tryRecompute(Gui::Document*); - void applyPlacement(Gui::Document*, App::DocumentObject*, const Base::Placement& p, bool incremental); - void applyPlacement(App::DocumentObject*, const QString& p, bool incremental); - QString getIncrementalPlacement(App::DocumentObject*, const QString&) const; - QString getSimplePlacement(App::DocumentObject*, const QString&) const; + void applyPlacement(const Gui::Document*, const App::DocumentObject*, + const Base::Placement& p, bool incremental); + void applyPlacement(const App::DocumentObject*, const QString& p, bool incremental); + QString getIncrementalPlacement(const App::DocumentObject*, const QString&) const; + QString getSimplePlacement(const App::DocumentObject*, const QString&) const; + void setupDocument(); + void slotActiveDocument(const Gui::Document&); + void openCommandIfActive(Gui::Document*); + void commitCommandIfActive(Gui::Document*); + void abortCommandIfActive(Gui::Document*); private Q_SLOTS: void openTransaction(); private: + using Connection = boost::signals2::scoped_connection; std::string propertyName; // the name of the placement property std::set documents; /** If false apply the placement directly to the transform nodes, * otherwise change the placement property. */ bool changeProperty; + /** If true do not open or commit transactions. In this case it's expected + * that it's done by the calling instance. + */ + bool ignoreTransaction; + Connection connectAct; + /** + * store these so we can reselect original object + * after user selects points and clicks Selected point(s) + */ + std::vector selectionObjects; + Base::Placement ref; + Base::Vector3d cntOfMass; }; class GuiExport Placement : public QDialog @@ -94,6 +123,9 @@ public: void setPropertyName(const std::string&); void setSelection(const std::vector&); void bindObject(); + void setPlacementAndBindObject(const App::DocumentObject* obj, + const std::string& propertyName); + void setIgnoreTransactions(bool value); Base::Vector3d getDirection() const; void setPlacement(const Base::Placement&); Base::Placement getPlacement() const; @@ -119,8 +151,8 @@ private: void setupConnections(); void setupUnits(); void setupSignalMapper(); - void setupDocument(); void setupRotationMethod(); + void bindProperty(const App::DocumentObject* obj, const std::string& propertyName); bool onApply(); void setPlacementData(const Base::Placement&); @@ -129,11 +161,9 @@ private: Base::Vector3d getPositionData() const; Base::Vector3d getAnglesData() const; Base::Vector3d getCenterData() const; - Base::Vector3d getCenterOfMass() const; QString getPlacementString() const; QString getPlacementFromEulerAngles() const; QString getPlacementFromAxisWithAngle() const; - void slotActiveDocument(const Gui::Document&); QWidget* getInvalidInput() const; void showErrorMessage(); @@ -141,18 +171,9 @@ Q_SIGNALS: void placementChanged(const QVariant &, bool, bool); private: - using Connection = boost::signals2::connection; Ui_Placement* ui; QSignalMapper* signalMapper; - Connection connectAct; PlacementHandler handler; - Base::Placement ref; - Base::Vector3d cntOfMass; - /** - * store these so we can reselect original object - * after user selects points and clicks Selected point(s) - */ - std::vector selectionObjects; }; class GuiExport DockablePlacement : public Placement @@ -179,7 +200,10 @@ public: void setPropertyName(const QString&); void setPlacement(const Base::Placement&); void setSelection(const std::vector&); + void clearSelection(); void bindObject(); + void setPlacementAndBindObject(const App::DocumentObject* obj, + const std::string& propertyName); bool accept() override; bool reject() override; void clicked(int id) override; @@ -203,6 +227,43 @@ private: Placement* widget; }; +class TaskPlacementPy: public Py::PythonExtension +{ +public: + using BaseType = Py::PythonExtension; + static void init_type(); + + TaskPlacementPy(); + ~TaskPlacementPy() override; + + Py::Object repr() override; + Py::Object getattr(const char* name) override; + int setattr(const char* name, const Py::Object&) override; + + Py::Object setPropertyName(const Py::Tuple&); + Py::Object setPlacement(const Py::Tuple&); + Py::Object setSelection(const Py::Tuple&); + Py::Object bindObject(const Py::Tuple&); + Py::Object setPlacementAndBindObject(const Py::Tuple&); + Py::Object setIgnoreTransactions(const Py::Tuple&); + + Py::Object showDefaultButtons(const Py::Tuple&); + Py::Object accept(const Py::Tuple&); + Py::Object reject(const Py::Tuple&); + Py::Object clicked(const Py::Tuple&); + Py::Object open(const Py::Tuple&); + Py::Object isAllowedAlterDocument(const Py::Tuple&); + Py::Object isAllowedAlterView(const Py::Tuple&); + Py::Object isAllowedAlterSelection(const Py::Tuple&); + Py::Object getStandardButtons(const Py::Tuple&); + +private: + static PyObject* PyMake(struct _typeobject*, PyObject*, PyObject*); + +private: + QPointer widget; +}; + } // namespace Dialog } // namespace Gui diff --git a/src/Gui/Placement.ui b/src/Gui/Placement.ui index 72a6854ec4..b50ba72e02 100644 --- a/src/Gui/Placement.ui +++ b/src/Gui/Placement.ui @@ -6,8 +6,8 @@ 0 0 - 277 - 394 + 456 + 492 @@ -233,7 +233,7 @@ - + 1 @@ -415,7 +415,7 @@ - + @@ -487,23 +487,9 @@ - - - OK - - - - - - - Close - - - - - - - Apply + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -525,44 +511,9 @@ yAxis zAxis applyIncrementalPlacement - oKButton - applyButton - closeButton - - oKButton - clicked() - Gui::Dialog::Placement - accept() - - - 164 - 252 - - - 8 - 28 - - - - - closeButton - clicked() - Gui::Dialog::Placement - reject() - - - 354 - 252 - - - 291 - 179 - - - rotationInput activated(int) @@ -579,5 +530,37 @@ + + buttonBox + accepted() + Gui::Dialog::Placement + accept() + + + 310 + 469 + + + 227 + 245 + + + + + buttonBox + rejected() + Gui::Dialog::Placement + reject() + + + 310 + 469 + + + 227 + 245 + + + diff --git a/src/Gui/SelectionObject.cpp b/src/Gui/SelectionObject.cpp index f705397b18..c8450a24cf 100644 --- a/src/Gui/SelectionObject.cpp +++ b/src/Gui/SelectionObject.cpp @@ -53,7 +53,7 @@ SelectionObject::SelectionObject(const Gui::SelectionChanges& msg) } } -SelectionObject::SelectionObject(App::DocumentObject* obj) +SelectionObject::SelectionObject(const App::DocumentObject* obj) { FeatName = obj->getNameInDocument(); DocName = obj->getDocument()->getName(); diff --git a/src/Gui/SelectionObject.h b/src/Gui/SelectionObject.h index 44e8e3b6bb..cc0d618246 100644 --- a/src/Gui/SelectionObject.h +++ b/src/Gui/SelectionObject.h @@ -50,7 +50,7 @@ public: /*! Constructs a SelectionObject from the SelectionChanges structure. */ explicit SelectionObject(const SelectionChanges& msg); - explicit SelectionObject(App::DocumentObject*); + explicit SelectionObject(const App::DocumentObject*); ~SelectionObject() override; /** * The default implementation returns an instance of @ref SelectionObjectPy. diff --git a/src/Gui/Transform.cpp b/src/Gui/Transform.cpp index 2937e46784..7d3f661321 100644 --- a/src/Gui/Transform.cpp +++ b/src/Gui/Transform.cpp @@ -279,13 +279,12 @@ Transform::Transform(QWidget* parent, Qt::WindowFlags fl) { ui = new Ui_Placement(); ui->setupUi(this); - connect(ui->applyButton, &QPushButton::clicked, + QPushButton* applyButton = ui->buttonBox->button(QDialogButtonBox::Apply); + connect(applyButton, &QPushButton::clicked, this, &Transform::onApplyButtonClicked); ui->resetButton->hide(); ui->applyIncrementalPlacement->hide(); - - ui->closeButton->setText(tr("Cancel")); this->setWindowTitle(tr("Transform")); // create a signal mapper in order to have one slot to perform the change @@ -332,9 +331,7 @@ void Transform::setTransformStrategy(TransformStrategy* ts) void Transform::showStandardButtons(bool b) { - ui->closeButton->setVisible(b); - ui->oKButton->setVisible(b); - ui->applyButton->setVisible(b); + ui->buttonBox->setVisible(b); } void Transform::onTransformChanged(int) @@ -413,7 +410,6 @@ void Transform::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); - ui->closeButton->setText(tr("Cancel")); this->setWindowTitle(tr("Transform")); } else {