From f9200f45028a6ce981f7362bda62d2fb572eaaef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Sun, 12 Feb 2017 09:01:22 +0100 Subject: [PATCH] Extend python interface for groups and fix test cases --- src/App/DocumentObjectPy.xml | 14 ++ src/App/DocumentObjectPyImp.cpp | 38 +++++ src/App/ExtensionContainer.cpp | 2 +- src/App/PropertyLinks.cpp | 9 ++ src/Mod/PartDesign/App/Body.cpp | 5 + src/Mod/PartDesign/Gui/CommandBody.cpp | 5 +- src/Mod/PartDesign/TestPartDesignGui.py | 206 ++++++++++++------------ src/Mod/Test/Document.py | 4 + 8 files changed, 178 insertions(+), 105 deletions(-) diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index 156d6e6c49..31e009cbbe 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -55,6 +55,20 @@ Recomputes this object + + + Returns the group the object is in or None if it is not part of a group. + Note that an object can only be in a single group, hence only a single return + value. + + + + + Returns the GeoFeatureGroup, and hence the local coorinate system, the object + is in or None if it is not part of a group. Note that an object can only be + in a single group, hence only a single return value. + + A list of all objects this object links to. diff --git a/src/App/DocumentObjectPyImp.cpp b/src/App/DocumentObjectPyImp.cpp index da62d024d2..cb5835d7ed 100644 --- a/src/App/DocumentObjectPyImp.cpp +++ b/src/App/DocumentObjectPyImp.cpp @@ -25,6 +25,8 @@ #include "DocumentObject.h" #include "Document.h" #include "Expression.h" +#include "GroupExtension.h" +#include "GeoFeatureGroupExtension.h" // inclusion of the generated files (generated out of DocumentObjectPy.xml) #include @@ -315,6 +317,42 @@ PyObject* DocumentObjectPy::recompute(PyObject *args) } } +PyObject* DocumentObjectPy::getParentGroup(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + try { + auto grp = GroupExtension::getGroupOfObject(getDocumentObjectPtr()); + if(!grp) { + Py_INCREF(Py_None); + return Py_None; + } + return grp->getPyObject(); + } + catch (const Base::Exception& e) { + throw Py::RuntimeError(e.what()); + } +} + +PyObject* DocumentObjectPy::getParentGeoFeatureGroup(PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + try { + auto grp = GeoFeatureGroupExtension::getGroupOfObject(getDocumentObjectPtr()); + if(!grp) { + Py_INCREF(Py_None); + return Py_None; + } + return grp->getPyObject(); + } + catch (const Base::Exception& e) { + throw Py::RuntimeError(e.what()); + } +} + PyObject *DocumentObjectPy::getCustomAttributes(const char* attr) const { // search for dynamic property diff --git a/src/App/ExtensionContainer.cpp b/src/App/ExtensionContainer.cpp index 64ff37606c..cbc203a76b 100644 --- a/src/App/ExtensionContainer.cpp +++ b/src/App/ExtensionContainer.cpp @@ -80,7 +80,7 @@ bool ExtensionContainer::hasExtension(Base::Type t, bool derived) const { } return false; } - return true; + return found; } bool ExtensionContainer::hasExtension(const std::string& name) const { diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index af835ad33e..d7320b47d7 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -40,6 +40,7 @@ #include "PropertyLinks.h" #include "GeoFeatureGroupExtension.h" +#include "OriginFeature.h" using namespace App; using namespace Base; @@ -88,6 +89,14 @@ void ensureCorrectGroups(PropertyContainer* container, App::DocumentObject* obje if(!container->isDerivedFrom(App::DocumentObject::getClassTypeId())) return; + //links to origin feature can go over CS borders, as they are the same everywere anyway. This is + //a workaround to allow moving of objects between GeoFeatureGroups that link to origin features. + //During movement there is always a link in to the wron CS and the error would occure. If we + //surpress the error at least the origin links can be fixed afterwards + //TODO: Find a more elegant solution + if(object->isDerivedFrom(App::OriginFeature::getClassTypeId())) + return; + //undo and redo do not need to be handled as they can only go to already checked stated (the link //state during those actions can get messed up, we really don't want to check for that) if(object->getDocument()->performsTransactionOperation()) diff --git a/src/Mod/PartDesign/App/Body.cpp b/src/Mod/PartDesign/App/Body.cpp index 7ec008b9aa..ca713c5571 100644 --- a/src/Mod/PartDesign/App/Body.cpp +++ b/src/Mod/PartDesign/App/Body.cpp @@ -283,6 +283,9 @@ std::vector Body::addObject(App::DocumentObject *feature) if (isSolidFeature(feature)) { Tip.setValue (feature); } + + std::vector result = {feature}; + return result; } @@ -380,6 +383,8 @@ std::vector Body::removeObject(App::DocumentObject* featur model.erase(it); Group.setValues(model); } + std::vector result = {feature}; + return result; } diff --git a/src/Mod/PartDesign/Gui/CommandBody.cpp b/src/Mod/PartDesign/Gui/CommandBody.cpp index e19488bcfb..4b1df7b51c 100644 --- a/src/Mod/PartDesign/Gui/CommandBody.cpp +++ b/src/Mod/PartDesign/Gui/CommandBody.cpp @@ -615,6 +615,9 @@ CmdPartDesignMoveFeature::CmdPartDesignMoveFeature() void CmdPartDesignMoveFeature::activated(int iMsg) { + QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Features moving is diabled"), + QObject::tr("Moving features is currently disabled as there is no way of handling origin connected moves")); + /* Q_UNUSED(iMsg); std::vector features = getSelection().getObjectsOfType(Part::Feature::getClassTypeId()); if (features.empty()) return; @@ -731,7 +734,7 @@ void CmdPartDesignMoveFeature::activated(int iMsg) PartDesignGui::relinkToOrigin(feat, target); } - updateActive(); + updateActive();*/ } bool CmdPartDesignMoveFeature::isActive(void) diff --git a/src/Mod/PartDesign/TestPartDesignGui.py b/src/Mod/PartDesign/TestPartDesignGui.py index cac9c51803..5daa2d4db0 100644 --- a/src/Mod/PartDesign/TestPartDesignGui.py +++ b/src/Mod/PartDesign/TestPartDesignGui.py @@ -86,121 +86,121 @@ class PartDesignGuiTestCases(unittest.TestCase): def setUp(self): self.Doc = FreeCAD.newDocument("SketchGuiTest") - def testRefuseToMoveSingleFeature(self): - FreeCAD.Console.PrintMessage('Testing refuse to move the feature with dependecies from one body to another\n') - self.BodySource = self.Doc.addObject('PartDesign::Body','Body') - Gui.activeView().setActiveObject('pdbody', self.BodySource) + #def testRefuseToMoveSingleFeature(self): + #FreeCAD.Console.PrintMessage('Testing refuse to move the feature with dependecies from one body to another\n') + #self.BodySource = self.Doc.addObject('PartDesign::Body','Body') + #Gui.activeView().setActiveObject('pdbody', self.BodySource) - self.BoxObj = self.Doc.addObject('PartDesign::AdditiveBox','Box') - self.BoxObj.Length=10.0 - self.BoxObj.Width=10.0 - self.BoxObj.Height=10.0 - self.BodySource.addObject(self.BoxObj) + #self.BoxObj = self.Doc.addObject('PartDesign::AdditiveBox','Box') + #self.BoxObj.Length=10.0 + #self.BoxObj.Width=10.0 + #self.BoxObj.Height=10.0 + #self.BodySource.addObject(self.BoxObj) - App.ActiveDocument.recompute() + #App.ActiveDocument.recompute() - self.Sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch') - self.Sketch.Support = (self.BoxObj, ('Face3',)) - self.Sketch.MapMode = 'FlatFace' - self.BodySource.addObject(self.Sketch) + #self.Sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch') + #self.Sketch.Support = (self.BoxObj, ('Face3',)) + #self.Sketch.MapMode = 'FlatFace' + #self.BodySource.addObject(self.Sketch) - geoList = [] - geoList.append(Part.LineSegment(App.Vector(2.0,8.0,0),App.Vector(8.0,8.0,0))) - geoList.append(Part.LineSegment(App.Vector(8.0,8.0,0),App.Vector(8.0,2.0,0))) - geoList.append(Part.LineSegment(App.Vector(8.0,2.0,0),App.Vector(2.0,2.0,0))) - geoList.append(Part.LineSegment(App.Vector(2.0,2.0,0),App.Vector(2.0,8.0,0))) - self.Sketch.addGeometry(geoList,False) - conList = [] - conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) - conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) - conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) - conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) - conList.append(Sketcher.Constraint('Horizontal',0)) - conList.append(Sketcher.Constraint('Horizontal',2)) - conList.append(Sketcher.Constraint('Vertical',1)) - conList.append(Sketcher.Constraint('Vertical',3)) - self.Sketch.addConstraint(conList) + #geoList = [] + #geoList.append(Part.LineSegment(App.Vector(2.0,8.0,0),App.Vector(8.0,8.0,0))) + #geoList.append(Part.LineSegment(App.Vector(8.0,8.0,0),App.Vector(8.0,2.0,0))) + #geoList.append(Part.LineSegment(App.Vector(8.0,2.0,0),App.Vector(2.0,2.0,0))) + #geoList.append(Part.LineSegment(App.Vector(2.0,2.0,0),App.Vector(2.0,8.0,0))) + #self.Sketch.addGeometry(geoList,False) + #conList = [] + #conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) + #conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) + #conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) + #conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) + #conList.append(Sketcher.Constraint('Horizontal',0)) + #conList.append(Sketcher.Constraint('Horizontal',2)) + #conList.append(Sketcher.Constraint('Vertical',1)) + #conList.append(Sketcher.Constraint('Vertical',3)) + #self.Sketch.addConstraint(conList) - self.Pad = self.Doc.addObject("PartDesign::Pad","Pad") - self.Pad.Profile = self.Sketch - self.Pad.Length = 10.000000 - self.Pad.Length2 = 100.000000 - self.Pad.Type = 0 - self.Pad.UpToFace = None - self.Pad.Reversed = 0 - self.Pad.Midplane = 0 - self.Pad.Offset = 0.000000 + #self.Pad = self.Doc.addObject("PartDesign::Pad","Pad") + #self.Pad.Profile = self.Sketch + #self.Pad.Length = 10.000000 + #self.Pad.Length2 = 100.000000 + #self.Pad.Type = 0 + #self.Pad.UpToFace = None + #self.Pad.Reversed = 0 + #self.Pad.Midplane = 0 + #self.Pad.Offset = 0.000000 - self.BodySource.addObject(self.Pad) + #self.BodySource.addObject(self.Pad) - self.Doc.recompute() - Gui.SendMsgToActiveView("ViewFit") + #self.Doc.recompute() + #Gui.SendMsgToActiveView("ViewFit") - self.BodyTarget = self.Doc.addObject('PartDesign::Body','Body') + #self.BodyTarget = self.Doc.addObject('PartDesign::Body','Body') - Gui.Selection.addSelection(App.ActiveDocument.Pad) - cobj = CallableCheckWarning(self) - QtCore.QTimer.singleShot(500, cobj) - Gui.runCommand('PartDesign_MoveFeature') - #assert depenedencies of the Sketch - self.assertEqual(len(self.BodySource.Group), 3, "Source body feature count is wrong") - self.assertEqual(len(self.BodyTarget.Group), 0, "Target body feature count is wrong") + #Gui.Selection.addSelection(App.ActiveDocument.Pad) + #cobj = CallableCheckWarning(self) + #QtCore.QTimer.singleShot(500, cobj) + #Gui.runCommand('PartDesign_MoveFeature') + ##assert depenedencies of the Sketch + #self.assertEqual(len(self.BodySource.Group), 3, "Source body feature count is wrong") + #self.assertEqual(len(self.BodyTarget.Group), 0, "Target body feature count is wrong") - def testMoveSingleFeature(self): - FreeCAD.Console.PrintMessage('Testing moving one feature from one body to another\n') - self.BodySource = self.Doc.addObject('PartDesign::Body','Body') - Gui.activeView().setActiveObject('pdbody', self.BodySource) + #def testMoveSingleFeature(self): + #FreeCAD.Console.PrintMessage('Testing moving one feature from one body to another\n') + #self.BodySource = self.Doc.addObject('PartDesign::Body','Body') + #Gui.activeView().setActiveObject('pdbody', self.BodySource) - self.Sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch') - self.Sketch.Support = (self.Doc.XY_Plane, ['']) - self.Sketch.MapMode = 'FlatFace' - self.BodySource.addObject(self.Sketch) - - geoList = [] - geoList.append(Part.LineSegment(App.Vector(-10.000000,10.000000,0),App.Vector(10.000000,10.000000,0))) - geoList.append(Part.LineSegment(App.Vector(10.000000,10.000000,0),App.Vector(10.000000,-10.000000,0))) - geoList.append(Part.LineSegment(App.Vector(10.000000,-10.000000,0),App.Vector(-10.000000,-10.000000,0))) - geoList.append(Part.LineSegment(App.Vector(-10.000000,-10.000000,0),App.Vector(-10.000000,10.000000,0))) - self.Sketch.addGeometry(geoList,False) - conList = [] - conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) - conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) - conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) - conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) - conList.append(Sketcher.Constraint('Horizontal',0)) - conList.append(Sketcher.Constraint('Horizontal',2)) - conList.append(Sketcher.Constraint('Vertical',1)) - conList.append(Sketcher.Constraint('Vertical',3)) - self.Sketch.addConstraint(conList) - - self.Pad = self.Doc.addObject("PartDesign::Pad","Pad") - self.Pad.Profile = self.Sketch - self.Pad.Length = 10.000000 - self.Pad.Length2 = 100.000000 - self.Pad.Type = 0 - self.Pad.UpToFace = None - self.Pad.Reversed = 0 - self.Pad.Midplane = 0 - self.Pad.Offset = 0.000000 - - self.BodySource.addObject(self.Pad) - - self.Doc.recompute() - Gui.SendMsgToActiveView("ViewFit") - - self.BodyTarget = self.Doc.addObject('PartDesign::Body','Body') - - Gui.Selection.addSelection(App.ActiveDocument.Pad) - cobj = CallableComboBox(self) - QtCore.QTimer.singleShot(500, cobj) - Gui.runCommand('PartDesign_MoveFeature') - #assert depenedencies of the Sketch - self.Doc.recompute() + #self.Sketch = self.Doc.addObject('Sketcher::SketchObject','Sketch') + #self.BodySource.addObject(self.Sketch) + #self.Sketch.Support = (self.BodySource.Origin.OriginFeatures[3], ['']) + #self.Sketch.MapMode = 'FlatFace' - self.assertFalse(self.Sketch.Support[0][0] in self.BodySource.Origin.OriginFeatures) - self.assertTrue(self.Sketch.Support[0][0] in self.BodyTarget.Origin.OriginFeatures) - self.assertEqual(len(self.BodySource.Group), 0, "Source body feature count is wrong") - self.assertEqual(len(self.BodyTarget.Group), 2, "Target body feature count is wrong") + + #geoList = [] + #geoList.append(Part.LineSegment(App.Vector(-10.000000,10.000000,0),App.Vector(10.000000,10.000000,0))) + #geoList.append(Part.LineSegment(App.Vector(10.000000,10.000000,0),App.Vector(10.000000,-10.000000,0))) + #geoList.append(Part.LineSegment(App.Vector(10.000000,-10.000000,0),App.Vector(-10.000000,-10.000000,0))) + #geoList.append(Part.LineSegment(App.Vector(-10.000000,-10.000000,0),App.Vector(-10.000000,10.000000,0))) + #self.Sketch.addGeometry(geoList,False) + #conList = [] + #conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) + #conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) + #conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) + #conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) + #conList.append(Sketcher.Constraint('Horizontal',0)) + #conList.append(Sketcher.Constraint('Horizontal',2)) + #conList.append(Sketcher.Constraint('Vertical',1)) + #conList.append(Sketcher.Constraint('Vertical',3)) + #self.Sketch.addConstraint(conList) + + #self.Pad = self.Doc.addObject("PartDesign::Pad","Pad") + #self.BodySource.addObject(self.Pad) + #self.Pad.Profile = self.Sketch + #self.Pad.Length = 10.000000 + #self.Pad.Length2 = 100.000000 + #self.Pad.Type = 0 + #self.Pad.UpToFace = None + #self.Pad.Reversed = 0 + #self.Pad.Midplane = 0 + #self.Pad.Offset = 0.000000 + + #self.Doc.recompute() + #Gui.SendMsgToActiveView("ViewFit") + + #self.BodyTarget = self.Doc.addObject('PartDesign::Body','Body') + + #Gui.Selection.addSelection(App.ActiveDocument.Pad) + #cobj = CallableComboBox(self) + #QtCore.QTimer.singleShot(500, cobj) + #Gui.runCommand('PartDesign_MoveFeature') + ##assert depenedencies of the Sketch + #self.Doc.recompute() + + #self.assertFalse(self.Sketch.Support[0][0] in self.BodySource.Origin.OriginFeatures) + #self.assertTrue(self.Sketch.Support[0][0] in self.BodyTarget.Origin.OriginFeatures) + #self.assertEqual(len(self.BodySource.Group), 0, "Source body feature count is wrong") + #self.assertEqual(len(self.BodyTarget.Group), 2, "Target body feature count is wrong") def tearDown(self): FreeCAD.closeDocument("SketchGuiTest") diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 039a9c581c..0413e9db7f 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -841,6 +841,8 @@ class UndoRedoCases(unittest.TestCase): grp1 = self.Doc.addObject("App::DocumentObjectGroup","Group1") grp2 = self.Doc.addObject("App::DocumentObjectGroup","Group2") grp1.addObject(obj1) + self.failUnless(obj1.getParentGroup()==grp1) + self.failUnless(obj1.getParentGeoFeatureGroup()==None) self.failUnless(grp1.hasObject(obj1)) grp2.addObject(obj1) self.failUnless(grp1.hasObject(obj1)==False) @@ -851,6 +853,8 @@ class UndoRedoCases(unittest.TestCase): prt2 = self.Doc.addObject("App::Part","Part2") prt1.addObject(grp2) + self.failUnless(grp2.getParentGeoFeatureGroup()==prt1) + self.failUnless(grp2.getParentGroup()==None) self.failUnless(grp2.hasObject(obj1)) self.failUnless(prt1.hasObject(grp2)) self.failUnless(prt1.hasObject(obj1))