Merge pull request #16220 from wwmayer/placementpy

Core: Expose Placement dialog to Python
This commit is contained in:
Yorik van Havre
2024-09-09 18:24:31 +02:00
committed by GitHub
8 changed files with 650 additions and 207 deletions

View File

@@ -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()
{

View File

@@ -1562,6 +1562,7 @@ void StdCmdPlacement::activated(int iMsg)
plm->setPropertyName(QLatin1String("Placement"));
plm->setSelection(selection);
plm->bindObject();
plm->clearSelection();
}
}
Gui::Control().showDialog(plm);

View File

@@ -32,13 +32,16 @@
#include <App/ComplexGeoData.h>
#include <App/Document.h>
#include <App/DocumentObjectPy.h>
#include <App/GeoFeature.h>
#include <Base/Console.h>
#include <Base/PlacementPy.h>
#include <Base/Tools.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/DockWindowManager.h>
#include <Gui/Document.h>
#include <Gui/PythonWrapper.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/Window.h>
@@ -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<std::string,App::Property*> 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<Gui::SelectionObject>& 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<App::DocumentObject*> PlacementHandler::getObjects(Gui::Document* document) const
std::vector<const App::DocumentObject*> PlacementHandler::getObjects(const Gui::Document* document) const
{
return document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId());
auto objs = document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId());
std::vector<const App::DocumentObject*> list;
list.insert(list.begin(), objs.begin(), objs.end());
return list;
}
std::vector<App::DocumentObject*> PlacementHandler::getSelectedObjects(Gui::Document* document) const
std::vector<const App::DocumentObject*> PlacementHandler::getSelectedObjects(const Gui::Document* document) const
{
return Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), document->getDocument()->getName());
App::Document* doc = document->getDocument();
std::vector<const App::DocumentObject*> 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<App::DocumentObject*> obj = getObjects(document);
std::vector<const App::DocumentObject*> 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<App::DocumentObject*> sel = getSelectedObjects(document);
std::vector<const App::DocumentObject*> 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<App::DocumentObject*> sel = getSelectedObjects(document);
std::vector<const App::DocumentObject*> 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<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
(App::GeoFeature::getClassTypeId());
if (!sel.empty()) {
for (auto it : sel) {
const App::PropertyComplexGeoData* propgeo = static_cast<App::GeoFeature*>(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<Base::Vector3d, std::vector<Base::Vector3d>> PlacementHandler::getSelectedPoints() const
{
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
std::vector<Base::Vector3d> 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<Base::Vector3d> 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<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
@@ -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<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
std::vector<Base::Vector3d> 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<Base::Vector3d> 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<Gui::SelectionObject>& 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<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
(App::GeoFeature::getClassTypeId());
if (!sel.empty()) {
for (auto it : sel) {
const App::PropertyComplexGeoData* propgeo = static_cast<App::GeoFeature*>(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<Gui::SelectionObject>& 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<Base::PlacementPy*>(plm)->getPlacementPtr());
}
return Py::None();
}
Py::Object TaskPlacementPy::setSelection(const Py::Tuple& args)
{
std::vector<Gui::SelectionObject> sel;
Py::Sequence list(args[0]);
for (const auto& obj : list) {
if (PyObject_TypeCheck(obj.ptr(), &App::DocumentObjectPy::Type)) {
auto doc = static_cast<App::DocumentObjectPy*>(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<std::string>(name);
if (PyObject_TypeCheck(object.ptr(), &App::DocumentObjectPy::Type)) {
auto py = static_cast<App::DocumentObjectPy*>(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<int>(buttons));
}
#include "moc_Placement.cpp"

View File

@@ -51,33 +51,62 @@ public:
PlacementHandler();
void openTransactionIfNeeded();
void setPropertyName(const std::string&);
void setIgnoreTransactions(bool value);
void setSelection(const std::vector<SelectionObject>&);
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<Base::Vector3d, std::vector<Base::Vector3d>> getSelectedPoints() const;
private:
std::vector<App::DocumentObject*> getObjects(Gui::Document*) const;
std::vector<App::DocumentObject*> getSelectedObjects(Gui::Document*) const;
std::vector<const App::DocumentObject*> getObjects(const Gui::Document*) const;
std::vector<const App::DocumentObject*> 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<std::string> 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<SelectionObject> 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<SelectionObject>&);
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<SelectionObject> 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<SelectionObject>&);
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<TaskPlacementPy>
{
public:
using BaseType = Py::PythonExtension<TaskPlacementPy>;
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<Placement> widget;
};
} // namespace Dialog
} // namespace Gui

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>277</width>
<height>394</height>
<width>456</width>
<height>492</height>
</rect>
</property>
<property name="windowTitle">
@@ -233,7 +233,7 @@
</item>
</widget>
</item>
<item row="1" column="0">
<item row="1" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
@@ -415,7 +415,7 @@
<widget class="QWidget" name="page_2"/>
</widget>
</item>
</layout>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
@@ -487,23 +487,9 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="oKButton">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applyButton">
<property name="text">
<string>Apply</string>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@@ -525,44 +511,9 @@
<tabstop>yAxis</tabstop>
<tabstop>zAxis</tabstop>
<tabstop>applyIncrementalPlacement</tabstop>
<tabstop>oKButton</tabstop>
<tabstop>applyButton</tabstop>
<tabstop>closeButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>oKButton</sender>
<signal>clicked()</signal>
<receiver>Gui::Dialog::Placement</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>164</x>
<y>252</y>
</hint>
<hint type="destinationlabel">
<x>8</x>
<y>28</y>
</hint>
</hints>
</connection>
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
<receiver>Gui::Dialog::Placement</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>354</x>
<y>252</y>
</hint>
<hint type="destinationlabel">
<x>291</x>
<y>179</y>
</hint>
</hints>
</connection>
<connection>
<sender>rotationInput</sender>
<signal>activated(int)</signal>
@@ -579,5 +530,37 @@
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Gui::Dialog::Placement</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>310</x>
<y>469</y>
</hint>
<hint type="destinationlabel">
<x>227</x>
<y>245</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Gui::Dialog::Placement</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>310</x>
<y>469</y>
</hint>
<hint type="destinationlabel">
<x>227</x>
<y>245</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -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();

View File

@@ -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.

View File

@@ -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 {