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