1
/***************************************************************************
2
* Copyright (c) 2008 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
30
#include <QSignalMapper>
33
#include <App/ComplexGeoData.h>
34
#include <App/Document.h>
35
#include <App/DocumentObjectPy.h>
36
#include <App/GeoFeature.h>
37
#include <Base/Console.h>
38
#include <Base/PlacementPy.h>
39
#include <Base/Tools.h>
40
#include <Gui/Application.h>
41
#include <Gui/Command.h>
42
#include <Gui/DockWindowManager.h>
43
#include <Gui/Document.h>
44
#include <Gui/PythonWrapper.h>
45
#include <Gui/Selection.h>
46
#include <Gui/ViewProvider.h>
47
#include <Gui/Window.h>
50
#include "ui_Placement.h"
53
using namespace Gui::Dialog;
54
namespace sp = std::placeholders;
56
namespace Gui { namespace Dialog {
60
explicit find_placement(const std::string& name) : propertyname(name)
63
bool operator () (const std::pair<std::string, App::Property*>& elem) const
65
if (elem.first == propertyname) {
66
// flag set that property is read-only or hidden
67
if (elem.second->testStatus(App::Property::ReadOnly) || elem.second->testStatus(App::Property::Hidden))
69
App::PropertyContainer* parent = elem.second->getContainer();
71
// flag set that property is read-only or hidden
72
if (parent->isReadOnly(elem.second) ||
73
parent->isHidden(elem.second))
76
return elem.second->isDerivedFrom
77
(Base::Type::fromName("App::PropertyPlacement"));
83
static App::PropertyPlacement* getProperty(const App::DocumentObject* obj,
84
const std::string& propertyName)
86
std::map<std::string,App::Property*> props;
87
obj->getPropertyMap(props);
89
// search for the placement property
90
std::map<std::string,App::Property*>::iterator jt;
91
jt = std::find_if(props.begin(), props.end(), find_placement(propertyName));
92
if (jt != props.end()) {
93
return dynamic_cast<App::PropertyPlacement*>(jt->second);
99
std::string propertyname;
105
PlacementHandler::PlacementHandler()
106
: propertyName{"Placement"}
107
, changeProperty{false}
108
, ignoreTransaction{false}
113
void PlacementHandler::openTransactionIfNeeded()
115
if (changeProperty) {
120
void PlacementHandler::setPropertyName(const std::string& name)
123
// Only with the Placement property it's possible to directly change the Inventor representation.
124
// For other placement properties with a different name the standard property handling must be used.
125
changeProperty = (propertyName != "Placement");
128
void PlacementHandler::setIgnoreTransactions(bool value)
130
ignoreTransaction = value;
133
void PlacementHandler::setSelection(const std::vector<Gui::SelectionObject>& selection)
135
selectionObjects = selection;
138
void PlacementHandler::reselectObjects()
140
//we have to clear selection and reselect original object(s)
141
//else later on the rotation is applied twice because there will
142
//be 2 (vertex) objects in the selection, and even if both are subobjects
143
//of the same object the rotation still gets applied twice
144
Gui::Selection().clearSelection();
145
//reselect original object that was selected when placement dlg first opened
146
for (const auto& it : selectionObjects) {
147
Gui::Selection().addSelection(it);
151
const App::DocumentObject* PlacementHandler::getFirstOfSelection() const
153
if (!selectionObjects.empty()) {
154
return selectionObjects.front().getObject();
160
const std::string& PlacementHandler::getPropertyName() const
165
void PlacementHandler::appendDocument(const std::string& name)
167
documents.insert(name);
170
void PlacementHandler::activatedDocument(const std::string& name)
172
appendDocument(name);
174
if (changeProperty) {
175
QMetaObject::invokeMethod(this, "openTransaction", Qt::QueuedConnection);
179
void PlacementHandler::openTransaction()
181
App::Document* activeDoc = App::GetApplication().getActiveDocument();
183
activeDoc->openTransaction("Placement");
186
void PlacementHandler::revertTransformation()
188
for (const auto & it : documents) {
189
Gui::Document* document = Application::Instance->getDocument(it.c_str());
191
if (!changeProperty) {
192
revertTransformationOfViewProviders(document);
195
abortCommandIfActive(document);
201
std::vector<const App::DocumentObject*> PlacementHandler::getObjects(const Gui::Document* document) const
203
auto objs = document->getDocument()->getObjectsOfType(App::DocumentObject::getClassTypeId());
204
std::vector<const App::DocumentObject*> list;
205
list.insert(list.begin(), objs.begin(), objs.end());
209
std::vector<const App::DocumentObject*> PlacementHandler::getSelectedObjects(const Gui::Document* document) const
211
App::Document* doc = document->getDocument();
212
std::vector<const App::DocumentObject*> list;
213
list.reserve(selectionObjects.size());
214
for (const auto& it : selectionObjects) {
215
const App::DocumentObject* obj = it.getObject();
216
if (obj && obj->getDocument() == doc) {
222
auto objs = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId(), doc->getName());
223
list.insert(list.begin(), objs.begin(), objs.end());
229
void PlacementHandler::revertTransformationOfViewProviders(Gui::Document* document)
231
std::vector<const App::DocumentObject*> obj = getObjects(document);
232
for (const auto & it : obj) {
233
auto property = find_placement::getProperty(it, this->propertyName);
235
Base::Placement cur = property->getValue();
236
Gui::ViewProvider* vp = document->getViewProvider(it);
238
vp->setTransformation(cur.toMatrix());
244
void PlacementHandler::setRefPlacement(const Base::Placement& plm)
249
const Base::Placement& PlacementHandler::getRefPlacement() const
254
void PlacementHandler::applyPlacement(const Base::Placement& p, bool incremental)
256
Gui::Document* document = Application::Instance->activeDocument();
260
std::vector<const App::DocumentObject*> sel = getSelectedObjects(document);
262
for (const auto & it : sel) {
263
applyPlacement(document, it, p, incremental);
267
Base::Console().Warning("No object selected.\n");
271
void PlacementHandler::applyPlacement(const Gui::Document* document, const App::DocumentObject* obj,
272
const Base::Placement& p, bool incremental)
274
auto property = find_placement::getProperty(obj, this->propertyName);
276
Base::Placement cur = property->getValue();
282
if (!changeProperty) {
283
Gui::ViewProvider* vp = document->getViewProvider(obj);
285
vp->setTransformation(cur.toMatrix());
289
property->setValue(cur);
294
void PlacementHandler::applyPlacement(const QString& data, bool incremental)
296
Gui::Document* document = Application::Instance->activeDocument();
300
// When directly changing the property we now only have to commit the transaction,
301
// do a recompute and open a new transaction
302
if (changeProperty) {
303
commitCommandIfActive(document);
304
tryRecompute(document);
305
openCommandIfActive(document);
308
std::vector<const App::DocumentObject*> sel = getSelectedObjects(document);
310
openCommandIfActive(document);
311
for (const auto & it : sel) {
312
applyPlacement(it, data, incremental);
314
commitCommandIfActive(document);
315
tryRecompute(document);
318
Base::Console().Warning("No object selected.\n");
323
void PlacementHandler::applyPlacement(const App::DocumentObject* obj, const QString& data, bool incremental)
325
auto property = find_placement::getProperty(obj, this->propertyName);
329
cmd = getIncrementalPlacement(obj, data);
332
cmd = getSimplePlacement(obj, data);
335
Gui::Command::runCommand(Gui::Command::App, cmd.toLatin1());
339
QString PlacementHandler::getIncrementalPlacement(const App::DocumentObject* obj, const QString& data) const
341
return QString::fromLatin1(
342
R"(App.getDocument("%1").%2.%3=%4.multiply(App.getDocument("%1").%2.%3))")
343
.arg(QString::fromLatin1(obj->getDocument()->getName()),
344
QString::fromLatin1(obj->getNameInDocument()),
345
QString::fromLatin1(this->propertyName.c_str()),
349
QString PlacementHandler::getSimplePlacement(const App::DocumentObject* obj, const QString& data) const
351
return QString::fromLatin1(
352
"App.getDocument(\"%1\").%2.%3=%4")
353
.arg(QString::fromLatin1(obj->getDocument()->getName()),
354
QString::fromLatin1(obj->getNameInDocument()),
355
QString::fromLatin1(this->propertyName.c_str()),
359
Base::Vector3d PlacementHandler::computeCenterOfMass() const
361
Base::Vector3d centerOfMass;
362
std::vector<App::DocumentObject*> sel = Gui::Selection().getObjectsOfType
363
(App::GeoFeature::getClassTypeId());
365
for (auto it : sel) {
366
const App::PropertyComplexGeoData* propgeo = static_cast<App::GeoFeature*>(it)->getPropertyOfGeometry();
367
const Data::ComplexGeoData* geodata = propgeo ? propgeo->getComplexData() : nullptr;
368
if (geodata && geodata->getCenterOfGravity(centerOfMass)) {
376
void PlacementHandler::setCenterOfMass(const Base::Vector3d& pnt)
381
Base::Vector3d PlacementHandler::getCenterOfMass() const
386
std::tuple<Base::Vector3d, std::vector<Base::Vector3d>> PlacementHandler::getSelectedPoints() const
388
std::vector<Gui::SelectionObject> selection = Gui::Selection().getSelectionEx();
389
std::vector<Base::Vector3d> picked;
390
//combine all pickedpoints into single vector
391
//even if points are from separate objects
392
Base::Vector3d firstSelected; //first selected will be central point when 3 points picked
393
for (auto it = selection.begin(); it != selection.end(); ++it) {
394
std::vector<Base::Vector3d> points = it->getPickedPoints();
395
if (it == selection.begin() && !points.empty()) {
396
firstSelected = points[0];
399
picked.insert(picked.begin(), points.begin(), points.end());
402
return std::make_tuple(firstSelected, picked);
405
void PlacementHandler::tryRecompute(Gui::Document* document)
408
document->getDocument()->recompute();
414
void PlacementHandler::setupDocument()
417
connectAct = Application::Instance->signalActiveDocument.connect
418
(std::bind(&PlacementHandler::slotActiveDocument, this, sp::_1));
420
App::Document* activeDoc = App::GetApplication().getActiveDocument();
422
appendDocument(activeDoc->getName());
426
void PlacementHandler::slotActiveDocument(const Gui::Document& doc)
428
activatedDocument(doc.getDocument()->getName());
431
void PlacementHandler::openCommandIfActive(Gui::Document* doc)
433
if (!ignoreTransaction) {
434
doc->openCommand(QT_TRANSLATE_NOOP("Command", "Placement"));
438
void PlacementHandler::commitCommandIfActive(Gui::Document* doc)
440
if (!ignoreTransaction) {
441
doc->commitCommand();
445
void PlacementHandler::abortCommandIfActive(Gui::Document* doc)
447
if (!ignoreTransaction) {
452
// ----------------------------------------------------------------------------
454
/* TRANSLATOR Gui::Dialog::Placement */
456
Placement::Placement(QWidget* parent, Qt::WindowFlags fl)
457
: QDialog(parent, fl)
464
setupRotationMethod();
467
Placement::~Placement()
472
void Placement::setupUi()
474
ui = new Ui_Placement();
476
ui->gridLayout->removeItem(ui->vSpacer);
479
void Placement::setupConnections()
481
QPushButton* applyButton = ui->buttonBox->button(QDialogButtonBox::Apply);
482
connect(applyButton, &QPushButton::clicked,
483
this, &Placement::onApplyButtonClicked);
484
connect(ui->applyIncrementalPlacement, &QCheckBox::toggled,
485
this, &Placement::onApplyIncrementalPlacementToggled);
486
connect(ui->resetButton, &QPushButton::clicked,
487
this, &Placement::onResetButtonClicked);
488
connect(ui->centerOfMass, &QCheckBox::toggled,
489
this, &Placement::onCenterOfMassToggled);
490
connect(ui->selectedVertex, &QPushButton::clicked,
491
this, &Placement::onSelectedVertexClicked);
492
connect(ui->applyAxial, &QPushButton::clicked,
493
this, &Placement::onApplyAxialClicked);
496
void Placement::setupUnits()
498
ui->xPos->setUnit(Base::Unit::Length);
499
ui->yPos->setUnit(Base::Unit::Length);
500
ui->zPos->setUnit(Base::Unit::Length);
501
ui->axialPos->setUnit(Base::Unit::Length);
502
ui->xCnt->setValue(Base::Quantity(0, Base::Unit::Length));
503
ui->yCnt->setValue(Base::Quantity(0, Base::Unit::Length));
504
ui->zCnt->setValue(Base::Quantity(0, Base::Unit::Length));
505
ui->angle->setUnit(Base::Unit::Angle);
506
ui->yawAngle->setMaximum(180.0F);
507
ui->yawAngle->setMinimum(-180.0F);
508
ui->yawAngle->setUnit(Base::Unit::Angle);
509
ui->yawAngle->checkRangeInExpression(true);
510
ui->pitchAngle->setMaximum(90.0F);
511
ui->pitchAngle->setMinimum(-90.0F);
512
ui->pitchAngle->setUnit(Base::Unit::Angle);
513
ui->pitchAngle->checkRangeInExpression(true);
514
ui->rollAngle->setMaximum(180.0F);
515
ui->rollAngle->setMinimum(-180.0F);
516
ui->rollAngle->setUnit(Base::Unit::Angle);
517
ui->rollAngle->checkRangeInExpression(true);
520
void Placement::setupSignalMapper()
522
// create a signal mapper in order to have one slot to perform the change
523
signalMapper = new QSignalMapper(this);
524
signalMapper->setMapping(this, 0);
527
QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
528
for (const auto & it : sb) {
529
connect(it, qOverload<double>(&QuantitySpinBox::valueChanged), signalMapper, qOverload<>(&QSignalMapper::map));
530
signalMapper->setMapping(it, id++);
533
#if QT_VERSION < QT_VERSION_CHECK(5,15,0)
534
connect(signalMapper, qOverload<int>(&QSignalMapper::mapped),
535
this, &Placement::onPlacementChanged);
537
connect(signalMapper, &QSignalMapper::mappedInt,
538
this, &Placement::onPlacementChanged);
542
void Placement::setupRotationMethod()
544
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement");
545
long index = hGrp->GetInt("RotationMethod");
546
ui->rotationInput->setCurrentIndex(index);
547
ui->stackedWidget->setCurrentIndex(index);
550
void Placement::showDefaultButtons(bool ok)
552
ui->buttonBox->setVisible(ok);
553
ui->buttonBoxLayout->invalidate();
555
ui->buttonBoxLayout->insertSpacerItem(0, ui->buttonBoxSpacer);
558
ui->buttonBoxLayout->removeItem(ui->buttonBoxSpacer);
562
void Placement::open()
564
handler.openTransactionIfNeeded();
567
QWidget* Placement::getInvalidInput() const
569
QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
570
for (const auto & it : sb) {
571
if (!it->hasValidInput())
577
void Placement::onPlacementChanged(int)
579
// If there are listeners to the 'placementChanged' signal we rely
580
// on that the listener updates any placement. If no listeners
581
// are connected the placement is applied to all selected objects
583
bool incr = ui->applyIncrementalPlacement->isChecked();
584
Base::Placement plm = this->getPlacement();
585
handler.applyPlacement(plm, incr);
587
QVariant data = QVariant::fromValue<Base::Placement>(plm);
588
Q_EMIT placementChanged(data, incr, false);
591
void Placement::onCenterOfMassToggled(bool on)
593
ui->xCnt->setDisabled(on);
594
ui->yCnt->setDisabled(on);
595
ui->zCnt->setDisabled(on);
598
Base::Vector3d pnt = handler.computeCenterOfMass();
599
handler.setCenterOfMass(pnt);
600
ui->xCnt->setValue(pnt.x);
601
ui->yCnt->setValue(pnt.y);
602
ui->zCnt->setValue(pnt.z);
606
void Placement::onSelectedVertexClicked()
608
ui->centerOfMass->setChecked(false);
610
Base::Vector3d center;
611
bool success = false;
612
auto [firstSelected, picked] = handler.getSelectedPoints();
613
handler.reselectObjects();
615
if (picked.size() == 1) {
619
else if (picked.size() == 2) {
620
//average the coords to get center of rotation
621
center = (picked[0] + picked[1]) / 2.0;
623
//setup a customized axis since the user selected 2 points
624
//keep any existing angle, but setup our own axis
625
Base::Placement plm = getPlacement();
626
Base::Rotation rot = plm.getRotation();
629
rot.getRawValue(tmp, angle);
631
if (firstSelected == picked[0]){
632
axis = Base::Vector3d(picked[1] - picked[0]);
635
axis = Base::Vector3d(picked[0] - picked[1]);
637
double length = axis.Length();
638
Base::Console().Message("Distance: %.8f\n",length);
639
if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click
641
QApplication::clipboard()->setText(loc.toString(length,'g',8));
644
Base::Console().Message("(Shift + click Selected points button to copy distance to clipboard)\n");
647
rot.setValue(axis, angle);
648
plm.setRotation(rot);
649
setPlacementData(plm); //creates custom axis, if needed
650
ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler
651
ui->stackedWidget->setCurrentIndex(0);
654
else if (picked.size() == 3){
655
/* User selected 3 points, so we find the plane defined by those
656
* and use the normal vector that contains the first point picked
657
* as the axis of rotation.
660
Base::Vector3d a, b(firstSelected), c; //b is on central axis
661
if (picked[0] == firstSelected){
665
else if (picked[1] == firstSelected){
669
else if (picked[2] == firstSelected){
674
Base::Vector3d norm((a-b).Cross(c-b));
678
//setup a customized axis normal to the plane
679
//keep any existing angle, but setup our own axis
680
Base::Placement plm = getPlacement();
681
Base::Rotation rot = plm.getRotation();
684
rot.getRawValue(tmp, angle);
685
double length = (a-c).Length();
686
Base::Console().Message("Distance: %.8f\n",length);
687
Base::Vector3d v1(a-b);
688
Base::Vector3d v2(c-b);
691
double targetAngle = Base::toDegrees(v2.GetAngle(v1));
692
Base::Console().Message("Target angle: %.8f degrees, complementary: %.8f degrees\n",targetAngle, 90.0-targetAngle);
693
if (QApplication::keyboardModifiers() == Qt::ShiftModifier){ //copy to clipboard on Shift+click
695
QApplication::clipboard()->setText(loc.toString(targetAngle,'g',8));
696
Base::Console().Message("(Angle copied to clipboard, but you might need to use a negative (-) angle sometimes.)\n");
699
Base::Console().Message("(Shift + click Selected points button to copy angle to clipboard)\n");
701
rot.setValue(norm, angle);
702
plm.setRotation(rot);
703
setPlacementData(plm); //creates custom axis, if needed
704
ui->rotationInput->setCurrentIndex(0); //use rotation with axis instead of euler
705
ui->stackedWidget->setCurrentIndex(0);
709
handler.setCenterOfMass(center);
710
ui->xCnt->setValue(center.x);
711
ui->yCnt->setValue(center.y);
712
ui->zCnt->setValue(center.z);
715
Base::Console().Warning("Placement selection error. Select either 1 or 2 points.\n");
717
msgBox.setText(tr("Please select 1, 2, or 3 points before clicking this button. A point may be on a vertex, \
718
face, or edge. If on a face or edge the point used will be the point at the mouse position along \
719
face or edge. If 1 point is selected it will be used as the center of rotation. If 2 points are \
720
selected the midpoint between them will be the center of rotation and a new custom axis will be \
721
created, if needed. If 3 points are selected the first point becomes the center of rotation and \
722
lies on the vector that is normal to the plane defined by the 3 points. Some distance and angle \
723
information is provided in the report view, which can be useful when aligning objects. For your \
724
convenience when Shift + click is used the appropriate distance or angle is copied to the clipboard."));
729
void Placement::onApplyAxialClicked()
731
signalMapper->blockSignals(true);
732
double axPos = ui->axialPos->value().getValue();
733
Base::Placement p = getPlacementData();
736
p.getRotation().getValue(axis, angle);
737
Base::Vector3d curPos (p.getPosition());
738
Base::Vector3d newPos;
739
Qt::KeyboardModifiers km = QApplication::keyboardModifiers();
740
if (km == Qt::ShiftModifier){ //go opposite direction on Shift+click
741
newPos = Base::Vector3d(curPos.x-(axis.x*axPos),curPos.y-(axis.y*axPos),curPos.z-(axis.z*axPos));
744
newPos = Base::Vector3d(curPos.x+(axis.x*axPos),curPos.y+(axis.y*axPos),curPos.z+(axis.z*axPos));
746
ui->xPos->setValue(Base::Quantity(newPos.x,Base::Unit::Length));
747
ui->yPos->setValue(Base::Quantity(newPos.y,Base::Unit::Length));
748
ui->zPos->setValue(Base::Quantity(newPos.z,Base::Unit::Length));
749
signalMapper->blockSignals(false);
750
onPlacementChanged(0);
753
void Placement::onApplyIncrementalPlacementToggled(bool on)
756
handler.setRefPlacement(getPlacementData());
757
onResetButtonClicked();
760
Base::Placement p = getPlacementData();
761
p = p * handler.getRefPlacement();
763
onPlacementChanged(0);
767
void Placement::keyPressEvent(QKeyEvent* ke)
769
// The placement dialog is embedded into a task panel
770
// which is a parent widget and will handle the event
774
void Placement::reject()
777
handler.applyPlacement(plm, true);
779
QVariant data = QVariant::fromValue<Base::Placement>(plm);
780
Q_EMIT placementChanged(data, true, false);
782
handler.revertTransformation();
784
// One of the quantity spin boxes still can emit a signal when it has the focus
785
// but its content is not fully updated.
786
// In order to override again the placement the signalMapper is blocked
787
// See related forum thread:
788
// https://forum.freecad.org/viewtopic.php?f=3&t=44341#p378659
789
QSignalBlocker block(signalMapper);
793
void Placement::accept()
796
handler.revertTransformation();
801
void Placement::onApplyButtonClicked()
806
void Placement::showErrorMessage()
808
QMessageBox msg(this);
809
msg.setWindowTitle(tr("Incorrect quantity"));
810
msg.setIcon(QMessageBox::Critical);
811
msg.setText(tr("There are input fields with incorrect input, please ensure valid placement values!"));
815
bool Placement::onApply()
817
//only process things when we have valid inputs!
818
QWidget* input = getInvalidInput();
825
// If there are listeners to the 'placementChanged' signal we rely
826
// on that the listener updates any placement. If no listeners
827
// are connected the placement is applied to all selected objects
829
bool incr = ui->applyIncrementalPlacement->isChecked();
830
Base::Placement plm = this->getPlacement();
831
handler.applyPlacement(getPlacementString(), incr);
833
QVariant data = QVariant::fromValue<Base::Placement>(plm);
834
Q_EMIT placementChanged(data, incr, true);
836
if (ui->applyIncrementalPlacement->isChecked()) {
837
QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
838
for (auto & it : sb) {
839
it->blockSignals(true);
841
it->blockSignals(false);
845
ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Placement");
846
hGrp->SetInt("RotationMethod", ui->rotationInput->currentIndex());
851
void Placement::onResetButtonClicked()
853
QList<Gui::QuantitySpinBox*> sb = this->findChildren<Gui::QuantitySpinBox*>();
854
for (auto & it : sb) {
855
it->blockSignals(true);
857
it->blockSignals(false);
860
onPlacementChanged(0);
864
* \brief Placement::setSelection
865
* Sets the array of selection objects.
868
void Placement::setSelection(const std::vector<Gui::SelectionObject>& selection)
870
handler.setSelection(selection);
873
void Placement::setPropertyName(const std::string& name)
875
handler.setPropertyName(name);
878
void Placement::setIgnoreTransactions(bool value)
880
handler.setIgnoreTransactions(value);
884
* \brief Placement::bindObject
885
* Binds the spin boxes to the placement components of the first object of the selection.
886
* This requires the call of \a setSelection() beforehand.
888
void Placement::bindObject()
890
if (const App::DocumentObject* obj = handler.getFirstOfSelection()) {
891
std::string propertyName = handler.getPropertyName();
892
bindProperty(obj, propertyName);
897
* \brief Placement::setPlacementAndBindObject
898
* Sets the placement, binds the spin boxes to the placement components of the passed object and
899
* sets the name of the placement property.
901
void Placement::setPlacementAndBindObject(const App::DocumentObject* obj, const std::string& propertyName)
904
App::PropertyPlacement* prop = find_placement::getProperty(obj, propertyName);
906
setPlacement(prop->getValue());
907
handler.setPropertyName(propertyName);
908
bindProperty(obj, propertyName);
909
handler.setSelection({SelectionObject{obj}});
914
void Placement::bindProperty(const App::DocumentObject* obj, const std::string& propertyName)
918
App::ObjectIdentifier path = App::ObjectIdentifier::parse(obj, propertyName);
919
if (path.getProperty()) {
920
ui->xPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.x")));
921
ui->yPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.y")));
922
ui->zPos->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Base.z")));
924
ui->xAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.x")));
925
ui->yAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.y")));
926
ui->zAxis->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Axis.z")));
927
ui->angle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Angle")));
929
ui->yawAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Yaw")));
930
ui->pitchAngle->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Pitch")));
931
ui->rollAngle ->bind(App::ObjectIdentifier::parse(obj, propertyName + std::string(".Rotation.Roll")));
933
ui->yawAngle->evaluateExpression();
934
ui->pitchAngle->evaluateExpression();
935
ui->rollAngle->evaluateExpression();
941
Base::Vector3d Placement::getDirection() const
943
double x = ui->xAxis->value().getValue();
944
double y = ui->yAxis->value().getValue();
945
double z = ui->zAxis->value().getValue();
946
return Base::Vector3d(x, y, z);
949
void Placement::setPlacement(const Base::Placement& p)
954
void Placement::setPlacementData(const Base::Placement& p)
956
QSignalBlocker block(signalMapper);
957
ui->xPos->setValue(Base::Quantity(p.getPosition().x, Base::Unit::Length));
958
ui->yPos->setValue(Base::Quantity(p.getPosition().y, Base::Unit::Length));
959
ui->zPos->setValue(Base::Quantity(p.getPosition().z, Base::Unit::Length));
962
p.getRotation().getYawPitchRoll(Y,P,R);
963
ui->yawAngle->setValue(Base::Quantity(Y, Base::Unit::Angle));
964
ui->pitchAngle->setValue(Base::Quantity(P, Base::Unit::Angle));
965
ui->rollAngle->setValue(Base::Quantity(R, Base::Unit::Angle));
969
p.getRotation().getRawValue(axis, angle);
970
ui->xAxis->setValue(axis.x);
971
ui->yAxis->setValue(axis.y);
972
ui->zAxis->setValue(axis.z);
973
ui->angle->setValue(Base::Quantity(Base::toDegrees(angle), Base::Unit::Angle));
976
Base::Placement Placement::getPlacement() const
978
Base::Placement p = getPlacementData();
982
Base::Rotation Placement::getRotationData() const
985
int index = ui->rotationInput->currentIndex();
987
Base::Vector3d dir = getDirection();
988
rot.setValue(Base::Vector3d(dir.x,dir.y,dir.z),Base::toRadians(ui->angle->value().getValue()));
990
else if (index == 1) { // Euler angles (XY'Z'')
992
ui->yawAngle->value().getValue(),
993
ui->pitchAngle->value().getValue(),
994
ui->rollAngle->value().getValue());
1000
Base::Vector3d Placement::getPositionData() const
1002
return Base::Vector3d(ui->xPos->value().getValue(),
1003
ui->yPos->value().getValue(),
1004
ui->zPos->value().getValue());
1007
Base::Vector3d Placement::getAnglesData() const
1009
return Base::Vector3d(ui->yawAngle->value().getValue(),
1010
ui->pitchAngle->value().getValue(),
1011
ui->rollAngle->value().getValue());
1014
Base::Vector3d Placement::getCenterData() const
1016
if (ui->centerOfMass->isChecked()) {
1017
return handler.getCenterOfMass();
1019
return Base::Vector3d(ui->xCnt->value().getValue(),
1020
ui->yCnt->value().getValue(),
1021
ui->zCnt->value().getValue());
1024
Base::Placement Placement::getPlacementData() const
1026
Base::Rotation rot = getRotationData();
1027
Base::Vector3d pos = getPositionData();
1028
Base::Vector3d cnt = getCenterData();
1030
Base::Placement plm(pos, rot, cnt);
1034
QString Placement::getPlacementFromEulerAngles() const
1036
Base::Vector3d pos = getPositionData();
1037
Base::Vector3d ypr = getAnglesData();
1038
Base::Vector3d cnt = getCenterData();
1039
return QString::fromLatin1(
1040
"App.Placement(App.Vector(%1,%2,%3), App.Rotation(%4,%5,%6), App.Vector(%7,%8,%9))")
1052
QString Placement::getPlacementFromAxisWithAngle() const
1054
Base::Vector3d pos = getPositionData();
1055
Base::Vector3d cnt = getCenterData();
1056
Base::Vector3d dir = getDirection();
1057
double angle = ui->angle->value().getValue();
1058
return QString::fromLatin1(
1059
"App.Placement(App.Vector(%1,%2,%3), App.Rotation(App.Vector(%4,%5,%6),%7), App.Vector(%8,%9,%10))")
1072
QString Placement::getPlacementString() const
1075
int index = ui->rotationInput->currentIndex();
1078
cmd = getPlacementFromAxisWithAngle();
1080
else if (index == 1) {
1081
cmd = getPlacementFromEulerAngles();
1087
void Placement::changeEvent(QEvent *e)
1089
if (e->type() == QEvent::LanguageChange) {
1090
ui->retranslateUi(this);
1093
QDialog::changeEvent(e);
1097
// ----------------------------------------------
1099
/* TRANSLATOR Gui::Dialog::DockablePlacement */
1101
DockablePlacement::DockablePlacement(QWidget* parent, Qt::WindowFlags fl) : Placement(parent, fl)
1103
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1104
QDockWidget* dw = pDockMgr->addDockWindow(QT_TR_NOOP("Placement"),
1105
this, Qt::BottomDockWidgetArea);
1106
dw->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable);
1110
DockablePlacement::~DockablePlacement() = default;
1112
void DockablePlacement::accept()
1114
// closes the dock window
1115
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1116
pDockMgr->removeDockWindow(this);
1117
Placement::accept();
1120
void DockablePlacement::reject()
1122
// closes the dock window
1123
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
1124
pDockMgr->removeDockWindow(this);
1125
Placement::reject();
1128
// ----------------------------------------------
1130
/* TRANSLATOR Gui::Dialog::TaskPlacement */
1132
TaskPlacement::TaskPlacement()
1134
this->setButtonPosition(TaskPlacement::South);
1135
widget = new Placement();
1136
widget->showDefaultButtons(false);
1138
connect(widget, &Placement::placementChanged, this, &TaskPlacement::slotPlacementChanged);
1141
TaskPlacement::~TaskPlacement() = default;
1145
* \brief TaskPlacement::setSelection
1146
* Sets the array of selection objects.
1149
void TaskPlacement::setSelection(const std::vector<Gui::SelectionObject>& selection)
1151
widget->setSelection(selection);
1155
* \brief TaskPlacement::clearSelection
1156
* Clears the array of selection objects.
1158
void TaskPlacement::clearSelection()
1160
widget->setSelection({});
1164
* \brief TaskPlacement::bindObject
1165
* Binds the spin boxes to the placement components of the first object of the selection.
1166
* This requires the call of \a setSelection() beforehand.
1168
void TaskPlacement::bindObject()
1170
widget->bindObject();
1173
void TaskPlacement::setPlacementAndBindObject(const App::DocumentObject* obj,
1174
const std::string& propertyName)
1176
widget->setPlacementAndBindObject(obj, propertyName);
1179
void TaskPlacement::open()
1184
void TaskPlacement::setPropertyName(const QString& name)
1186
widget->setPropertyName(name.toStdString());
1189
QDialogButtonBox::StandardButtons TaskPlacement::getStandardButtons() const
1191
return QDialogButtonBox::Ok|
1192
QDialogButtonBox::Cancel|
1193
QDialogButtonBox::Apply;
1196
void TaskPlacement::setPlacement(const Base::Placement& p)
1198
widget->setPlacement(p);
1201
void TaskPlacement::slotPlacementChanged(const QVariant & p, bool incr, bool data)
1203
Q_EMIT placementChanged(p, incr, data);
1206
bool TaskPlacement::accept()
1209
return (widget->result() == QDialog::Accepted);
1212
bool TaskPlacement::reject()
1215
return (widget->result() == QDialog::Rejected);
1218
void TaskPlacement::clicked(int id)
1220
if (id == QDialogButtonBox::Apply) {
1221
widget->onApplyButtonClicked();
1225
// ----------------------------------------------
1227
void TaskPlacementPy::init_type()
1229
behaviors().name("TaskPlacement");
1230
behaviors().doc("TaskPlacement");
1231
behaviors().set_tp_new(PyMake);
1232
// you must have overwritten the virtual functions
1233
behaviors().supportRepr();
1234
behaviors().supportGetattr();
1235
behaviors().supportSetattr();
1237
add_varargs_method("setPropertyName", &TaskPlacementPy::setPropertyName,
1238
"setPropertyName(string)");
1239
add_varargs_method("setPlacement", &TaskPlacementPy::setPlacement,
1240
"setPlacement(Placement)");
1241
add_varargs_method("setSelection", &TaskPlacementPy::setSelection,
1242
"setSelection(list)");
1243
add_varargs_method("bindObject", &TaskPlacementPy::bindObject,
1245
add_varargs_method("setPlacementAndBindObject", &TaskPlacementPy::setPlacementAndBindObject,
1246
"setPlacementAndBindObject(obj, string)");
1247
add_varargs_method("setIgnoreTransactions", &TaskPlacementPy::setIgnoreTransactions,
1248
"setIgnoreTransactions(bool)");
1249
add_varargs_method("showDefaultButtons", &TaskPlacementPy::showDefaultButtons,
1250
"showDefaultButtons(bool)");
1251
add_varargs_method("accept", &TaskPlacementPy::accept,
1253
add_varargs_method("reject", &TaskPlacementPy::reject,
1255
add_varargs_method("clicked", &TaskPlacementPy::clicked,
1257
add_varargs_method("open", &TaskPlacementPy::open,
1259
add_varargs_method("isAllowedAlterDocument", &TaskPlacementPy::isAllowedAlterDocument,
1260
"isAllowedAlterDocument()");
1261
add_varargs_method("isAllowedAlterView", &TaskPlacementPy::isAllowedAlterView,
1262
"isAllowedAlterView()");
1263
add_varargs_method("isAllowedAlterSelection", &TaskPlacementPy::isAllowedAlterSelection,
1264
"isAllowedAlterSelection()");
1265
add_varargs_method("getStandardButtons", &TaskPlacementPy::getStandardButtons,
1266
"getStandardButtons()");
1270
PyObject* TaskPlacementPy::PyMake(struct _typeobject * type, PyObject * args, PyObject * kwds)
1274
if (!PyArg_ParseTuple(args, "")) {
1277
return new TaskPlacementPy();
1280
TaskPlacementPy::TaskPlacementPy()
1281
: widget{new Placement}
1285
TaskPlacementPy::~TaskPlacementPy() = default;
1287
Py::Object TaskPlacementPy::repr()
1289
return Py::String("TaskPlacement");
1292
Py::Object TaskPlacementPy::getattr(const char * name)
1294
if (strcmp(name, "form") == 0) {
1295
Gui::PythonWrapper wrap;
1296
wrap.loadWidgetsModule();
1297
return wrap.fromQWidget(widget, "QDialog");
1299
return BaseType::getattr(name);
1302
int TaskPlacementPy::setattr(const char* name, const Py::Object& attr)
1304
if (strcmp(name, "form") == 0 && attr.isNone()) {
1309
return BaseType::setattr(name, attr);
1312
Py::Object TaskPlacementPy::setPropertyName(const Py::Tuple& args)
1314
const char* propname {};
1315
if (!PyArg_ParseTuple(args.ptr(), "s", &propname)) {
1316
throw Py::Exception();
1320
widget->setPropertyName(propname);
1325
Py::Object TaskPlacementPy::setPlacement(const Py::Tuple& args)
1328
if (!PyArg_ParseTuple(args.ptr(), "O!", &Base::PlacementPy::Type, &plm)) {
1329
throw Py::Exception();
1333
widget->setPlacement(*static_cast<Base::PlacementPy*>(plm)->getPlacementPtr());
1338
Py::Object TaskPlacementPy::setSelection(const Py::Tuple& args)
1340
std::vector<Gui::SelectionObject> sel;
1341
Py::Sequence list(args[0]);
1343
for (const auto& obj : list) {
1344
if (PyObject_TypeCheck(obj.ptr(), &App::DocumentObjectPy::Type)) {
1345
auto doc = static_cast<App::DocumentObjectPy*>(obj.ptr());
1346
sel.emplace_back(doc->getDocumentObjectPtr());
1351
widget->setSelection(sel);
1356
Py::Object TaskPlacementPy::bindObject(const Py::Tuple& args)
1358
if (!PyArg_ParseTuple(args.ptr(), "")) {
1359
throw Py::Exception();
1363
widget->bindObject();
1369
Py::Object TaskPlacementPy::setPlacementAndBindObject(const Py::Tuple& args)
1371
Py::Object object = args[0];
1372
Py::String name = args[1];
1373
std::string propName = static_cast<std::string>(name);
1375
if (PyObject_TypeCheck(object.ptr(), &App::DocumentObjectPy::Type)) {
1376
auto py = static_cast<App::DocumentObjectPy*>(object.ptr());
1377
auto obj = py->getDocumentObjectPtr();
1380
widget->setPlacementAndBindObject(obj, propName);
1387
Py::Object TaskPlacementPy::setIgnoreTransactions(const Py::Tuple& args)
1390
widget->setIgnoreTransactions(Py::Boolean(args[0]));
1395
Py::Object TaskPlacementPy::showDefaultButtons(const Py::Tuple& args)
1398
widget->showDefaultButtons(Py::Boolean(args[0]));
1403
Py::Object TaskPlacementPy::accept(const Py::Tuple& args)
1405
if (!PyArg_ParseTuple(args.ptr(), "")) {
1406
throw Py::Exception();
1412
res = widget->result() == QDialog::Accepted;
1414
return Py::Boolean(res);
1417
Py::Object TaskPlacementPy::reject(const Py::Tuple& args)
1419
if (!PyArg_ParseTuple(args.ptr(), "")) {
1420
throw Py::Exception();
1426
res = widget->result() == QDialog::Rejected;
1428
return Py::Boolean(res);
1431
Py::Object TaskPlacementPy::clicked(const Py::Tuple& args)
1434
if (!PyArg_ParseTuple(args.ptr(), "i", &index)) {
1435
throw Py::Exception();
1438
if (widget && index == QDialogButtonBox::Apply) {
1439
widget->onApplyButtonClicked();
1444
Py::Object TaskPlacementPy::open(const Py::Tuple& args)
1446
if (!PyArg_ParseTuple(args.ptr(), "")) {
1447
throw Py::Exception();
1456
Py::Object TaskPlacementPy::isAllowedAlterDocument(const Py::Tuple& args)
1458
if (!PyArg_ParseTuple(args.ptr(), "")) {
1459
throw Py::Exception();
1461
return Py::Boolean(true);
1464
Py::Object TaskPlacementPy::isAllowedAlterView(const Py::Tuple& args)
1466
if (!PyArg_ParseTuple(args.ptr(), "")) {
1467
throw Py::Exception();
1469
return Py::Boolean(true);
1472
Py::Object TaskPlacementPy::isAllowedAlterSelection(const Py::Tuple& args)
1474
if (!PyArg_ParseTuple(args.ptr(), "")) {
1475
throw Py::Exception();
1477
return Py::Boolean(true);
1480
Py::Object TaskPlacementPy::getStandardButtons(const Py::Tuple& args)
1482
if (!PyArg_ParseTuple(args.ptr(), "")) {
1483
throw Py::Exception();
1485
auto buttons = QDialogButtonBox::Ok|
1486
QDialogButtonBox::Cancel|
1487
QDialogButtonBox::Apply;
1488
return Py::Long(static_cast<int>(buttons));
1491
#include "moc_Placement.cpp"