diff --git a/src/Mod/Part/App/AppPart.cpp b/src/Mod/Part/App/AppPart.cpp
index 21edf90cf1..eaa8f9ea58 100644
--- a/src/Mod/Part/App/AppPart.cpp
+++ b/src/Mod/Part/App/AppPart.cpp
@@ -51,7 +51,6 @@
#include "PartFeatures.h"
#include "BodyBase.h"
#include "PrimitiveFeature.h"
-#include "Attacher.h"
#include "Part2DObject.h"
#include "CustomFeature.h"
#include "TopoShapePy.h"
@@ -91,6 +90,7 @@
#include "ToroidPy.h"
#include "BRepOffsetAPI_MakePipeShellPy.h"
#include "PartFeaturePy.h"
+#include "AttachEnginePy.h"
#include "PropertyGeometryList.h"
#include "DatumFeature.h"
#include "Attacher.h"
@@ -210,6 +210,8 @@ PyMODINIT_FUNC initPart()
Base::Interpreter().addType(&Part::PartFeaturePy ::Type,partModule,"Feature");
+ Base::Interpreter().addType(&Attacher::AttachEnginePy ::Type,partModule,"AttachEngine");
+
PyObject* brepModule = Py_InitModule3("BRepOffsetAPI", 0, "BrepOffsetAPI");
Py_INCREF(brepModule);
PyModule_AddObject(partModule, "BRepOffsetAPI", brepModule);
@@ -230,6 +232,7 @@ PyMODINIT_FUNC initPart()
Part::Feature ::init();
Part::FeatureExt ::init();
Part::AttachableObject ::init();
+ Part::AttachableObjectPython::init();
Part::BodyBase ::init();
Part::FeaturePython ::init();
Part::FeatureGeometrySet ::init();
diff --git a/src/Mod/Part/App/AttachEnginePy.xml b/src/Mod/Part/App/AttachEnginePy.xml
new file mode 100644
index 0000000000..99a605674a
--- /dev/null
+++ b/src/Mod/Part/App/AttachEnginePy.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+ AttachEngine abstract class
+ AttachEngine abstract class - the functinality of AttachableObject, but outside of DocumentObject
+
+
+
+ Type of engine: 3d, plane, line, or point.
+
+
+
+
+
+ Current attachment mode.
+
+
+
+
+
+ Current attachment mode.
+
+
+
+
+
+ Current attachment mode.
+
+
+
+
+
+ If True, Z axis of attached placement is flipped. X axis is flipped in addition (CS has to remain right-handed).
+
+
+
+
+
+ Value of parameter for some curve attachment modes. Range of 0..1 spans the length of the edge (parameter value can be outside of the range for curves that allow extrapolation.
+
+
+
+
+
+
+ List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects.
+
+
+
+
+
+ List of all attachment modes of all AttachEngines. This is the list of modes in MapMode enum properties of AttachableObjects.
+
+
+
+
+
+ List of all reference shape types recognized by AttachEngine.
+
+
+
+
+
+
+ getModeInfo(mode): returns supported reference combinations, user-friendly name, and so on.
+
+
+
+
+ getRefTypeOfShape(shape): returns shape type as interpreted by AttachEngine. Returns a string.
+
+
+
+
+ isFittingRefType(type_shape, type_needed): tests if shape type, specified by type_shape (string), fits a type required by attachment mode type_needed (string). e.g. 'Circle' fits a requirement of 'Edge', and 'Curve' doesn't fit if a 'Circle' is required.
+
+
+
+
+ downgradeRefType(type): returns next more general type. E.g. downgradeType('Circle') yeilds 'Curve'.
+
+
+
+
+ getRefTypeInfo(type): returns information (dict) on shape type. Keys:'UserFriendlyName', 'TypeIndex', 'Rank'. Rank is the number of times reftype can be downgraded, before it becomes 'Any'.
+
+
+
+
+ copy(): returns a new instance of AttachEngine.
+
+
+
+
+ calculateAttachedPlacement(orig_placement): returns result of attachment, based
+on current Mode, References, etc. SuperPlacment is included.
+
+original_placement is the previous placement of the object being attached. It
+is used to preserve orientation for Translate attachment mode. For other modes,
+it is ignored.
+
+Returns the new placement. If not attached, returns None. If attachment fails,
+an exception is raised.
+
+
+
+
+
+suggestModes(): runs mode suggestion routine and returns a dictionary with
+results and supplementary information.
+
+Keys:
+'allApplicableModes': list of modes that can accept current references. Note
+that it is only a check by types, and does not guarantee the modes will
+actually work.
+
+'bestFitMode': mode that fits current references best. Note that the mode may
+not be valid for the set of references; check for if 'message' is 'OK'.
+
+'error': error message for when 'message' is 'UnexpectedError' or
+'LinkBroken'.
+
+'message': general result of suggestion. 'IncompatibleGeometry', 'NoModesFit':
+no modes accept current set of references; 'OK': some modes do accept current
+set of references (though it's not guarantted the modes will work - surrestor
+only checks for correct types); 'UnexpectedError': should never happen.
+
+'nextRefTypeHint': what more can be added to references to reach other modes
+('reachableModes' provide more extended information on this)
+
+'reachableModes': a dict, where key is mode, and value is a list of sequences
+of references that can be added to fit that mode.
+
+'references_Types': a list of types of geometry linked by references (that's
+the input information for suggestor, actually).
+
+
+
+
+ readParametersFromFeature(document_object): sets AttachEngine parameters (References, Mode, etc.) by reading out properties of AttachableObject-derived feature.
+
+
+
+
+
+writeParametersToFeature(document_object): updates properties of
+AttachableObject-derived feature with current AttachEngine parameters
+(References, Mode, etc.).
+
+Warning: if a feature linked by AttachEngine.References was deleted, this method
+will crash FreeCAD.
+
+
+
+
+
diff --git a/src/Mod/Part/App/AttachEnginePyImp.cpp b/src/Mod/Part/App/AttachEnginePyImp.cpp
new file mode 100644
index 0000000000..e497dcbcfb
--- /dev/null
+++ b/src/Mod/Part/App/AttachEnginePyImp.cpp
@@ -0,0 +1,546 @@
+#include "PreCompiled.h"
+#ifndef _PreComp_
+# include
+#endif
+
+#include "Mod/Part/App/Attacher.h"
+#include
+#include
+#include "AttachableObjectPy.h"
+#include "TopoShapePy.h"
+
+#include "OCCError.h"
+
+// inclusion of the generated files (generated out of AttachableObjectPy.xml)
+#include "AttachEnginePy.h"
+#include "AttachEnginePy.cpp"
+
+using namespace Attacher;
+
+// returns a string which represents the object e.g. when printed in python
+std::string AttachEnginePy::representation(void) const
+{
+ return std::string("");
+}
+
+PyObject* AttachEnginePy::PyMake(struct _typeobject *, PyObject *, PyObject *)
+{
+ // create a new instance of AttachEngine3D
+ return new AttachEnginePy(new AttachEngine3D);
+}
+
+// constructor method
+int AttachEnginePy::PyInit(PyObject* args, PyObject* /*kwd*/)
+{
+ PyObject* o;
+ if (PyArg_ParseTuple(args, "")) {
+ return 0;
+ }
+
+ PyErr_Clear();
+ if (PyArg_ParseTuple(args, "O!", &(AttachEnginePy::Type), &o)) {
+ AttachEngine* attacher = static_cast(o)->getAttachEnginePtr();
+ AttachEngine* oldAttacher = this->getAttachEnginePtr();
+ this->_pcTwinPointer = attacher->copy();
+ delete oldAttacher;
+ return 0;
+ }
+
+ PyErr_Clear();
+ char* typeName;
+ if (PyArg_ParseTuple(args, "s", &typeName)) {
+ Base::Type t = Base::Type::fromName(typeName);
+ AttachEngine* pNewAttacher = nullptr;
+ if (t.isDerivedFrom(AttachEngine::getClassTypeId())){
+ pNewAttacher = static_cast(Base::Type::createInstanceByName(typeName));
+ }
+ if (!pNewAttacher) {
+ std::stringstream errMsg;
+ errMsg << "Object if this type is not derived from AttachEngine: " << typeName;
+ PyErr_SetString(Base::BaseExceptionFreeCADError, errMsg.str().c_str());
+ return -1;
+ }
+ AttachEngine* oldAttacher = this->getAttachEnginePtr();
+ this->_pcTwinPointer = pNewAttacher;
+ delete oldAttacher;
+ return 0;
+ }
+
+ PyErr_SetString(Base::BaseExceptionFreeCADError, "Wrong set of constructor arguments. Can be: (), ('Attacher::AttachEngine3D'), ('Attacher::AttachEnginePlane'), ('Attacher::AttachEngineLine'), ('Attacher::AttachEnginePoint'), (other_attacher_instance).");
+ return -1;
+
+}
+
+
+Py::String AttachEnginePy::getAttacherType(void) const
+{
+ return Py::String(std::string(this->getAttachEnginePtr()->getTypeId().getName()));
+}
+
+/**
+ * @brief macro ATTACHERPY_STDCATCH_ATTR: catch for exceptions in attribute
+ * code (re-throws the exception converted to Py::Exception). It is a helper
+ * to avoid repeating the same error handling code over and over again.
+ */
+#define ATTACHERPY_STDCATCH_ATTR \
+ catch (Standard_Failure) {\
+ Handle_Standard_Failure e = Standard_Failure::Caught();\
+ throw Py::Exception(Part::PartExceptionOCCError, e->GetMessageString());\
+ } catch (Base::Exception &e) {\
+ throw Py::Exception(Base::BaseExceptionFreeCADError, e.what());\
+ }
+
+Py::String AttachEnginePy::getMode(void) const
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ return Py::String(attacher.getModeName(attacher.mapMode));
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+void AttachEnginePy::setMode(Py::String arg)
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ std::string modeName = (std::string)arg;
+ attacher.mapMode = attacher.getModeByName(modeName);
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::Object AttachEnginePy::getReferences(void) const
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ AttachEngine::verifyReferencesAreSafe(attacher.references);
+ return Py::Object(attacher.references.getPyObject(),true);
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+void AttachEnginePy::setReferences(Py::Object arg)
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ attacher.references.setPyObject(arg.ptr());
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::Object AttachEnginePy::getSuperPlacement(void) const
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ return Py::Object(new Base::PlacementPy(new Base::Placement(attacher.superPlacement)),true);
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+void AttachEnginePy::setSuperPlacement(Py::Object arg)
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ if (PyObject_TypeCheck(arg.ptr(), &(Base::PlacementPy::Type))) {
+ const Base::PlacementPy* plmPy = static_cast(arg.ptr());
+ attacher.superPlacement = *(plmPy->getPlacementPtr());
+ } else {
+ std::string error = std::string("type must be 'Placement', not ");
+ error += arg.type().as_string();
+ throw Py::TypeError(error);
+ }
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::Boolean AttachEnginePy::getReverse(void) const
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ return Py::Boolean(attacher.mapReverse);
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+void AttachEnginePy::setReverse(Py::Boolean arg)
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ attacher.mapReverse = arg.isTrue();
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::Float AttachEnginePy::getParameter(void) const
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ return Py::Float(attacher.attachParameter);
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+void AttachEnginePy::setParameter(Py::Float arg)
+{
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ attacher.attachParameter = (double)arg;
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+
+Py::List AttachEnginePy::getCompleteModeList(void) const
+{
+ try {
+ Py::List ret;
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ for(int imode = 0 ; imode < mmDummy_NumberOfModes ; imode++){
+ ret.append(Py::String(attacher.getModeName(eMapMode(imode))));
+ }
+ return ret;
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::List AttachEnginePy::getCompleteRefTypeList(void) const
+{
+ try {
+ Py::List ret;
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ for(int irt = 0 ; irt < rtDummy_numberOfShapeTypes ; irt++){
+ ret.append(Py::String(attacher.getRefTypeName(eRefType(irt))));
+ }
+ return ret;
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+Py::List AttachEnginePy::getImplementedModes(void) const
+{
+ try {
+ Py::List ret;
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ for(int imode = 0 ; imode < mmDummy_NumberOfModes ; imode++){
+ if(attacher.modeRefTypes[imode].size() > 0){
+ ret.append(Py::String(attacher.getModeName(eMapMode(imode))));
+ }
+ }
+ return ret;
+ } ATTACHERPY_STDCATCH_ATTR;
+}
+
+/**
+ * @brief macro ATTACHERPY_STDCATCH_METH: catch for exceptions in method code
+ * (returns NULL if an exception is caught). It is a helper to avoid repeating
+ * the same error handling code over and over again.
+ */
+#define ATTACHERPY_STDCATCH_METH \
+ catch (Standard_Failure) {\
+ Handle_Standard_Failure e = Standard_Failure::Caught();\
+ PyErr_SetString(Part::PartExceptionOCCError, e->GetMessageString());\
+ return NULL;\
+ } catch (Base::Exception &e) {\
+ PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());\
+ return NULL;\
+ } catch (const Py::Exception &){\
+ return NULL;\
+ }
+
+
+PyObject* AttachEnginePy::getModeInfo(PyObject* args)
+{
+ char* modeName;
+ if (!PyArg_ParseTuple(args, "s", &modeName))
+ return 0;
+
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ eMapMode mmode = attacher.getModeByName(modeName);
+ Py::List pyListOfCombinations;
+ Py::List pyCombination;
+ refTypeStringList &listOfCombinations = attacher.modeRefTypes.at(mmode);
+ for(const refTypeString &combination: listOfCombinations){
+ pyCombination = Py::List(combination.size());
+ for(int iref = 0 ; iref < combination.size() ; iref++){
+ pyCombination[iref] = Py::String(AttachEngine::getRefTypeName(combination[iref]));
+ }
+ pyListOfCombinations.append(pyCombination);
+ }
+ Py::Dict ret;
+ ret["ReferenceCombinations"] = pyListOfCombinations;
+ ret["ModeIndex"] = Py::Int(mmode);
+
+ try {
+ Py::Module module(PyImport_ImportModule("PartGui"),true);
+ if (!module.hasAttr("AttachEngineResources")) {
+ // in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
+ throw Py::RuntimeError("Gui is not up");//DeepSOIC: wanted to throw ImportError here, but it's not defined, so I don't know...
+ }
+ Py::Object submod(module.getAttr("AttachEngineResources"));
+ Py::Callable method(submod.getAttr("getModeStrings"));
+ Py::Tuple arg(2);
+ arg.setItem(0, Py::String(this->getAttachEnginePtr()->getTypeId().getName()));
+ arg.setItem(1, Py::Int(mmode));
+ Py::List strs = method.apply(arg);
+ assert(strs.size() == 2);
+ ret["UserFriendlyName"] = strs[0];
+ ret["BriefDocu"] = strs[1];
+ } catch (Py::Exception& e) {
+ if (PyErr_ExceptionMatches(PyExc_ImportError)) {
+ // the GUI is not up.
+ Base::Console().Warning("AttachEngine: Gui not up, so no gui-related entries in getModeInfo.\n");
+ e.clear();
+ } else {
+ Base::Console().Warning("AttachEngine.getModeInfo: error obtaining GUI strings\n");
+ e.clear();
+ }
+ } catch (Base::Exception &e){
+ Base::Console().Warning("AttachEngine.getModeInfo: error obtaining GUI strings:");
+ Base::Console().Warning(e.what());
+ Base::Console().Warning("\n");
+ }
+
+ return Py::new_reference_to(ret);
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::getRefTypeOfShape(PyObject* args)
+{
+ PyObject *pcObj;
+ if (!PyArg_ParseTuple(args, "O!", &(Part::TopoShapePy::Type), &pcObj))
+ return NULL;
+
+ try{
+ TopoDS_Shape shape = static_cast(pcObj)->getTopoShapePtr()->_Shape;
+ eRefType rt = AttachEngine::getShapeType(shape);
+ return Py::new_reference_to(Py::String(AttachEngine::getRefTypeName(rt)));
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::isFittingRefType(PyObject* args)
+{
+ char* type_shape_str;
+ char* type_need_str;
+ if (!PyArg_ParseTuple(args, "ss", &type_shape_str, &type_need_str))
+ return 0;
+ try {
+ eRefType type_shape = AttachEngine::getRefTypeByName(std::string(type_shape_str));
+ eRefType type_need = AttachEngine::getRefTypeByName(std::string(type_need_str));
+ bool result = AttachEngine::isShapeOfType(type_shape, type_need) > -1;
+ return Py::new_reference_to(Py::Boolean(result));
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::downgradeRefType(PyObject* args)
+{
+ char* type_shape_str;
+ if (!PyArg_ParseTuple(args, "s", &type_shape_str))
+ return 0;
+ try {
+ eRefType type_shape = AttachEngine::getRefTypeByName(std::string(type_shape_str));
+ eRefType result = AttachEngine::downgradeType(type_shape);
+ return Py::new_reference_to(Py::String(AttachEngine::getRefTypeName(result)));
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::getRefTypeInfo(PyObject* args)
+{
+ char* typeName;
+ if (!PyArg_ParseTuple(args, "s", &typeName))
+ return 0;
+
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ eRefType rt = attacher.getRefTypeByName(typeName);
+ Py::Dict ret;
+ ret["TypeIndex"] = Py::Int(rt);
+ ret["Rank"] = Py::Int(AttachEngine::getTypeRank(rt));
+
+ try {
+ Py::Module module(PyImport_ImportModule("PartGui"),true);
+ if (!module.hasAttr("AttachEngineResources")) {
+ // in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
+ throw Py::RuntimeError("Gui is not up");//DeepSOIC: wanted to throw ImportError here, but it's not defined, so I don't know...
+ }
+ Py::Object submod(module.getAttr("AttachEngineResources"));
+ Py::Callable method(submod.getAttr("getRefTypeUserFriendlyName"));
+ Py::Tuple arg(1);
+ arg.setItem(0, Py::Int(rt));
+ Py::String st = method.apply(arg);
+ ret["UserFriendlyName"] = st;
+ } catch (Py::Exception& e) {
+ if (PyErr_ExceptionMatches(PyExc_ImportError)) {
+ // the GUI is not up.
+ Base::Console().Warning("AttachEngine: Gui not up, so no gui-related entries in getModeInfo.\n");
+ e.clear();
+ } else {
+ Base::Console().Warning("AttachEngine.getRefTypeInfo: error obtaining GUI strings\n");
+ e.clear();
+ }
+ } catch (Base::Exception &e){
+ Base::Console().Warning("AttachEngine.getRefTypeInfo: error obtaining GUI strings:");
+ Base::Console().Warning(e.what());
+ Base::Console().Warning("\n");
+ }
+
+ return Py::new_reference_to(ret);
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::copy(PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return 0;
+
+ return new AttachEnginePy(this->getAttachEnginePtr()->copy());
+}
+
+PyObject* AttachEnginePy::calculateAttachedPlacement(PyObject* args)
+{
+ PyObject *pcObj;
+ if (!PyArg_ParseTuple(args, "O!", &(Base::PlacementPy::Type), &pcObj))
+ return NULL;
+ try{
+ const Base::Placement& plm = *(static_cast(pcObj)->getPlacementPtr());
+ Base::Placement result;
+ try{
+ result = this->getAttachEnginePtr()->calculateAttachedPlacement(plm);
+ } catch (ExceptionCancel) {
+ Py_IncRef(Py_None);
+ return Py_None;
+ }
+ return new Base::PlacementPy(new Base::Placement(result));
+ } ATTACHERPY_STDCATCH_METH;
+
+ return NULL;
+}
+
+PyObject* AttachEnginePy::suggestModes(PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return 0;
+
+ try {
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ SuggestResult sugr;
+ attacher.suggestMapModes(sugr);
+ Py::Dict result;
+ { //sugr.allApplicableModes
+ Py::List pyList;
+ for(eMapMode mmode: sugr.allApplicableModes){
+ pyList.append(Py::String(AttachEngine::getModeName(mmode)));
+ }
+ result["allApplicableModes"] = pyList;
+ }
+ { //sugr.bestFitMode
+ result["bestFitMode"] = Py::String(AttachEngine::getModeName(sugr.bestFitMode));
+ }
+ {//sugr.error
+ bool isError = sugr.message == SuggestResult::srUnexpectedError
+ || sugr.message == SuggestResult::srLinkBroken;
+ result["error"] = Py::String(isError ? sugr.error.what() : "");
+ }
+ {//sugr.message
+ std::string msg;
+ switch(sugr.message){
+ case SuggestResult::srIncompatibleGeometry:
+ msg = "IncompatibleGeometry";
+ break;
+ case SuggestResult::srLinkBroken:
+ msg = "LinkBroken";
+ break;
+ case SuggestResult::srNoModesFit:
+ msg = "NoModesFit";
+ break;
+ case SuggestResult::srOK:
+ msg = "OK";
+ break;
+ case SuggestResult::srUnexpectedError:
+ msg = "UnexpectedError";
+ break;
+ default:
+ msg = "";
+ }
+ result["message"] = Py::String(msg);
+ }
+ {//sugr.nextRefTypeHint
+ Py::List pyList;
+ for(eRefType rt : sugr.nextRefTypeHint){
+ pyList.append(Py::String(AttachEngine::getRefTypeName(rt)));
+ }
+ result["nextRefTypeHint"] = pyList;
+ }
+ {//sugr.reachableModes
+ Py::Dict pyRM;
+ for(std::pair &rm: sugr.reachableModes){
+ Py::List pyListOfCombinations;
+ for(refTypeString &rts : rm.second){
+ Py::List pyCombination;
+ for(eRefType rt : rts){
+ pyCombination.append(Py::String(AttachEngine::getRefTypeName(rt)));
+ }
+ pyListOfCombinations.append(pyCombination);
+ }
+ pyRM[AttachEngine::getModeName(rm.first)] = pyListOfCombinations;
+ }
+ result["reachableModes"] = pyRM;
+ }
+ {//sugr.references_Types
+ Py::List pyList;
+ for(eRefType rt : sugr.references_Types){
+ pyList.append(Py::String(AttachEngine::getRefTypeName(rt)));
+ }
+ result["references_Types"] = pyList;
+ }
+
+ return Py::new_reference_to(result);
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::readParametersFromFeature(PyObject* args)
+{
+ PyObject* obj;
+ if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type),&obj))
+ return NULL; // NULL triggers exception
+
+ try{
+ const App::DocumentObjectPy* dobjpy = static_cast(obj);
+ const App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr();
+ if (! dobj->isDerivedFrom(Part::AttachableObject::getClassTypeId())){
+ throw Py::TypeError("Supplied object isn't Part::AttachableObject");
+ }
+ const Part::AttachableObject* feat = static_cast(dobj);
+ AttachEngine &attacher = *(this->getAttachEnginePtr());
+ attacher.setUp(feat->Support,
+ eMapMode(feat->MapMode.getValue()),
+ feat->MapReversed.getValue(),
+ feat->MapPathParameter.getValue(),
+ 0.0,0.0,
+ feat->superPlacement.getValue());
+ return Py::new_reference_to(Py::None());
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::writeParametersToFeature(PyObject* args)
+{
+ PyObject* obj;
+ if (!PyArg_ParseTuple(args, "O!",&(App::DocumentObjectPy::Type),&obj))
+ return NULL; // NULL triggers exception
+
+ try{
+ App::DocumentObjectPy* dobjpy = static_cast(obj);
+ App::DocumentObject* dobj = dobjpy->getDocumentObjectPtr();
+ if (! dobj->isDerivedFrom(Part::AttachableObject::getClassTypeId())){
+ throw Py::TypeError("Supplied object isn't Part::AttachableObject");
+ }
+ Part::AttachableObject* feat = static_cast(dobj);
+ const AttachEngine &attacher = *(this->getAttachEnginePtr());
+ AttachEngine::verifyReferencesAreSafe(attacher.references);
+ feat->Support.Paste(attacher.references);
+ feat->MapMode.setValue(attacher.mapMode);
+ feat->MapReversed.setValue(attacher.mapReverse);
+ feat->MapPathParameter.setValue(attacher.attachParameter);
+ feat->superPlacement.setValue(attacher.superPlacement);
+ return Py::new_reference_to(Py::None());
+ } ATTACHERPY_STDCATCH_METH;
+}
+
+PyObject* AttachEnginePy::getCustomAttributes(const char*) const
+{
+ return 0;
+}
+
+int AttachEnginePy::setCustomAttributes(const char*,PyObject*)
+{
+ return 0;
+}
+
diff --git a/src/Mod/Part/App/AttachableObject.cpp b/src/Mod/Part/App/AttachableObject.cpp
index b1e615516b..52fff96c97 100644
--- a/src/Mod/Part/App/AttachableObject.cpp
+++ b/src/Mod/Part/App/AttachableObject.cpp
@@ -30,6 +30,8 @@
#include
#include
+#include
+#include "AttachableObjectPy.h"
using namespace Part;
@@ -40,6 +42,9 @@ PROPERTY_SOURCE(Part::AttachableObject, Part::Feature);
AttachableObject::AttachableObject()
: _attacher(0)
{
+ ADD_PROPERTY_TYPE(AttacherType, ("Attacher::AttachEngine3D"), "Attachment",(App::PropertyType)(App::Prop_None),"Class name of attach engine object driving the attachment.");
+ this->AttacherType.setStatus(App::Property::Status::Hidden, true);
+
ADD_PROPERTY_TYPE(Support, (0,0), "Attachment",(App::PropertyType)(App::Prop_None),"Support of the 2D geometry");
ADD_PROPERTY_TYPE(MapMode, (mmDeactivated), "Attachment", App::Prop_None, "Mode of attachment to other object");
@@ -67,7 +72,44 @@ void AttachableObject::setAttacher(AttachEngine* attacher)
if (_attacher)
delete _attacher;
_attacher = attacher;
- updateAttacherVals();
+ if (_attacher){
+ const char* typeName = attacher->getTypeId().getName();
+ if(strcmp(this->AttacherType.getValue(),typeName)!=0) //make sure we need to change, to break recursive onChange->changeAttacherType->onChange...
+ this->AttacherType.setValue(typeName);
+ updateAttacherVals();
+ } else {
+ if (strlen(AttacherType.getValue()) != 0){ //make sure we need to change, to break recursive onChange->changeAttacherType->onChange...
+ this->AttacherType.setValue("");
+ }
+ }
+}
+
+bool AttachableObject::changeAttacherType(const char* typeName)
+{
+ //check if we need to actually change anything
+ if (_attacher){
+ if (strcmp(_attacher->getTypeId().getName(),typeName)==0){
+ return false;
+ }
+ } else if (strlen(typeName) == 0){
+ return false;
+ }
+ if (strlen(typeName) == 0){
+ setAttacher(nullptr);
+ return true;
+ }
+ Base::Type t = Base::Type::fromName(typeName);
+ if (t.isDerivedFrom(AttachEngine::getClassTypeId())){
+ AttachEngine* pNewAttacher = static_cast(Base::Type::createInstanceByName(typeName));
+ this->setAttacher(pNewAttacher);
+ return true;
+ } else {
+ std::stringstream errMsg;
+ errMsg << "Object if this type is not derived from AttachEngine: " << typeName;
+ throw Base::Exception(errMsg.str());
+ }
+ assert(false);//exec shouldn't ever get here
+ return false;
}
bool AttachableObject::positionBySupport()
@@ -136,6 +178,10 @@ void AttachableObject::onChanged(const App::Property* prop)
}
+ if(prop == &(this->AttacherType)){
+ this->changeAttacherType(this->AttacherType.getValue());
+ }
+
Part::Feature::onChanged(prop);
}
@@ -151,4 +197,22 @@ void AttachableObject::updateAttacherVals()
this->superPlacement.getValue());
}
+namespace App {
+/// @cond DOXERR
+ PROPERTY_SOURCE_TEMPLATE(Part::AttachableObjectPython, Part::AttachableObject)
+ template<> const char* Part::AttachableObjectPython::getViewProviderName(void) const {
+ return "PartGui::ViewProviderPython";
+ }
+ template<> PyObject* Part::AttachableObjectPython::getPyObject(void) {
+ if (PythonObject.is(Py::_None())) {
+ // ref counter is set to 1
+ PythonObject = Py::Object(new FeaturePythonPyT(this),true);
+ }
+ return Py::new_reference_to(PythonObject);
+ }
+/// @endcond
+
+// explicit template instantiation
+ template class PartExport FeaturePythonT;
+}
diff --git a/src/Mod/Part/App/AttachableObject.h b/src/Mod/Part/App/AttachableObject.h
index 0a3044265b..ed68a39584 100644
--- a/src/Mod/Part/App/AttachableObject.h
+++ b/src/Mod/Part/App/AttachableObject.h
@@ -59,7 +59,7 @@ class PartExport AttachableObject : public Part::Feature
PROPERTY_HEADER(Part::AttachableObject);
public:
AttachableObject();
- ~AttachableObject();
+ virtual ~AttachableObject();
/**
* @brief setAttacher sets the AttachEngine object. The class takes the
@@ -68,9 +68,20 @@ public:
* @param attacher. AttachableObject takes ownership and will delete it eventually.
*/
virtual void setAttacher(Attacher::AttachEngine* attacher);
- Attacher::AttachEngine &attacher(void) const {return *_attacher;}
- /// if the 2DObject lies on the Face of an other object this links to it
+ /**
+ * @brief changeAttacherType
+ * @param typeName is the typename of new attacher class. Must be derived
+ * from Attacher::AttachEngine.
+ * @return true if attacher was changed. false if attacher is already of the
+ * type requested. Throws if invalid type is supplied.
+ */
+ bool changeAttacherType(const char* typeName);
+
+ Attacher::AttachEngine &attacher(void) const {if(!_attacher) throw Base::Exception("AttachableObject: no attacher is set."); return *_attacher;}
+
+
+ App::PropertyString AttacherType;
App::PropertyLinkSubList Support;
App::PropertyEnumeration MapMode; //see AttachEngine::eMapMode
App::PropertyBool MapReversed; //inverts Z and X internal axes
@@ -104,6 +115,8 @@ private:
};
+typedef App::FeaturePythonT AttachableObjectPython;
+
} // namespace Part
#endif // PARTATTACHABLEOBJECT_H
diff --git a/src/Mod/Part/App/AttachableObjectPy.xml b/src/Mod/Part/App/AttachableObjectPy.xml
new file mode 100644
index 0000000000..89a75d45ef
--- /dev/null
+++ b/src/Mod/Part/App/AttachableObjectPy.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+ This object represents an attachable object with OCC shape.
+
+
+
+ positionBySupport(): Reposition object based on Support, MapMode and MapPathParameter properties.
+Returns True if attachment calculation was successful, false if object is not attached and Placement wasn't updated,
+and raises an exception if attachment calculation fails.
+
+
+
+
+ changeAttacherType(typename): Changes Attacher class of this object.
+typename: string. The following are accepted so far:
+'Attacher::AttachEngine3D'
+'Attacher::AttachEnginePlane'
+'Attacher::AttachEngineLine'
+'Attacher::AttachEnginePoint'
+
+
+
+
+
+ AttachEngine object driving this AttachableObject. Returns a copy.
+
+
+
+
+
diff --git a/src/Mod/Part/App/AttachableObjectPyImp.cpp b/src/Mod/Part/App/AttachableObjectPyImp.cpp
new file mode 100644
index 0000000000..aa45e065b5
--- /dev/null
+++ b/src/Mod/Part/App/AttachableObjectPyImp.cpp
@@ -0,0 +1,87 @@
+
+#include "PreCompiled.h"
+
+#include "Mod/Part/App/AttachableObject.h"
+#include "OCCError.h"
+
+#include "AttachEnginePy.h"
+
+// inclusion of the generated files (generated out of AttachableObjectPy.xml)
+#include "AttachableObjectPy.h"
+#include "AttachableObjectPy.cpp"
+
+using namespace Part;
+
+// returns a string which represents the object e.g. when printed in python
+std::string AttachableObjectPy::representation(void) const
+{
+ return std::string("");
+}
+
+PyObject* AttachableObjectPy::positionBySupport(PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return 0;
+ bool bAttached = false;
+ try{
+ bAttached = this->getAttachableObjectPtr()->positionBySupport();
+ } catch (Standard_Failure) {
+ Handle_Standard_Failure e = Standard_Failure::Caught();
+ PyErr_SetString(PartExceptionOCCError, e->GetMessageString());
+ return NULL;
+ } catch (Base::Exception &e) {
+ PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
+ return NULL;
+ }
+ return Py::new_reference_to(Py::Boolean(bAttached));
+}
+
+PyObject* AttachableObjectPy::changeAttacherType(PyObject *args)
+{
+ const char* typeName;
+ if (!PyArg_ParseTuple(args, "s", &typeName))
+ return 0;
+ bool ret;
+ try{
+ ret = this->getAttachableObjectPtr()->changeAttacherType(typeName);
+ } catch (Standard_Failure) {
+ Handle_Standard_Failure e = Standard_Failure::Caught();
+ PyErr_SetString(PartExceptionOCCError, e->GetMessageString());
+ return NULL;
+ } catch (Base::Exception &e) {
+ PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
+ return NULL;
+ }
+ return Py::new_reference_to(Py::Boolean(ret));
+}
+
+Py::Object AttachableObjectPy::getAttacher(void) const
+{
+ try {
+ this->getAttachableObjectPtr()->attacher(); //throws if attacher is not set
+ } catch (Base::Exception) {
+ return Py::None();
+ }
+
+ try {
+ return Py::Object( new Attacher::AttachEnginePy(this->getAttachableObjectPtr()->attacher().copy()), true);
+ } catch (Standard_Failure) {
+ Handle_Standard_Failure e = Standard_Failure::Caught();
+ throw Py::Exception(Part::PartExceptionOCCError, e->GetMessageString());
+ } catch (Base::Exception &e) {
+ throw Py::Exception(Base::BaseExceptionFreeCADError, e.what());
+ }
+
+}
+
+PyObject *AttachableObjectPy::getCustomAttributes(const char* /*attr*/) const
+{
+ return 0;
+}
+
+int AttachableObjectPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
+{
+ return 0;
+}
+
+
diff --git a/src/Mod/Part/App/Attacher.cpp b/src/Mod/Part/App/Attacher.cpp
index 2ec738a329..9fbbc085dd 100644
--- a/src/Mod/Part/App/Attacher.cpp
+++ b/src/Mod/Part/App/Attacher.cpp
@@ -61,6 +61,8 @@
#include "Attacher.h"
#include
#include
+#include
+#include
using namespace Part;
using namespace Attacher;
@@ -120,10 +122,44 @@ const char* AttachEngine::eMapModeStrings[]= {
NULL};
+//this list must be in sync with eRefType enum.
+//These strings are used only by Py interface of Attacher. Strings for use in Gui are in Mod/Part/Gui/AttacherTexts.cpp
+const char* AttachEngine::eRefTypeStrings[]= {
+ "Any",
+ "Vertex",
+ "Edge",
+ "Face",
+
+ "Line",
+ "Curve",
+ "Circle",
+ "Conic",
+ "Ellipse",
+ "Parabola",
+ "Hyperbola",
+
+ "Plane",
+ "Sphere",
+ "Revolve",
+ "Cylinder",
+ "Torus",
+ "Cone",
+
+ "Object",
+ "Solid",
+ "Wire",
+ NULL
+};
+
+
+
+
TYPESYSTEM_SOURCE_ABSTRACT(Attacher::AttachEngine, Base::BaseClass);
AttachEngine::AttachEngine()
+ : mapReverse(false), attachParameter(0.0), mapMode(mmDeactivated),
+ surfU(0.0), surfV(0.0)
{
}
@@ -576,6 +612,57 @@ std::string AttachEngine::getModeName(eMapMode mmode)
return std::string(AttachEngine::eMapModeStrings[mmode]);
}
+eMapMode AttachEngine::getModeByName(const std::string &modeName)
+{
+ for (int mmode = 0 ; mmode < mmDummy_NumberOfModes ; mmode++){
+ if (strcmp(eMapModeStrings[mmode],modeName.c_str())==0) {
+ return eMapMode(mmode);
+ }
+ }
+ std::stringstream errMsg;
+ errMsg << "AttachEngine::getModeByName: mode with this name doesn't exist: " << modeName;
+ throw Base::Exception(errMsg.str());
+}
+
+std::string AttachEngine::getRefTypeName(eRefType shapeType)
+{
+ eRefType flagless = eRefType(shapeType & 0xFF);
+ if(flagless < 0 || flagless >= rtDummy_numberOfShapeTypes)
+ throw Base::Exception("eRefType value is out of range");
+ std::string result = std::string(eRefTypeStrings[flagless]);
+ if (shapeType & rtFlagHasPlacement){
+ result.append("|Placement");
+ }
+ return result;
+}
+
+eRefType AttachEngine::getRefTypeByName(const std::string& typeName)
+{
+ std::string flagless;
+ std::string flags;
+ size_t seppos = typeName.find('|');
+ flagless = typeName.substr(0, seppos);
+ if(seppos != std::string::npos ){
+ flags = typeName.substr(seppos+1);
+ }
+ for(int irt = 0 ; irt < rtDummy_numberOfShapeTypes ; irt++){
+ if(strcmp(flagless.c_str(),eRefTypeStrings[irt]) == 0){
+ if(strcmp("Placement",flags.c_str()) == 0){
+ return eRefType(irt | rtFlagHasPlacement);
+ } else if (flags.length() == 0){
+ return eRefType(irt);
+ } else {
+ std::stringstream errmsg;
+ errmsg << "RefType flag not recognized: " << flags;
+ throw Base::Exception(errmsg.str());
+ }
+ }
+ }
+ std::stringstream errmsg;
+ errmsg << "RefType not recognized: " << typeName;
+ throw Base::Exception(errmsg.str());
+}
+
GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &shapes)
{
//explode compounds
@@ -638,7 +725,7 @@ GProp_GProps AttachEngine::getInertialPropsOfShape(const std::vector &storage,
std::vector &types)
{
+ verifyReferencesAreSafe(references);
const std::vector &objs = references.getValues();
const std::vector &sub = references.getSubValues();
geofs.resize(objs.size());
@@ -745,6 +833,23 @@ void AttachEngine::throwWrongMode(eMapMode mmode)
throw Base::Exception(errmsg.str().c_str());
}
+void AttachEngine::verifyReferencesAreSafe(const App::PropertyLinkSubList &references)
+{
+ const std::vector links = references.getValues();
+ const std::vector docs = App::GetApplication().getDocuments();
+ for(App::DocumentObject* lnk : links){
+ bool found = false;
+ for(App::Document* doc : docs){
+ if(doc->isIn(lnk)){
+ found = true;
+ }
+ }
+ if (!found){
+ throw Base::Exception("AttachEngine: verifyReferencesAreSafe: references point to deleted object.");
+ }
+ }
+}
+
//=================================================================================
diff --git a/src/Mod/Part/App/Attacher.h b/src/Mod/Part/App/Attacher.h
index d2eb8b22c5..b2d22b637c 100644
--- a/src/Mod/Part/App/Attacher.h
+++ b/src/Mod/Part/App/Attacher.h
@@ -105,7 +105,8 @@ enum eMapMode {
/**
* @brief The eRefType enum lists the types of references. If adding one, see
- * also AttachEngine::getShapeType(), AttachEngine::downgradeType(), AttacherTexts.cpp/getShTypeText()
+ * also AttachEngine::eRefTypeStrings, AttachEngine::getShapeType(),
+ * AttachEngine::downgradeType(), AttacherTexts.cpp/getShTypeText()
*/
enum eRefType {
//topo //ranks: (number of times the type is downgradable)
@@ -333,11 +334,24 @@ public://helper functions that may be useful outside of the class
*/
static std::string getModeName(eMapMode mmode);
+ static eMapMode getModeByName(const std::string &modeName);
+
+ static std::string getRefTypeName(eRefType shapeType);
+
+ static eRefType getRefTypeByName(const std::string &typeName);
+
static GProp_GProps getInertialPropsOfShape(const std::vector &shapes);
+ /**
+ * @brief verifyReferencesAreSafe: checks if pointers in references still
+ * point to objects contained in open documents. This guarantees the links
+ * are valid. Throws Base::Exception if invalid links are found.
+ */
+ static void verifyReferencesAreSafe(const App::PropertyLinkSubList& references);
public: //enums
static const char* eMapModeStrings[];
+ static const char* eRefTypeStrings[];
public: //members
diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt
index a223b4d2d6..333557ad60 100644
--- a/src/Mod/Part/App/CMakeLists.txt
+++ b/src/Mod/Part/App/CMakeLists.txt
@@ -69,7 +69,9 @@ generate_from_xml(RectangularTrimmedSurfacePy)
generate_from_xml(SurfaceOfExtrusionPy)
generate_from_xml(SurfaceOfRevolutionPy)
generate_from_xml(PartFeaturePy)
+generate_from_xml(AttachableObjectPy)
generate_from_xml(Part2DObjectPy)
+generate_from_xml(AttachEnginePy)
generate_from_xml(TopoShapePy)
generate_from_xml(TopoShapeCompoundPy)
generate_from_xml(TopoShapeCompSolidPy)
@@ -216,8 +218,12 @@ SET(Python_SRCS
SurfaceOfRevolutionPyImp.cpp
PartFeaturePy.xml
PartFeaturePyImp.cpp
+ AttachableObjectPy.xml
+ AttachableObjectPyImp.cpp
Part2DObjectPy.xml
Part2DObjectPyImp.cpp
+ AttachEnginePy.xml
+ AttachEnginePyImp.cpp
TopoShapePy.xml
TopoShapePyImp.cpp
TopoShapeCompSolidPy.xml
diff --git a/src/Mod/Part/App/Part2DObject.cpp b/src/Mod/Part/App/Part2DObject.cpp
index c02b930a08..84ffd87269 100644
--- a/src/Mod/Part/App/Part2DObject.cpp
+++ b/src/Mod/Part/App/Part2DObject.cpp
@@ -66,7 +66,7 @@ PROPERTY_SOURCE(Part::Part2DObject, Part::AttachableObject)
Part2DObject::Part2DObject()
{
- this->setAttacher(new Attacher::AttachEngine3D);
+ this->setAttacher(new Attacher::AttachEnginePlane);
}
diff --git a/src/Mod/Part/App/Part2DObjectPy.xml b/src/Mod/Part/App/Part2DObjectPy.xml
index 4698128f17..299bcd84d9 100644
--- a/src/Mod/Part/App/Part2DObjectPy.xml
+++ b/src/Mod/Part/App/Part2DObjectPy.xml
@@ -1,24 +1,17 @@
This object represents a 2D Shape in a 3D World
-
-
- positionBySupport(): Reposition object based on Support, MapMode and MapPathParameter properties.
-Returns True if attachment calculation was successful, false if object is not attached and Placement wasn't updated,
-and raises an exception if attachment calculation fails.
-
-
diff --git a/src/Mod/Part/App/Part2DObjectPyImp.cpp b/src/Mod/Part/App/Part2DObjectPyImp.cpp
index ed5a01b2c0..795e5aba45 100644
--- a/src/Mod/Part/App/Part2DObjectPyImp.cpp
+++ b/src/Mod/Part/App/Part2DObjectPyImp.cpp
@@ -16,23 +16,6 @@ std::string Part2DObjectPy::representation(void) const
return std::string("");
}
-PyObject* Part2DObjectPy::positionBySupport(PyObject *args)
-{
- if (!PyArg_ParseTuple(args, ""))
- return 0;
- bool bAttached = false;
- try{
- bAttached = this->getPart2DObjectPtr()->positionBySupport();
- } catch (Standard_Failure) {
- Handle_Standard_Failure e = Standard_Failure::Caught();
- PyErr_SetString(PartExceptionOCCError, e->GetMessageString());
- return NULL;
- } catch (Base::Exception &e) {
- PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
- return NULL;
- }
- return Py::new_reference_to(Py::Boolean(bAttached));
-}
PyObject *Part2DObjectPy::getCustomAttributes(const char* /*attr*/) const
diff --git a/src/Mod/Part/App/PreCompiled.h b/src/Mod/Part/App/PreCompiled.h
index 67c8bc391e..ad0c3508f7 100644
--- a/src/Mod/Part/App/PreCompiled.h
+++ b/src/Mod/Part/App/PreCompiled.h
@@ -34,6 +34,9 @@
# define PartExport
#endif
+//needed to make autogenerated Py interface source code to compile
+#define AttacherExport PartExport
+
// here get the warnings of too long specifiers disabled (needed for VC6)
#ifdef _MSC_VER
# pragma warning( disable : 4251 )
diff --git a/src/Mod/Part/Gui/AppPartGui.cpp b/src/Mod/Part/Gui/AppPartGui.cpp
index ec119b937e..c9913200c5 100644
--- a/src/Mod/Part/Gui/AppPartGui.cpp
+++ b/src/Mod/Part/Gui/AppPartGui.cpp
@@ -28,6 +28,7 @@
#include
+#include "AttacherTexts.h"
#include "SoBrepFaceSet.h"
#include "SoBrepEdgeSet.h"
#include "SoBrepPointSet.h"
@@ -117,9 +118,15 @@ PyMODINIT_FUNC initPartGui()
return;
}
- (void)PartGui::initModule();
+ PyObject* partGuiModule = PartGui::initModule();
Base::Console().Log("Loading GUI of Part module... done\n");
+ PyObject* pAttachEngineTextsModule = Py_InitModule3("AttachEngineResources", AttacherGui::AttacherGuiPy::Methods,
+ "AttachEngine Gui resources");
+ Py_INCREF(pAttachEngineTextsModule);
+ PyModule_AddObject(partGuiModule, "AttachEngineResources", pAttachEngineTextsModule);
+
+
PartGui::SoBrepFaceSet ::initClass();
PartGui::SoBrepEdgeSet ::initClass();
PartGui::SoBrepPointSet ::initClass();
diff --git a/src/Mod/Part/Gui/AttacherTexts.cpp b/src/Mod/Part/Gui/AttacherTexts.cpp
index 742b74c484..8ffc1a5e3f 100644
--- a/src/Mod/Part/Gui/AttacherTexts.cpp
+++ b/src/Mod/Part/Gui/AttacherTexts.cpp
@@ -25,6 +25,8 @@
# include
#endif
#include "AttacherTexts.h"
+#include
+#include
using namespace Attacher;
@@ -262,7 +264,7 @@ TextSet getUIStrings(Base::Type attacherType, eMapMode mmode)
}
}
- assert(false && "No user-friendly string defined for this attachment mode.");
+ Base::Console().Warning("No user-friendly string defined for this attachment mode and attacher type: %s %s \n",AttachEngine::getModeName(mmode).c_str(), attacherType.getName());
return TwoStrings(QString::fromStdString(AttachEngine::getModeName(mmode)), QString());
}
@@ -325,4 +327,64 @@ QStringList getRefListForMode(AttachEngine &attacher, eMapMode mmode)
return strlist;
}
+
+// --------------------Py interface---------------------
+
+PyObject* AttacherGuiPy::sGetModeStrings(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/)
+{
+ int modeIndex = 0;
+ char* attacherType;
+ if (!PyArg_ParseTuple(args, "si", &attacherType, &modeIndex))
+ return NULL;
+
+ try {
+ Base::Type t = Base::Type::fromName(attacherType);
+ if (! t.isDerivedFrom(AttachEngine::getClassTypeId())){
+ std::stringstream ss;
+ ss << "Object of this type is not derived from AttachEngine: ";
+ ss << attacherType;
+ throw Py::TypeError(ss.str());
+ }
+ TextSet strs = getUIStrings(t,eMapMode(modeIndex));
+ Py::List result;
+ for(QString &s : strs){
+ QByteArray ba_utf8 = s.toUtf8();
+ result.append(Py::String(ba_utf8.data(), "utf-8"));
+ }
+
+ return Py::new_reference_to(result);
+ } catch (const Py::Exception&) {
+ return 0;
+ } catch (const Base::Exception& e) {
+ PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
+ return 0;
+ }
+}
+
+PyObject* AttacherGuiPy::sGetRefTypeUserFriendlyName(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/)
+{
+ int refTypeIndex = 0;
+ if (!PyArg_ParseTuple(args, "i", &refTypeIndex))
+ return NULL;
+
+ try {
+ QByteArray ba_utf8 = getShapeTypeText(eRefType(refTypeIndex)).toUtf8();
+ return Py::new_reference_to(Py::String(ba_utf8.data(), "utf-8"));
+ } catch (const Py::Exception&) {
+ return 0;
+ } catch (const Base::Exception& e) {
+ PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
+ return 0;
+ }
+}
+
+
+PyMethodDef AttacherGuiPy::Methods[] = {
+ {"getModeStrings", (PyCFunction) AttacherGuiPy::sGetModeStrings, 1,
+ "getModeStrings(attacher_type, mode_index) - gets mode user-friendly name and brief description."},
+ {"getRefTypeUserFriendlyName", (PyCFunction) AttacherGuiPy::sGetRefTypeUserFriendlyName, 1,
+ "getRefTypeUserFriendlyName(type_index) - gets user-friendly name of AttachEngine's shape type."},
+};
+
+
} //namespace AttacherGui
diff --git a/src/Mod/Part/Gui/AttacherTexts.h b/src/Mod/Part/Gui/AttacherTexts.h
index 91bdee8bd3..903f604b17 100644
--- a/src/Mod/Part/Gui/AttacherTexts.h
+++ b/src/Mod/Part/Gui/AttacherTexts.h
@@ -33,8 +33,10 @@
#include
#include
#include
+#include
#include
+
namespace AttacherGui {
typedef std::vector TextSet;
@@ -54,6 +56,14 @@ QString PartGuiExport getShapeTypeText(Attacher::eRefType type);
QStringList PartGuiExport getRefListForMode(Attacher::AttachEngine &attacher, Attacher::eMapMode mmode);
-}
+// Python interface
+class PartGuiExport AttacherGuiPy{
+public:
+ static PyMethodDef Methods[];
+ static PyObject* sGetModeStrings(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/);
+ static PyObject* sGetRefTypeUserFriendlyName(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/);
+};
+
+}
#endif