diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 0c6fceb43f..3cd42a548b 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -444,13 +444,14 @@ void Document::exportGraphviz(std::ostream& out) const // Create subgraphs for all documentobjects that it depends on; it will depend on some property there auto i = expressions.begin(); while (i != expressions.end()) { - std::set deps; + std::map deps; i->second->getIdentifiers(deps); - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - DocumentObject * o = j->getDocumentObject(); + for(auto j=deps.begin(); j!=deps.end(); ++j) { + if(j->second) + continue; + DocumentObject * o = j->first.getDocumentObject(); // Doesn't exist already? if (o && !GraphList[o]) { @@ -469,7 +470,6 @@ void Document::exportGraphviz(std::ostream& out) const } } - ++j; } ++i; } @@ -554,24 +554,23 @@ void Document::exportGraphviz(std::ostream& out) const while (i != expressions.end()) { // Get dependencies - std::set deps; + std::map deps; i->second->getIdentifiers(deps); // Create subgraphs for all documentobjects that it depends on; it will depend on some property there - std::set::const_iterator j = deps.begin(); - while (j != deps.end()) { - DocumentObject * depObjDoc = j->getDocumentObject(); - std::map::const_iterator k = GlobalVertexList.find(getId(*j)); + for(auto j=deps.begin(); j!=deps.end(); ++j) { + if(j->second) + continue; + DocumentObject * depObjDoc = j->first.getDocumentObject(); + std::map::const_iterator k = GlobalVertexList.find(getId(j->first)); if (k == GlobalVertexList.end()) { Graph * depSgraph = GraphList[depObjDoc] ? GraphList[depObjDoc] : &DepList; - LocalVertexList[getId(*j)] = add_vertex(*depSgraph); - GlobalVertexList[getId(*j)] = vertex_no++; - setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(*j)], j->getPropertyName() + j->getSubPathStr()); + LocalVertexList[getId(j->first)] = add_vertex(*depSgraph); + GlobalVertexList[getId(j->first)] = vertex_no++; + setPropertyVertexAttributes(*depSgraph, LocalVertexList[getId(j->first)], j->first.getPropertyName() + j->first.getSubPathStr()); } - - ++j; } ++i; } @@ -703,17 +702,18 @@ void Document::exportGraphviz(std::ostream& out) const auto i = expressions.begin(); while (i != expressions.end()) { - std::set deps; + std::map deps; i->second->getIdentifiers(deps); // Create subgraphs for all documentobjects that it depends on; it will depend on some property there - std::set::const_iterator k = deps.begin(); - while (k != deps.end()) { - DocumentObject * depObjDoc = k->getDocumentObject(); + for(auto k=deps.begin(); k!=deps.end(); ++k) { + if(k->second) + continue; + DocumentObject * depObjDoc = k->first.getDocumentObject(); Edge edge; bool inserted; - tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(*k)], DepList); + tie(edge, inserted) = add_edge(GlobalVertexList[getId(i->first)], GlobalVertexList[getId(k->first)], DepList); // Add this edge to the set of all expression generated edges existingEdges.insert(std::make_pair(docObj, depObjDoc)); @@ -721,7 +721,6 @@ void Document::exportGraphviz(std::ostream& out) const // Edges between properties should be a bit smaller, and dashed edgeAttrMap[edge]["arrowsize"] = "0.5"; edgeAttrMap[edge]["style"] = "dashed"; - ++k; } ++i; } diff --git a/src/App/DynamicProperty.cpp b/src/App/DynamicProperty.cpp index 925783850c..5851814b34 100644 --- a/src/App/DynamicProperty.cpp +++ b/src/App/DynamicProperty.cpp @@ -297,6 +297,18 @@ DynamicProperty::PropData DynamicProperty::getDynamicPropertyData(const Property return PropData(); } +bool DynamicProperty::changeDynamicProperty(const Property *prop, const char *group, const char *doc) { + auto &index = props.get<1>(); + auto it = index.find(const_cast(prop)); + if (it == index.end()) + return false; + if(group) + it->group = group; + if(doc) + it->doc = doc; + return true; +} + const char *DynamicProperty::getPropertyName(const Property *prop) const { auto &index = props.get<1>(); diff --git a/src/App/DynamicProperty.h b/src/App/DynamicProperty.h index 760bfdc834..ba09a48568 100644 --- a/src/App/DynamicProperty.h +++ b/src/App/DynamicProperty.h @@ -149,8 +149,8 @@ public: Property* property; std::string name; const char *pName; - std::string group; - std::string doc; + mutable std::string group; + mutable std::string doc; short attr; bool readonly; bool hidden; @@ -168,6 +168,8 @@ public: PropData getDynamicPropertyData(const Property* prop) const; + bool changeDynamicProperty(const Property *prop, const char *group, const char *doc); + private: std::string getUniquePropertyName(PropertyContainer &pc, const char *Name) const; diff --git a/src/App/Enumeration.cpp b/src/App/Enumeration.cpp index bda0899206..7a8a1b8643 100644 --- a/src/App/Enumeration.cpp +++ b/src/App/Enumeration.cpp @@ -38,6 +38,7 @@ Enumeration::Enumeration() } Enumeration::Enumeration(const Enumeration &other) + : _EnumArray(NULL), _ownEnumArray(false), _index(0), _maxVal(-1) { if (other._ownEnumArray) { setEnums(other.getEnumVector()); @@ -81,11 +82,9 @@ Enumeration::~Enumeration() void Enumeration::tearDown(void) { // Ugly... - char **plEnums = (char **)_EnumArray; - - // Delete C Strings first - while (*plEnums != NULL) { - free(*(plEnums++)); + for(char **plEnums = (char **)_EnumArray; *plEnums != NULL; ++plEnums) { + // Delete C Strings first + free(*plEnums); } delete [] _EnumArray; @@ -97,6 +96,9 @@ void Enumeration::tearDown(void) void Enumeration::setEnums(const char **plEnums) { + if(plEnums == _EnumArray) + return; + std::string oldValue; bool preserve = (isValid() && plEnums != NULL); if (preserve) { @@ -117,7 +119,11 @@ void Enumeration::setEnums(const char **plEnums) findMaxVal(); // set _index - _index = 0; + if (_index < 0) + _index = 0; + else if (_index > _maxVal) + _index = _maxVal; + if (preserve) { setValue(oldValue); } @@ -310,6 +316,10 @@ Enumeration & Enumeration::operator=(const Enumeration &other) bool Enumeration::operator==(const Enumeration &other) const { + if(_index != other._index) + return false; + if (getCStr() == other.getCStr()) + return true; if (getCStr() == NULL || other.getCStr() == NULL) { return false; } diff --git a/src/App/Expression.cpp b/src/App/Expression.cpp index d7d69884fc..eb324cb0a5 100644 --- a/src/App/Expression.cpp +++ b/src/App/Expression.cpp @@ -240,17 +240,7 @@ std::string unquote(const std::string & input) // // ExpressionVistor // -void ExpressionVisitor::getDeps(Expression &e, ExpressionDeps &deps) { - e._getDeps(deps); -} - -void ExpressionVisitor::getDepObjects(Expression &e, - std::set &deps, std::vector *labels) -{ - e._getDepObjects(deps,labels); -} - -void ExpressionVisitor::getIdentifiers(Expression &e, std::set &ids) { +void ExpressionVisitor::getIdentifiers(Expression &e, std::map &ids) { e._getIdentifiers(ids); } @@ -905,58 +895,52 @@ Expression * Expression::parse(const DocumentObject *owner, const std::string &b return ExpressionParser::parse(owner, buffer.c_str()); } -class GetDepsExpressionVisitor : public ExpressionVisitor { -public: - GetDepsExpressionVisitor(ExpressionDeps &deps) - :deps(deps) - {} - - virtual void visit(Expression &e) { - this->getDeps(e,deps); +void Expression::getDeps(ExpressionDeps &deps, int option) const { + for(auto &v : getIdentifiers()) { + bool hidden = v.second; + const ObjectIdentifier &var = v.first; + if((hidden && option==DepNormal) + || (!hidden && option==DepHidden)) + continue; + for(auto &dep : var.getDep(true)) { + DocumentObject *obj = dep.first; + for(auto &propName : dep.second) { + deps[obj][propName].push_back(var); + } + } } - - ExpressionDeps &deps; -}; - -void Expression::getDeps(ExpressionDeps &deps) const { - GetDepsExpressionVisitor v(deps); - const_cast(this)->visit(v); } -ExpressionDeps Expression::getDeps() const { +ExpressionDeps Expression::getDeps(int option) const { ExpressionDeps deps; - getDeps(deps); + getDeps(deps, option); return deps; } -class GetDepObjsExpressionVisitor : public ExpressionVisitor { -public: - GetDepObjsExpressionVisitor(std::set &deps, std::vector *labels) - :deps(deps),labels(labels) - {} - - virtual void visit(Expression &e) { - this->getDepObjects(e,deps,labels); +void Expression::getDepObjects( + std::map &deps, std::vector *labels) const +{ + for(auto &v : getIdentifiers()) { + bool hidden = v.second; + const ObjectIdentifier &var = v.first; + for(auto &dep : var.getDep(false,labels)) { + DocumentObject *obj = dep.first; + auto res = deps.insert(std::make_pair(obj,hidden)); + if(!hidden || res.second) + res.first->second = hidden; + } } - - std::set &deps; - std::vector *labels; -}; - -void Expression::getDepObjects(std::set &deps, std::vector *labels) const { - GetDepObjsExpressionVisitor v(deps,labels); - const_cast(this)->visit(v); } -std::set Expression::getDepObjects(std::vector *labels) const { - std::set deps; +std::map Expression::getDepObjects(std::vector *labels) const { + std::map deps; getDepObjects(deps,labels); return deps; } class GetIdentifiersExpressionVisitor : public ExpressionVisitor { public: - GetIdentifiersExpressionVisitor(std::set &deps) + GetIdentifiersExpressionVisitor(std::map &deps) :deps(deps) {} @@ -964,16 +948,16 @@ public: this->getIdentifiers(e,deps); } - std::set &deps; + std::map &deps; }; -void Expression::getIdentifiers(std::set &deps) const { +void Expression::getIdentifiers(std::map &deps) const { GetIdentifiersExpressionVisitor v(deps); const_cast(this)->visit(v); } -std::set Expression::getIdentifiers() const { - std::set deps; +std::map Expression::getIdentifiers() const { + std::map deps; getIdentifiers(deps); return deps; } @@ -1016,7 +1000,7 @@ ExpressionPtr Expression::importSubNames(const std::map if(!owner || !owner->getDocument()) return 0; ObjectIdentifier::SubNameMap subNameMap; - for(auto &dep : getDeps()) { + for(auto &dep : getDeps(DepAll)) { for(auto &info : dep.second) { for(auto &path : info.second) { auto obj = path.getDocumentObject(); @@ -1064,7 +1048,8 @@ ExpressionPtr Expression::updateLabelReference( if(ref.size()<=2) return ExpressionPtr(); std::vector labels; - getDepObjects(&labels); + for(auto &v : getIdentifiers()) + v.first.getDepLabels(labels); for(auto &label : labels) { // ref contains something like $label. and we need to strip '$' and '.' if(ref.compare(1,ref.size()-2,label)==0) { @@ -1709,6 +1694,32 @@ bool OperatorExpression::isRightAssociative() const TYPESYSTEM_SOURCE(App::FunctionExpression, App::UnitExpression) +static int _HiddenReference; + +struct HiddenReference { + HiddenReference(bool cond) + :cond(cond) + { + if(cond) + ++_HiddenReference; + } + ~HiddenReference() { + if(cond) + --_HiddenReference; + } + + static bool check(int option) { + return (option==Expression::DepNormal && _HiddenReference) + || (option==Expression::DepHidden && !_HiddenReference); + } + + static bool isHidden() { + return _HiddenReference!=0; + } + + bool cond; +}; + FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f, std::string &&name, std::vector _args) : UnitExpression(_owner) , f(_f) @@ -1736,6 +1747,7 @@ FunctionExpression::FunctionExpression(const DocumentObject *_owner, Function _f case FLOOR: case MINVERT: case STR: + case HIDDENREF: if (args.size() != 1) EXPR_THROW("Invalid number of arguments: exactly one required."); break; @@ -2087,6 +2099,8 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std return res; } else if (f == STR) { return Py::String(args[0]->getPyValue().as_string()); + } else if (f == HIDDENREF) { + return args[0]->getPyValue(); } Py::Object e1 = args[0]->getPyValue(); @@ -2219,7 +2233,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std unit = v1.getUnit(); break; default: - _EXPR_THROW("Unknown function: " << f,expr); + _EXPR_THROW("Unknown function: " << f,0); } /* Compute result */ @@ -2299,7 +2313,7 @@ Py::Object FunctionExpression::evaluate(const Expression *expr, int f, const std output = floor(value); break; default: - _EXPR_THROW("Unknown function: " << f,expr); + _EXPR_THROW("Unknown function: " << f,0); } return Py::asObject(new QuantityPy(new Quantity(scaler * output, unit))); @@ -2421,6 +2435,8 @@ void FunctionExpression::_toString(std::ostream &ss, bool persistent,int) const ss << "create("; break;; case STR: ss << "str("; break;; + case HIDDENREF: + ss << "hiddenref("; break;; default: ss << fname << "("; break;; } @@ -2454,6 +2470,7 @@ void FunctionExpression::_visit(ExpressionVisitor &v) { std::vector::const_iterator i = args.begin(); + HiddenReference ref(f == HIDDENREF); while (i != args.end()) { (*i)->visit(v); ++i; @@ -2592,24 +2609,12 @@ Expression *VariableExpression::_copy() const return new VariableExpression(owner, var); } -void VariableExpression::_getDeps(ExpressionDeps &deps) const +void VariableExpression::_getIdentifiers(std::map &deps) const { - auto dep = var.getDep(); - if(dep.first) - deps[dep.first][dep.second].push_back(var); -} - -void VariableExpression::_getDepObjects( - std::set &deps, std::vector *labels) const -{ - auto dep = var.getDep(labels); - if(dep.first) - deps.insert(dep.first); -} - -void VariableExpression::_getIdentifiers(std::set &deps) const -{ - deps.insert(var); + bool hidden = HiddenReference::isHidden(); + auto res = deps.insert(std::make_pair(var,hidden)); + if(!hidden || res.second) + res.first->second = hidden; } bool VariableExpression::_relabeledDocument(const std::string &oldName, @@ -2977,16 +2982,19 @@ Expression *RangeExpression::simplify() const return copy(); } -void RangeExpression::_getDeps(ExpressionDeps &deps) const +void RangeExpression::_getIdentifiers(std::map &deps) const { + bool hidden = HiddenReference::isHidden(); + assert(owner); Range i(getRange()); - auto &dep = deps[owner]; do { - std::string address = i.address(); - dep[address].push_back(ObjectIdentifier(owner,address)); + ObjectIdentifier var(owner,i.address()); + auto res = deps.insert(std::make_pair(var,hidden)); + if(!hidden || res.second) + res.first->second = hidden; } while (i.next()); } @@ -3243,6 +3251,7 @@ static void initParser(const App::DocumentObject *owner) registered_functions["minvert"] = FunctionExpression::MINVERT; registered_functions["create"] = FunctionExpression::CREATE; registered_functions["str"] = FunctionExpression::STR; + registered_functions["hiddenref"] = FunctionExpression::HIDDENREF; // Aggregates registered_functions["sum"] = FunctionExpression::SUM; diff --git a/src/App/Expression.h b/src/App/Expression.h index d0071500b0..8d394db807 100644 --- a/src/App/Expression.h +++ b/src/App/Expression.h @@ -51,7 +51,9 @@ typedef std::unique_ptr ExpressionPtr; AppExport bool isAnyEqual(const App::any &v1, const App::any &v2); AppExport Base::Quantity anyToQuantity(const App::any &value, const char *errmsg = 0); +// Map of depending objects to a map of depending property name to the full referencing object identifier typedef std::map > > ExpressionDeps; + class AppExport ExpressionVisitor { public: virtual ~ExpressionVisitor() {} @@ -62,9 +64,7 @@ public: virtual App::PropertyLinkBase* getPropertyLink() {return 0;} protected: - void getIdentifiers(Expression &e, std::set &); - void getDeps(Expression &e, ExpressionDeps &); - void getDepObjects(Expression &e, std::set &, std::vector *); + void getIdentifiers(Expression &e, std::map &); bool adjustLinks(Expression &e, const std::set &inList); bool relabeledDocument(Expression &e, const std::string &oldName, const std::string &newName); bool renameObjectIdentifier(Expression &e, @@ -135,14 +135,19 @@ public: virtual int priority() const; - void getIdentifiers(std::set &) const; - std::set getIdentifiers() const; + void getIdentifiers(std::map &) const; + std::map getIdentifiers() const; - void getDeps(ExpressionDeps &deps) const; - ExpressionDeps getDeps() const; + enum DepOption { + DepNormal, + DepHidden, + DepAll, + }; + void getDeps(ExpressionDeps &deps, int option=DepNormal) const; + ExpressionDeps getDeps(int option=DepNormal) const; - std::set getDepObjects(std::vector *labels=0) const; - void getDepObjects(std::set &, std::vector *labels=0) const; + std::map getDepObjects(std::vector *labels=0) const; + void getDepObjects(std::map &, std::vector *labels=0) const; ExpressionPtr importSubNames(const std::map &nameMap) const; @@ -188,9 +193,7 @@ protected: virtual bool _isIndexable() const {return false;} virtual Expression *_copy() const = 0; virtual void _toString(std::ostream &ss, bool persistent, int indent=0) const = 0; - virtual void _getDeps(ExpressionDeps &) const {} - virtual void _getDepObjects(std::set &, std::vector *) const {} - virtual void _getIdentifiers(std::set &) const {} + virtual void _getIdentifiers(std::map &) const {} virtual bool _adjustLinks(const std::set &, ExpressionVisitor &) {return false;} virtual bool _updateElementReference(App::DocumentObject *,bool,ExpressionVisitor &) {return false;} virtual bool _relabeledDocument(const std::string &, const std::string &, ExpressionVisitor &) {return false;} diff --git a/src/App/ExpressionParser.h b/src/App/ExpressionParser.h index e0dfe1ee0d..c354216299 100644 --- a/src/App/ExpressionParser.h +++ b/src/App/ExpressionParser.h @@ -273,6 +273,7 @@ public: MINVERT, // invert matrix/placement/rotation CREATE, // create new object of a given type STR, // stringify + HIDDENREF, // hidden reference that has no dependency check // Aggregates AGGREGATES, @@ -345,9 +346,7 @@ protected: virtual Py::Object _getPyValue() const override; virtual void _toString(std::ostream &ss, bool persistent, int indent) const override; virtual bool _isIndexable() const override; - virtual void _getDeps(ExpressionDeps &) const override; - virtual void _getDepObjects(std::set &, std::vector *) const override; - virtual void _getIdentifiers(std::set &) const override; + virtual void _getIdentifiers(std::map &) const override; virtual bool _adjustLinks(const std::set &, ExpressionVisitor &) override; virtual void _importSubNames(const ObjectIdentifier::SubNameMap &) override; virtual void _updateLabelReference(App::DocumentObject *, const std::string &, const char *) override; @@ -435,7 +434,7 @@ protected: virtual Expression * _copy() const override; virtual void _toString(std::ostream &ss, bool persistent, int indent) const override; virtual Py::Object _getPyValue() const override; - virtual void _getDeps(ExpressionDeps &) const override; + virtual void _getIdentifiers(std::map &) const override; virtual bool _renameObjectIdentifier(const std::map &, const ObjectIdentifier &, ExpressionVisitor &) override; virtual void _moveCells(const CellAddress &, int, int, ExpressionVisitor &) override; diff --git a/src/App/ObjectIdentifier.cpp b/src/App/ObjectIdentifier.cpp index 0aebc7f48d..bf0afb7867 100644 --- a/src/App/ObjectIdentifier.cpp +++ b/src/App/ObjectIdentifier.cpp @@ -33,18 +33,19 @@ /// Here the FreeCAD includes sorted by Base,App,Gui...... #include -#include +#include +#include +#include +#include +#include +#include "ComplexGeoData.h" #include "Property.h" #include "Application.h" #include "Document.h" #include "DocumentObject.h" #include "ObjectIdentifier.h" #include "ExpressionParser.h" -#include -#include -#include -#include -#include +#include "Link.h" FC_LOG_LEVEL_INIT("Expression",true,true) @@ -1086,32 +1087,58 @@ enum PseudoPropertyType { PseudoCadquery, }; -std::pair ObjectIdentifier::getDep(std::vector *labels) const { +void ObjectIdentifier::getDepLabels(std::vector &labels) const { + getDepLabels(ResolveResults(*this),labels); +} + +void ObjectIdentifier::getDepLabels( + const ResolveResults &result, std::vector &labels) const +{ + if(documentObjectName.getString().size()) { + if(documentObjectName.isRealString()) + labels.push_back(documentObjectName.getString()); + } else if(result.propertyIndex == 1) + labels.push_back(components[0].name.getString()); + if(subObjectName.getString().size()) + PropertyLinkBase::getLabelReferences(labels,subObjectName.getString().c_str()); +} + +ObjectIdentifier::Dependencies +ObjectIdentifier::getDep(bool needProps, std::vector *labels) const +{ + Dependencies deps; + getDep(deps,needProps,labels); + return deps; +} + +void ObjectIdentifier::getDep( + Dependencies &deps, bool needProps, std::vector *labels) const +{ ResolveResults result(*this); - if(labels) { - if(documentObjectName.getString().size()) { - if(documentObjectName.isRealString()) - labels->push_back(documentObjectName.getString()); - } else if(result.propertyIndex == 1) - labels->push_back(components[0].name.getString()); - if(subObjectName.getString().size()) - PropertyLinkBase::getLabelReferences(*labels,subObjectName.getString().c_str()); + if(labels) + getDepLabels(result,*labels); + + if(!result.resolvedDocumentObject) + return; + + if(!needProps) { + deps[result.resolvedDocumentObject]; + return; } - if(subObjectName.getString().empty()) { - if(result.propertyType==PseudoNone) { - CellAddress addr; - if(addr.parseAbsoluteAddress(result.propertyName.c_str())) - return std::make_pair(result.resolvedDocumentObject,addr.toString(true)); - return std::make_pair(result.resolvedDocumentObject,result.propertyName); - }else if(result.propertyType == PseudoSelf - && result.resolvedDocumentObject - && result.propertyIndex+1 < (int)components.size()) - { - return std::make_pair(result.resolvedDocumentObject, - components[result.propertyIndex+1].getName()); - } + + if(!result.resolvedProperty) { + if(result.propertyName.size()) + deps[result.resolvedDocumentObject].insert(result.propertyName); + return; + } + + Base::PyGILStateLocker lock; + try { + access(result,0,&deps); + }catch(Py::Exception &) { + Base::PyException e; + }catch(...){ } - return std::make_pair(result.resolvedDocumentObject,std::string()); } /** @@ -1241,7 +1268,7 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj, if(!obj) return 0; - static std::map _props = { + static std::unordered_map _props = { {"_shape",PseudoShape}, {"_pla",PseudoPlacement}, {"_matrix",PseudoMatrix}, @@ -1270,23 +1297,8 @@ Property *ObjectIdentifier::resolveProperty(const App::DocumentObject *obj, } return &const_cast(obj)->Label; //fake the property } - - auto prop = obj->getPropertyByName(propertyName); - if(prop && !prop->testStatus(Property::Hidden) && !(prop->getType() & PropertyType::Prop_Hidden)) - return prop; - - auto linked = obj->getLinkedObject(true); - if(!linked || linked==obj) { - auto ext = obj->getExtensionByType(true); - if(!ext) - return prop; - linked = ext->getTrueLinkedObject(true); - if(!linked || linked==obj) - return prop; - } - - auto linkedProp = linked->getPropertyByName(propertyName); - return linkedProp?linkedProp:prop; + + return obj->getPropertyByName(propertyName); } @@ -1503,7 +1515,8 @@ void ObjectIdentifier::String::checkImport(const App::DocumentObject *owner, } } -Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *value) const +Py::Object ObjectIdentifier::access(const ResolveResults &result, + Py::Object *value, Dependencies *deps) const { if(!result.resolvedDocumentObject || !result.resolvedProperty || (subObjectName.getString().size() && !result.resolvedSubObject)) @@ -1647,13 +1660,65 @@ Py::Object ObjectIdentifier::access(const ResolveResults &result, Py::Object *va break; }}} } + + auto setPropDep = [deps](DocumentObject *obj, Property *prop, const char *propName) { + if(!deps || !obj) + return; + if(prop && prop->getContainer()!=obj) { + auto linkTouched = Base::freecad_dynamic_cast( + obj->getPropertyByName("_LinkTouched")); + if(linkTouched) + propName = linkTouched->getName(); + else { + auto propOwner = Base::freecad_dynamic_cast(prop->getContainer()); + if(propOwner) + obj = propOwner; + else + propName = 0; + } + } + auto &propset = (*deps)[obj]; + // inserting a null name in the propset indicates the dependency is + // on all properties of the corresponding object. + if(propset.size()!=1 || propset.begin()->size()) { + if(!propName) + propset.clear(); + propset.insert(propName); + } + return; + }; + + App::DocumentObject *lastObj = result.resolvedDocumentObject; + if(result.resolvedSubObject) { + setPropDep(lastObj,0,0); + lastObj = result.resolvedSubObject; + } + if(ptype == PseudoNone) + setPropDep(lastObj, result.resolvedProperty, result.resolvedProperty->getName()); + else + setPropDep(lastObj,0,0); + lastObj = 0; + if(components.empty()) return pyobj; + size_t count = components.size(); if(value) --count; assert(idx<=count); - for(;idx(*pyobj)->getDocumentObjectPtr(); + else if(lastObj) { + const char *attr = components[idx].getName().c_str(); + auto prop = lastObj->getPropertyByName(attr); + if(!prop && pyobj.hasAttr(attr)) + attr = 0; + setPropDep(lastObj,prop,attr); + lastObj = 0; + } pyobj = components[idx].get(pyobj); + } if(value) { components[idx].set(pyobj,*value); return Py::Object(); diff --git a/src/App/ObjectIdentifier.h b/src/App/ObjectIdentifier.h index f47d7808b7..5c09f1035b 100644 --- a/src/App/ObjectIdentifier.h +++ b/src/App/ObjectIdentifier.h @@ -342,7 +342,45 @@ public: bool relabeledDocument(ExpressionVisitor &v, const std::string &oldLabel, const std::string &newLabel); - std::pair getDep(std::vector *labels=0) const; + /** Type for storing dependency of an ObjectIdentifier + * + * The dependency is a map from document object to a set of property names. + * An object identifier may references multiple objects using syntax like + * 'Part.Group[0].Width'. + * + * Also, we use set of string instead of set of Property pointer, because + * the property may not exist at the time this ObjectIdentifier is + * constructed. + */ + typedef std::map > Dependencies; + + /** Get dependencies of this object identifier + * + * @param needProps: whether need property dependencies. + * @param labels: optional return of any label references. + * + * In case of multi-object references, like 'Part.Group[0].Width', if no + * property dependency is required, then this function will only return the + * first referred object dependency. Or else, all object and property + * dependencies will be returned. + */ + Dependencies getDep(bool needProps, std::vector *labels=0) const; + + /** Get dependencies of this object identifier + * + * @param deps: returns the depdenencies. + * @param needProps: whether need property dependencies. + * @param labels: optional return of any label references. + * + * In case of multi-object references, like 'Part.Group[0].Width', if no + * property dependency is required, then this function will only return the + * first referred object dependency. Or else, all object and property + * dependencies will be returned. + */ + void getDep(Dependencies &deps, bool needProps, std::vector *labels=0) const; + + /// Returns all label references + void getDepLabels(std::vector &labels) const; App::Document *getDocument(String name = String(), bool *ambiguous=0) const; @@ -422,7 +460,8 @@ protected: void getSubPathStr(std::ostream &ss, const ResolveResults &result, bool toPython=false) const; - Py::Object access(const ResolveResults &rs, Py::Object *value=0) const; + Py::Object access(const ResolveResults &rs, + Py::Object *value=0, Dependencies *deps=0) const; void resolve(ResolveResults & results) const; void resolveAmbiguity(ResolveResults &results); @@ -430,6 +469,8 @@ protected: static App::DocumentObject *getDocumentObject( const App::Document *doc, const String &name, std::bitset<32> &flags); + void getDepLabels(const ResolveResults &result, std::vector &labels) const; + App::DocumentObject * owner; String documentName; String documentObjectName; diff --git a/src/App/Property.cpp b/src/App/Property.cpp index d4571eadd6..04b08190b1 100644 --- a/src/App/Property.cpp +++ b/src/App/Property.cpp @@ -71,6 +71,8 @@ std::string Property::getFullName() const { if(myName) { if(father) name = father->getFullName() + "."; + else + name = "?."; name += myName; }else return "?"; diff --git a/src/App/PropertyContainer.h b/src/App/PropertyContainer.h index bb03473cec..b9763de970 100644 --- a/src/App/PropertyContainer.h +++ b/src/App/PropertyContainer.h @@ -200,6 +200,10 @@ public: return dynamicProps.getDynamicPropertyData(prop); } + bool changeDynamicProperty(const Property *prop, const char *group, const char *doc) { + return dynamicProps.changeDynamicProperty(prop,group,doc); + } + virtual bool removeDynamicProperty(const char* name) { return dynamicProps.removeDynamicProperty(name); } diff --git a/src/App/PropertyContainerPy.xml b/src/App/PropertyContainerPy.xml index 061709405b..44833e5ea5 100644 --- a/src/App/PropertyContainerPy.xml +++ b/src/App/PropertyContainerPy.xml @@ -68,6 +68,11 @@ If the list contains 'Hidden' then the item even doesn't appear in the property Return the name of the group which the property belongs to in this class. The properties sorted in different named groups for convenience. + + + Set the name of the group of a dynamic property. + + @@ -97,6 +102,11 @@ text names of the status. Return the documentation string of the property of this class. + + + Set the documentation string of a dynamic property of this class. + + Return all enumeration strings of the property of this class or None if not a PropertyEnumeration. diff --git a/src/App/PropertyContainerPyImp.cpp b/src/App/PropertyContainerPyImp.cpp index f1775bb127..29aba7869b 100644 --- a/src/App/PropertyContainerPyImp.cpp +++ b/src/App/PropertyContainerPyImp.cpp @@ -339,6 +339,25 @@ PyObject* PropertyContainerPy::getGroupOfProperty(PyObject *args) return Py::new_reference_to(Py::String("")); } +PyObject* PropertyContainerPy::setGroupOfProperty(PyObject *args) +{ + char *pstr; + char *group; + if (!PyArg_ParseTuple(args, "ss", &pstr, &group)) // convert args: Python->C + return NULL; // NULL triggers exception + + PY_TRY { + Property* prop = getPropertyContainerPtr()->getDynamicPropertyByName(pstr); + if (!prop) { + PyErr_Format(PyExc_AttributeError, "Property container has no dynamic property '%s'", pstr); + return 0; + } + prop->getContainer()->changeDynamicProperty(prop,group,0); + Py_Return; + } PY_CATCH +} + + PyObject* PropertyContainerPy::getDocumentationOfProperty(PyObject *args) { char *pstr; @@ -358,6 +377,24 @@ PyObject* PropertyContainerPy::getDocumentationOfProperty(PyObject *args) return Py::new_reference_to(Py::String("")); } +PyObject* PropertyContainerPy::setDocumentationOfProperty(PyObject *args) +{ + char *pstr; + char *doc; + if (!PyArg_ParseTuple(args, "ss", &pstr, &doc)) // convert args: Python->C + return NULL; // NULL triggers exception + + PY_TRY { + Property* prop = getPropertyContainerPtr()->getDynamicPropertyByName(pstr); + if (!prop) { + PyErr_Format(PyExc_AttributeError, "Property container has no dynamic property '%s'", pstr); + return 0; + } + prop->getContainer()->changeDynamicProperty(prop,0,doc); + Py_Return; + } PY_CATCH +} + PyObject* PropertyContainerPy::getEnumerationsOfProperty(PyObject *args) { char *pstr; diff --git a/src/App/PropertyExpressionEngine.cpp b/src/App/PropertyExpressionEngine.cpp index f8f8e84f5d..cf6996d530 100644 --- a/src/App/PropertyExpressionEngine.cpp +++ b/src/App/PropertyExpressionEngine.cpp @@ -37,10 +37,12 @@ #include #include +FC_LOG_LEVEL_INIT("App",true); using namespace App; using namespace Base; using namespace boost; +using namespace boost::placeholders; TYPESYSTEM_SOURCE_ABSTRACT(App::PropertyExpressionContainer , App::PropertyXLinkContainer) @@ -72,6 +74,15 @@ void PropertyExpressionContainer::slotRelabelDocument(const App::Document &doc) /////////////////////////////////////////////////////////////////////////////////////// +struct PropertyExpressionEngine::Private { + // For some reason, MSVC has trouble with vector of scoped_connection if + // defined in header, hence the private structure here. + std::vector conns; + std::unordered_map > propMap; +}; + +/////////////////////////////////////////////////////////////////////////////////////// + TYPESYSTEM_SOURCE(App::PropertyExpressionEngine , App::PropertyExpressionContainer) /** @@ -125,7 +136,7 @@ void PropertyExpressionEngine::hasSetValue() return; } - std::set deps; + std::map deps; std::vector labels; unregisterElementReference(); UpdateElementReferenceExpressionVisitor v(*this); @@ -141,9 +152,95 @@ void PropertyExpressionEngine::hasSetValue() updateDeps(std::move(deps)); + if(pimpl) { + pimpl->conns.clear(); + pimpl->propMap.clear(); + } + // check if there is any hidden references + bool hasHidden = false; + for(auto &v : _Deps) { + if(v.second) { + hasHidden = true; + break; + } + } + if(hasHidden) { + if(!pimpl) + pimpl.reset(new Private); + for(auto &e : expressions) { + auto expr = e.second.expression; + if(!expr) continue; + for(auto &dep : expr->getIdentifiers()) { + if(!dep.second) + continue; + const ObjectIdentifier &var = dep.first; + for(auto &vdep : var.getDep(true)) { + auto obj = vdep.first; + auto objName = obj->getFullName() + "."; + for(auto &propName : vdep.second) { + std::string key = objName + propName; + auto &propDeps = pimpl->propMap[key]; + if(propDeps.empty()) { + if(propName.size()) + pimpl->conns.push_back(obj->signalChanged.connect(boost::bind( + &PropertyExpressionEngine::slotChangedProperty,this,_1,_2))); + else + pimpl->conns.push_back(obj->signalChanged.connect(boost::bind( + &PropertyExpressionEngine::slotChangedObject,this,_1,_2))); + } + propDeps.push_back(e.first); + } + } + } + } + } + PropertyExpressionContainer::hasSetValue(); } +void PropertyExpressionEngine::updateHiddenReference(const std::string &key) { + if(!pimpl) + return; + auto it = pimpl->propMap.find(key); + if(it == pimpl->propMap.end()) + return; + for(auto &var : it->second) { + auto it = expressions.find(var); + if(it == expressions.end() || it->second.busy) + continue; + Property *myProp = var.getProperty(); + if(!myProp) + continue; + Base::StateLocker guard(it->second.busy); + App::any value; + try { + value = it->second.expression->getValueAsAny(); + if(!isAnyEqual(value, myProp->getPathValue(var))) + myProp->setPathValue(var, value); + }catch(Base::Exception &e) { + e.ReportException(); + FC_ERR("Failed to evaluate property binding " + << myProp->getFullName() << " on change of " << key); + }catch(std::bad_cast &) { + FC_ERR("Invalid type '" << value.type().name() + << "' in property binding " << myProp->getFullName() + << " on change of " << key); + }catch(std::exception &e) { + FC_ERR(e.what()); + FC_ERR("Failed to evaluate property binding " + << myProp->getFullName() << " on change of " << key); + } + } +} + +void PropertyExpressionEngine::slotChangedObject(const App::DocumentObject &obj, const App::Property &) { + updateHiddenReference(obj.getFullName()); +} + +void PropertyExpressionEngine::slotChangedProperty(const App::DocumentObject &, const App::Property &prop) { + updateHiddenReference(prop.getFullName()); +} + void PropertyExpressionEngine::Paste(const Property &from) { const PropertyExpressionEngine &fromee = dynamic_cast(from); @@ -562,17 +659,17 @@ DocumentObjectExecReturn *App::PropertyExpressionEngine::execute(ExecuteOption o prop->setPathValue(*it, value); }catch(Base::Exception &e) { std::ostringstream ss; - ss << e.what() << std::endl << "in property binding '" << prop->getName() << "'"; + ss << e.what() << std::endl << "in property binding '" << prop->getFullName() << "'"; e.setMessage(ss.str()); throw; }catch(std::bad_cast &) { std::ostringstream ss; ss << "Invalid type '" << value.type().name() << "'"; - ss << "\nin property binding '" << prop->getName() << "'"; + ss << "\nin property binding '" << prop->getFullName() << "'"; throw Base::TypeError(ss.str().c_str()); }catch(std::exception &e) { std::ostringstream ss; - ss << e.what() << "\nin property binding '" << prop->getName() << "'"; + ss << e.what() << "\nin property binding '" << prop->getFullName() << "'"; throw Base::RuntimeError(ss.str().c_str()); } } @@ -610,9 +707,11 @@ void PropertyExpressionEngine::getPathsToDocumentObject(DocumentObject* obj, bool PropertyExpressionEngine::depsAreTouched() const { - for(auto obj : _Deps) - if(obj->isTouched()) + for(auto &v : _Deps) { + // v.second inidcates if it is a hidden reference + if(!v.second && v.first->isTouched()) return true; + } return false; } @@ -639,8 +738,9 @@ std::string PropertyExpressionEngine::validateExpression(const ObjectIdentifier assert(pathDocObj); auto inList = pathDocObj->getInListEx(true); - for(auto docObj : expr->getDepObjects()) { - if(inList.count(docObj)) { + for(auto &v : expr->getDepObjects()) { + auto docObj = v.first; + if(!v.second && inList.count(docObj)) { std::stringstream ss; ss << "cyclic reference to " << docObj->getFullName(); return ss.str(); @@ -765,8 +865,8 @@ bool PropertyExpressionEngine::adjustLink(const std::set &inLis if(!owner) return false; bool found = false; - for(auto obj : _Deps) { - if(inList.count(obj)) { + for(auto &v : _Deps) { + if(inList.count(v.first)) { found = true; break; } diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 5510a74961..926ff856d3 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -85,9 +85,11 @@ public: struct ExpressionInfo { std::shared_ptr expression; /**< The actual expression tree */ + bool busy; ExpressionInfo(std::shared_ptr expression = std::shared_ptr()) { this->expression = expression; + this->busy = false; } ExpressionInfo(const ExpressionInfo & other) { @@ -186,6 +188,10 @@ private: boost::unordered_map &revNodes, DiGraph &g, ExecuteOption option=ExecuteAll) const; + void slotChangedObject(const App::DocumentObject &obj, const App::Property &prop); + void slotChangedProperty(const App::DocumentObject &obj, const App::Property &prop); + void updateHiddenReference(const std::string &key); + bool running; /**< Boolean used to avoid loops */ bool restoring = false; @@ -201,6 +207,9 @@ private: /**< Expressions are read from file to this map first before they are validated and inserted into the actual map */ std::unique_ptr > restoredExpressions; + struct Private; + std::unique_ptr pimpl; + friend class AtomicPropertyChange; }; diff --git a/src/App/PropertyLinks.cpp b/src/App/PropertyLinks.cpp index af4102c879..6fdc905610 100644 --- a/src/App/PropertyLinks.cpp +++ b/src/App/PropertyLinks.cpp @@ -4483,7 +4483,7 @@ void PropertyXLinkContainer::afterRestore() { if(info.docLabel != obj->getDocument()->Label.getValue()) _DocMap[App::quote(info.docLabel)] = obj->getDocument()->Label.getValue(); } - if(_Deps.insert(obj).second) + if(_Deps.insert(std::make_pair(obj,info.xlink->getScope()==LinkScope::Hidden)).second) _XLinks[obj->getFullName()] = std::move(info.xlink); } _XLinkRestores.reset(); @@ -4496,24 +4496,27 @@ void PropertyXLinkContainer::breakLink(App::DocumentObject *obj, bool clear) { if(!owner || !owner->getNameInDocument()) return; if(!clear || obj!=owner) { - if(!_Deps.erase(obj)) + auto it = _Deps.find(obj); + if(it == _Deps.end()) return; aboutToSetValue(); onBreakLink(obj); - if(obj->getDocument() == owner->getDocument()) - obj->_removeBackLink(owner); - else + if (obj->getDocument() != owner->getDocument()) _XLinks.erase(obj->getFullName()); + else if (!it->second) + obj->_removeBackLink(owner); + _Deps.erase(it); hasSetValue(); return; } if(obj!=owner) return; - for(auto obj : _Deps) { + for(auto &v : _Deps) { + auto obj = v.first; if(!obj || !obj->getNameInDocument()) continue; onBreakLink(obj); - if(obj->getDocument()==owner->getDocument()) + if(!v.second && obj->getDocument()==owner->getDocument()) obj->_removeBackLink(owner); } _XLinks.clear(); @@ -4553,6 +4556,19 @@ void PropertyXLinkContainer::Save (Base::Writer &writer) const { writer.Stream() << "\" docs=\"" << docSet.size(); } + std::ostringstream ss; + int hidden = 0; + int i=-1; + for(auto &v : _XLinks) { + ++i; + if(v.second->getScope() == LinkScope::Hidden) { + ss << i << ' '; + ++hidden; + } + } + if(hidden) + writer.Stream() << "\" hidden=\"" << ss.str(); + writer.Stream() << "\">" << std::endl; writer.incInd(); @@ -4575,6 +4591,15 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) { auto count = reader.getAttributeAsUnsigned("count"); _XLinkRestores.reset(new std::vector(count)); + if(reader.hasAttribute("hidden")) { + std::istringstream iss(reader.getAttribute("hidden")); + int index; + while(iss >> index) { + if(index>=0 && index(count)) + _XLinkRestores->at(index).hidden = true; + } + } + if(reader.hasAttribute("docs")) { auto docCount = reader.getAttributeAsUnsigned("docs"); _DocMap.clear(); @@ -4593,6 +4618,8 @@ void PropertyXLinkContainer::Restore(Base::XMLReader &reader) { for(auto &info : *_XLinkRestores) { info.xlink.reset(createXLink()); + if(info.hidden) + info.xlink->setScope(LinkScope::Hidden); info.xlink->Restore(reader); } reader.readEndElement("XLinks"); @@ -4624,16 +4651,23 @@ bool PropertyXLinkContainer::isLinkedToDocument(const App::Document &doc) const return false; } -void PropertyXLinkContainer::updateDeps(std::set &&newDeps) { +void PropertyXLinkContainer::updateDeps(std::map &&newDeps) { auto owner = Base::freecad_dynamic_cast(getContainer()); if(!owner || !owner->getNameInDocument()) return; newDeps.erase(owner); - for(auto obj : newDeps) { + for(auto &v : newDeps) { + auto obj = v.first; if(obj && obj->getNameInDocument()) { auto it = _Deps.find(obj); if(it != _Deps.end()) { + if(v.second != it->second) { + if(v.second) + obj->_removeBackLink(owner); + else + obj->_addBackLink(owner); + } _Deps.erase(it); continue; } @@ -4643,21 +4677,21 @@ void PropertyXLinkContainer::updateDeps(std::set &&newDeps) { xlink.reset(createXLink()); xlink->setValue(obj); } + xlink->setScope(v.second?LinkScope::Hidden:LinkScope::Global); } -#ifndef USE_OLD_DAG - else + else if(!v.second) obj->_addBackLink(owner); -#endif + onAddDep(obj); } } - for(auto obj : _Deps) { + for(auto &v : _Deps) { + auto obj = v.first; if(!obj || !obj->getNameInDocument()) continue; if(obj->getDocument()==owner->getDocument()) { -#ifndef USE_OLD_DAG - obj->_removeBackLink(owner); -#endif + if(!v.second) + obj->_removeBackLink(owner); }else _XLinks.erase(obj->getFullName()); onRemoveDep(obj); @@ -4681,8 +4715,9 @@ void PropertyXLinkContainer::clearDeps() { return; #ifndef USE_OLD_DAG if (!owner->testStatus(ObjectStatus::Destroy)) { - for(auto obj : _Deps) { - if(obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument()) + for(auto &v : _Deps) { + auto obj = v.first; + if(!v.second && obj && obj->getNameInDocument() && obj->getDocument()==owner->getDocument()) obj->_removeBackLink(owner); } } @@ -4692,8 +4727,11 @@ void PropertyXLinkContainer::clearDeps() { _LinkRestored = false; } -void PropertyXLinkContainer::getLinks(std::vector &objs, - bool, std::vector * /*subs*/, bool /*newStyle*/) const +void PropertyXLinkContainer::getLinks(std::vector &objs, + bool all, std::vector * /*subs*/, bool /*newStyle*/) const { - objs.insert(objs.end(),_Deps.begin(),_Deps.end()); + for(auto &v : _Deps) { + if(all || !v.second) + objs.push_back(v.first); + } } diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 7b82113a15..5bc813d5e0 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -1339,11 +1339,11 @@ protected: virtual void onBreakLink(App::DocumentObject *obj); virtual void onAddDep(App::DocumentObject *) {} virtual void onRemoveDep(App::DocumentObject *) {} - void updateDeps(std::set &&newDeps); + void updateDeps(std::map &&newDeps); void clearDeps(); protected: - std::set _Deps; + std::map _Deps; std::map > _XLinks; std::map _DocMap; bool _LinkRestored; @@ -1353,6 +1353,7 @@ private: std::unique_ptr xlink; std::string docName; std::string docLabel; + bool hidden=false; }; std::unique_ptr > _XLinkRestores; }; diff --git a/src/App/PropertyStandard.cpp b/src/App/PropertyStandard.cpp index 684ca308f5..9898d94277 100644 --- a/src/App/PropertyStandard.cpp +++ b/src/App/PropertyStandard.cpp @@ -304,18 +304,22 @@ PropertyEnumeration::~PropertyEnumeration() void PropertyEnumeration::setEnums(const char **plEnums) { - // Setting the enum is done only once inside the constructor - // but before the current index is already set. So, this needs - // to be preserved. - int index = _enum._index; + // For backward compatibility, if the property container is not attached to + // any document (i.e. its full name starts with '?'), do not notify, or + // else existing code may crash. + bool notify = !boost::starts_with(getFullName(), "?"); + if (notify) + aboutToSetValue(); _enum.setEnums(plEnums); - // Make sure not to set an index out of range - int max = _enum.maxValue(); - _enum._index = std::min(index, max); + if (notify) + hasSetValue(); } void PropertyEnumeration::setEnums(const std::vector &Enums) { + // _enum.setEnums() will preserve old value possible, so no need to do it + // here +#if 0 if (_enum.isValid()) { const std::string &index = getValueAsString(); _enum.setEnums(Enums); @@ -323,6 +327,9 @@ void PropertyEnumeration::setEnums(const std::vector &Enums) } else { _enum.setEnums(Enums); } +#else + setEnumVector(Enums); +#endif } void PropertyEnumeration::setValue(const char *value) @@ -368,7 +375,7 @@ const char * PropertyEnumeration::getValueAsString() const return _enum.getCStr(); } -Enumeration PropertyEnumeration::getEnum() const +const Enumeration & PropertyEnumeration::getEnum() const { return _enum; } @@ -378,6 +385,19 @@ std::vector PropertyEnumeration::getEnumVector() const return _enum.getEnumVector(); } +void PropertyEnumeration::setEnumVector(const std::vector &values) +{ + // For backward compatibility, if the property container is not attached to + // any document (i.e. its full name starts with '?'), do not notify, or + // else existing code may crash. + bool notify = !boost::starts_with(getFullName(), "?"); + if (notify) + aboutToSetValue(); + _enum.setEnums(values); + if (notify) + hasSetValue(); +} + const char ** PropertyEnumeration::getEnums() const { return _enum.getEnums(); @@ -414,6 +434,8 @@ void PropertyEnumeration::Restore(Base::XMLReader &reader) // get the value of my Attribute long val = reader.getAttributeAsInteger("value"); + aboutToSetValue(); + if (reader.hasAttribute("CustomEnum")) { reader.readElement("CustomEnumList"); int count = reader.getAttributeAsInteger("count"); @@ -436,14 +458,21 @@ void PropertyEnumeration::Restore(Base::XMLReader &reader) val = getValue(); } - setValue(val); + _enum.setValue(val); + hasSetValue(); } PyObject * PropertyEnumeration::getPyObject() { if (!_enum.isValid()) { - PyErr_SetString(PyExc_AssertionError, "The enum is empty"); - return nullptr; + // There is legimate use case of having an empty PropertyEnumeration and + // set its enumeration items later. Returning error here cause hasattr() + // to return False even though the property exists. + // + // PyErr_SetString(PyExc_AssertionError, "The enum is empty"); + // return 0; + // + Py_Return; } return Py_BuildValue("s", getValueAsString()); @@ -458,6 +487,7 @@ void PropertyEnumeration::setPyObject(PyObject *value) _enum.setValue(val, true); hasSetValue(); } + return; } else if (PyUnicode_Check(value)) { std::string str = PyUnicode_AsUTF8(value); @@ -467,34 +497,47 @@ void PropertyEnumeration::setPyObject(PyObject *value) hasSetValue(); } else { - std::stringstream out; - out << "'" << str << "' is not part of the enumeration"; - throw Base::ValueError(out.str()); + FC_THROWM(Base::ValueError, "'" << str + << "' is not part of the enumeration in " + << getFullName()); } + return; } else if (PySequence_Check(value)) { - Py_ssize_t nSize = PySequence_Size(value); - std::vector values; - values.resize(nSize); - for (Py_ssize_t i = 0; i < nSize; ++i) { - PyObject *item = PySequence_GetItem(value, i); + try { + std::vector values; - if (PyUnicode_Check(item)) { - values[i] = PyUnicode_AsUTF8(item); - } - else { - std::string error = std::string("type in list must be str or unicode, not "); - throw Base::TypeError(error + item->ob_type->tp_name); + int idx = -1; + Py::Sequence seq(value); + + if(seq.size() == 2) { + Py::Object v(seq[0].ptr()); + if(!v.isString() && v.isSequence()) { + idx = Py::Int(seq[1].ptr()); + seq = v; + } } + + values.resize(seq.size()); + + for (int i = 0; i < seq.size(); ++i) + values[i] = Py::Object(seq[i].ptr()).as_string(); + + aboutToSetValue(); + _enum.setEnums(values); + if (idx>=0) + _enum.setValue(idx,true); + hasSetValue(); + return; + } catch (Py::Exception &) { + Base::PyException e; + e.ReportException(); } - _enum.setEnums(values); - setValue((long)0); - } - else { - std::string error = std::string("type must be int, str or unicode not "); - throw Base::TypeError(error + value->ob_type->tp_name); } + + FC_THROWM(Base::TypeError, "PropertyEnumeration " << getFullName() + << " expects type to be int, string, or list(string), or list(list, int)"); } Property * PropertyEnumeration::Copy() const @@ -504,22 +547,20 @@ Property * PropertyEnumeration::Copy() const void PropertyEnumeration::Paste(const Property &from) { - aboutToSetValue(); - const PropertyEnumeration& prop = dynamic_cast(from); - _enum = prop._enum; - - hasSetValue(); + setValue(prop._enum); } -void PropertyEnumeration::setPathValue(const ObjectIdentifier &path, const boost::any &value) +void PropertyEnumeration::setPathValue(const ObjectIdentifier &, const boost::any &value) { - verifyPath(path); - if (value.type() == typeid(int)) setValue(boost::any_cast(value)); + else if (value.type() == typeid(long)) + setValue(boost::any_cast(value)); else if (value.type() == typeid(double)) setValue(boost::any_cast(value)); + else if (value.type() == typeid(float)) + setValue(boost::any_cast(value)); else if (value.type() == typeid(short)) setValue(boost::any_cast(value)); else if (value.type() == typeid(std::string)) @@ -528,8 +569,61 @@ void PropertyEnumeration::setPathValue(const ObjectIdentifier &path, const boost setValue(boost::any_cast(value)); else if (value.type() == typeid(const char*)) setValue(boost::any_cast(value)); - else - throw bad_cast(); + else { + Base::PyGILStateLocker lock; + Py::Object pyValue = pyObjectFromAny(value); + setPyObject(pyValue.ptr()); + } +} + +bool PropertyEnumeration::setPyPathValue(const ObjectIdentifier &, const Py::Object &value) +{ + setPyObject(value.ptr()); + return true; +} + +const boost::any PropertyEnumeration::getPathValue(const ObjectIdentifier &path) const +{ + std::string p = path.getSubPathStr(); + if (p == ".Enum" || p == ".All") { + Base::PyGILStateLocker lock; + Py::Object res; + getPyPathValue(path, res); + return pyObjectToAny(res,false); + } + else if (p == ".String") { + auto v = getValueAsString(); + return std::string(v?v:""); + } else + return getValue(); +} + +bool PropertyEnumeration::getPyPathValue(const ObjectIdentifier &path, Py::Object &r) const +{ + std::string p = path.getSubPathStr(); + if (p == ".Enum" || p == ".All") { + Base::PyGILStateLocker lock; + Py::Tuple res(_enum.maxValue()+1); + const char **enums = _enum.getEnums(); + PropertyString tmp; + for(int i=0;i<=_enum.maxValue();++i) { + tmp.setValue(enums[i]); + res.setItem(i,Py::asObject(tmp.getPyObject())); + } + if(p == ".Enum") + r = res; + else { + Py::Tuple tuple(2); + tuple.setItem(0, res); + tuple.setItem(1, Py::Int(getValue())); + r = tuple; + } + } else if (p == ".String") { + auto v = getValueAsString(); + r = Py::String(v?v:""); + } else + r = Py::Int(getValue()); + return true; } //************************************************************************** diff --git a/src/App/PropertyStandard.h b/src/App/PropertyStandard.h index aa4557b1a5..49bc7509d3 100644 --- a/src/App/PropertyStandard.h +++ b/src/App/PropertyStandard.h @@ -186,11 +186,13 @@ public: const char * getValueAsString(void) const; /// Returns Enumeration object - Enumeration getEnum(void) const; + const Enumeration &getEnum(void) const; /// get all possible enum values as vector of strings std::vector getEnumVector(void) const; + /// set enum values as vector of strings + void setEnumVector(const std::vector &); /// get the pointer to the enum list const char ** getEnums(void) const; @@ -211,7 +213,9 @@ public: virtual void Paste(const Property &from); virtual void setPathValue(const App::ObjectIdentifier & path, const boost::any & value); - virtual const boost::any getPathValue(const App::ObjectIdentifier & /*path*/) const { return _enum; } + virtual bool setPyPathValue(const App::ObjectIdentifier & path, const Py::Object &value); + virtual const boost::any getPathValue(const App::ObjectIdentifier & /*path*/) const; + virtual bool getPyPathValue(const ObjectIdentifier &path, Py::Object &r) const; private: Enumeration _enum; diff --git a/src/App/Range.h b/src/App/Range.h index 554a02057a..a9182b7466 100644 --- a/src/App/Range.h +++ b/src/App/Range.h @@ -120,6 +120,12 @@ public: /** Current column */ inline int column() const { return col_curr; } + /** Row count */ + inline int rowCount() const { return row_end - row_begin + 1; } + + /** Column count */ + inline int colCount() const { return col_end - col_begin + 1; } + /** Position of start of range */ inline CellAddress from() const { return CellAddress(row_begin, col_begin); } diff --git a/src/Base/GeometryPyCXX.cpp b/src/Base/GeometryPyCXX.cpp index 3edbc2453c..806a11ca79 100644 --- a/src/Base/GeometryPyCXX.cpp +++ b/src/Base/GeometryPyCXX.cpp @@ -90,6 +90,21 @@ Base::Vector3d Py::Vector::toVector() const namespace Base { +Py::PythonClassObject Vector2dPy::create(const Vector2d& v) +{ + return create(v.x, v.y); +} + +Py::PythonClassObject Vector2dPy::create(double x, double y) +{ + Py::Callable class_type(type()); + Py::Tuple arg(2); + arg.setItem(0, Py::Float(x)); + arg.setItem(1, Py::Float(y)); + Py::PythonClassObject o = Py::PythonClassObject(class_type.apply(arg, Py::Dict())); + return o; +} + Vector2dPy::Vector2dPy(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) : Py::PythonClass::PythonClass(self, args, kwds) { @@ -106,17 +121,6 @@ Vector2dPy::~Vector2dPy() { } -void Vector2dPy::init_type(void) -{ - behaviors().name( "Vector2d" ); - behaviors().doc( "Vector2d class" ); - behaviors().supportGetattro(); - behaviors().supportSetattro(); - behaviors().supportRepr(); - // Call to make the type ready for use - behaviors().readyType(); -} - Py::Object Vector2dPy::repr() { Py::Float x(v.x); @@ -176,4 +180,225 @@ int Vector2dPy::setattro(const Py::String &name_, const Py::Object &value) } } +Py::Object Vector2dPy::number_negative() +{ + return create(-v.x, -v.y); +} + +Py::Object Vector2dPy::number_positive() +{ + return create(v.x, v.y); +} + +Py::Object Vector2dPy::number_absolute() +{ + return create(fabs(v.x), fabs(v.y)); +} + +Py::Object Vector2dPy::number_invert() +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_int() +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_float() +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_long() +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_add( const Py::Object & py) +{ + Vector2d u(Py::toVector2d(py)); + u = v + u; + return create(u); +} + +Py::Object Vector2dPy::number_subtract( const Py::Object & py) +{ + Vector2d u(Py::toVector2d(py)); + u = v - u; + return create(u); +} + +Py::Object Vector2dPy::number_multiply( const Py::Object & py) +{ + if (PyObject_TypeCheck(py.ptr(), Vector2dPy::type_object())) { + Vector2d u(Py::toVector2d(py)); + double d = v * u; + return Py::Float(d); + } + else if (py.isNumeric()) { + double d = static_cast(Py::Float(py)); + return create(v * d); + } + else { + throw Py::TypeError("Argument must be Vector2d or Float"); + } +} + +Py::Object Vector2dPy::number_remainder( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_divmod( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_lshift( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_rshift( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_and( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_xor( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_or( const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::number_power( const Py::Object &, const Py::Object & ) +{ + throw Py::TypeError("Not defined"); +} + +Py::Object Vector2dPy::isNull(const Py::Tuple& args) +{ + double tol = 0.0; + if (args.size() > 0) { + tol = static_cast(Py::Float(args[0])); + } + return Py::Boolean(v.IsNull(tol)); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, isNull) + +Py::Object Vector2dPy::length(const Py::Tuple&) +{ + return Py::Float(v.Length()); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, length) + +Py::Object Vector2dPy::atan2(const Py::Tuple&) +{ + return Py::Float(v.Angle()); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, atan2) + +Py::Object Vector2dPy::square(const Py::Tuple&) +{ + return Py::Float(v.Sqr()); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, square) + +Py::Object Vector2dPy::scale(const Py::Tuple& args) +{ + double f = static_cast(Py::Float(args[0])); + v.Scale(f); + return Py::None(); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, scale) + +Py::Object Vector2dPy::rotate(const Py::Tuple& args) +{ + double f = static_cast(Py::Float(args[0])); + v.Rotate(f); + return Py::None(); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, rotate) + +Py::Object Vector2dPy::normalize(const Py::Tuple&) +{ + v.Normalize(); + return Py::None(); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, normalize) + +Py::Object Vector2dPy::perpendicular(const Py::Tuple& args) +{ + bool f = static_cast(Py::Boolean(args[0])); + Base::Vector2d p = v.Perpendicular(f); + return create(p); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, perpendicular) + +Py::Object Vector2dPy::distance(const Py::Tuple& args) +{ + Base::Vector2d p = Py::toVector2d(args[0]); + return Py::Float(p.Distance(v)); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, distance) + +Py::Object Vector2dPy::isEqual(const Py::Tuple& args) +{ + Base::Vector2d p = Py::toVector2d(args[0]); + double f = static_cast(Py::Float(args[1])); + return Py::Boolean(v.IsEqual(p, f)); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, isEqual) + +Py::Object Vector2dPy::getAngle(const Py::Tuple& args) +{ + Base::Vector2d p = Py::toVector2d(args[0]); + return Py::Float(v.GetAngle(p)); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, getAngle) + +Py::Object Vector2dPy::projectToLine(const Py::Tuple& args) +{ + Base::Vector2d p = Py::toVector2d(args[0]); + Base::Vector2d d = Py::toVector2d(args[1]); + v.ProjectToLine(p, d); + return Py::None(); +} +PYCXX_VARARGS_METHOD_DECL(Vector2dPy, projectToLine) + +void Vector2dPy::init_type(void) +{ + behaviors().name( "Vector2d" ); + behaviors().doc( "Vector2d class" ); + behaviors().supportGetattro(); + behaviors().supportSetattro(); + behaviors().supportRepr(); + behaviors().supportNumberType(); + + PYCXX_ADD_VARARGS_METHOD(isNull, isNull, "isNull()"); + PYCXX_ADD_VARARGS_METHOD(length, length, "length()"); + PYCXX_ADD_VARARGS_METHOD(atan2, atan2, "atan2()"); + PYCXX_ADD_VARARGS_METHOD(square, square, "square()"); + PYCXX_ADD_VARARGS_METHOD(scale, scale, "scale()"); + PYCXX_ADD_VARARGS_METHOD(rotate, rotate, "rotate()"); + PYCXX_ADD_VARARGS_METHOD(normalize, normalize, "normalize()"); + PYCXX_ADD_VARARGS_METHOD(perpendicular, perpendicular, "perpendicular()"); + PYCXX_ADD_VARARGS_METHOD(distance, distance, "distance()"); + PYCXX_ADD_VARARGS_METHOD(isEqual, isEqual, "isEqual()"); + PYCXX_ADD_VARARGS_METHOD(getAngle, getAngle, "getAngle()"); + PYCXX_ADD_VARARGS_METHOD(projectToLine, projectToLine, "projectToLine()"); + + // Call to make the type ready for use + behaviors().readyType(); +} + } diff --git a/src/Base/GeometryPyCXX.h b/src/Base/GeometryPyCXX.h index 8dfd6cf9f8..24faa31fe4 100644 --- a/src/Base/GeometryPyCXX.h +++ b/src/Base/GeometryPyCXX.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,8 @@ inline Vector3 getVectorFromTuple(PyObject* o) class BaseExport Vector2dPy : public Py::PythonClass { public: + static Py::PythonClassObject create(const Vector2d&); + static Py::PythonClassObject create(double x, double y); Vector2dPy(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds); virtual ~Vector2dPy(); @@ -68,6 +71,41 @@ public: v.y = y; } + /** @name methods for group handling */ + //@{ + virtual Py::Object number_negative(); + virtual Py::Object number_positive(); + virtual Py::Object number_absolute(); + virtual Py::Object number_invert(); + virtual Py::Object number_int(); + virtual Py::Object number_float(); + virtual Py::Object number_long(); + virtual Py::Object number_add( const Py::Object & ); + virtual Py::Object number_subtract( const Py::Object & ); + virtual Py::Object number_multiply( const Py::Object & ); + virtual Py::Object number_remainder( const Py::Object & ); + virtual Py::Object number_divmod( const Py::Object & ); + virtual Py::Object number_lshift( const Py::Object & ); + virtual Py::Object number_rshift( const Py::Object & ); + virtual Py::Object number_and( const Py::Object & ); + virtual Py::Object number_xor( const Py::Object & ); + virtual Py::Object number_or( const Py::Object & ); + virtual Py::Object number_power( const Py::Object &, const Py::Object & ); + //@} + + Py::Object isNull(const Py::Tuple&); + Py::Object length(const Py::Tuple&); + Py::Object atan2(const Py::Tuple&); + Py::Object square(const Py::Tuple&); + Py::Object scale(const Py::Tuple&); + Py::Object rotate(const Py::Tuple&); + Py::Object normalize(const Py::Tuple&); + Py::Object perpendicular(const Py::Tuple&); + Py::Object distance(const Py::Tuple&); + Py::Object isEqual(const Py::Tuple&); + Py::Object getAngle(const Py::Tuple&); + Py::Object projectToLine(const Py::Tuple&); + private: Vector2d v; }; diff --git a/src/Gui/MDIViewPy.cpp b/src/Gui/MDIViewPy.cpp index 31a399cebd..33ec535da1 100644 --- a/src/Gui/MDIViewPy.cpp +++ b/src/Gui/MDIViewPy.cpp @@ -54,7 +54,9 @@ void MDIViewPy::init_type() behaviors().supportGetattr(); behaviors().supportSetattr(); - add_varargs_method("message",&MDIViewPy::message,"message()"); + add_varargs_method("message",&MDIViewPy::sendMessage,"deprecated: use sendMessage"); + add_varargs_method("sendMessage",&MDIViewPy::sendMessage,"sendMessage(str)"); + add_varargs_method("supportMessage",&MDIViewPy::supportMessage,"supportMessage(str)"); add_varargs_method("fitAll",&MDIViewPy::fitAll,"fitAll()"); add_varargs_method("setActiveObject", &MDIViewPy::setActiveObject, "setActiveObject(name,object,subname=None)\nadd or set a new active object"); add_varargs_method("getActiveObject", &MDIViewPy::getActiveObject, "getActiveObject(name,resolve=True)\nreturns the active object for the given type"); @@ -67,6 +69,8 @@ MDIViewPy::MDIViewPy(MDIView *mdi) MDIViewPy::~MDIViewPy() { + // in case the class is instantiated on the stack + ob_refcnt = 0; } Py::Object MDIViewPy::repr() @@ -79,16 +83,41 @@ Py::Object MDIViewPy::repr() return Py::String(s_out.str()); } -Py::Object MDIViewPy::message(const Py::Tuple& args) +Py::Object MDIViewPy::sendMessage(const Py::Tuple& args) { const char **ppReturn = 0; char *psMsgStr; - if (!PyArg_ParseTuple(args.ptr(), "s;Message string needed (string)",&psMsgStr)) // convert args: Python->C + if (!PyArg_ParseTuple(args.ptr(), "s;Message string needed (string)",&psMsgStr)) throw Py::Exception(); try { + bool ok = false; if (_view) - _view->onMsg(psMsgStr,ppReturn); + ok = _view->onMsg(psMsgStr,ppReturn); + return Py::Boolean(ok); + } + catch (const Base::Exception& e) { + throw Py::RuntimeError(e.what()); + } + catch (const std::exception& e) { + throw Py::RuntimeError(e.what()); + } + catch (...) { + throw Py::RuntimeError("Unknown C++ exception"); + } +} + +Py::Object MDIViewPy::supportMessage(const Py::Tuple& args) +{ + char *psMsgStr; + if (!PyArg_ParseTuple(args.ptr(), "s;Message string needed (string)",&psMsgStr)) + throw Py::Exception(); + + try { + bool ok = false; + if (_view) + _view->onHasMsg(psMsgStr); + return Py::Boolean(ok); } catch (const Base::Exception& e) { throw Py::RuntimeError(e.what()); @@ -99,7 +128,6 @@ Py::Object MDIViewPy::message(const Py::Tuple& args) catch (...) { throw Py::RuntimeError("Unknown C++ exception"); } - return Py::None(); } Py::Object MDIViewPy::fitAll(const Py::Tuple& args) diff --git a/src/Gui/MDIViewPy.h b/src/Gui/MDIViewPy.h index 46a0665bf3..469bdca6f3 100644 --- a/src/Gui/MDIViewPy.h +++ b/src/Gui/MDIViewPy.h @@ -27,11 +27,12 @@ #include #include #include +#include namespace Gui { class MDIView; -class MDIViewPy : public Py::PythonExtension +class GuiExport MDIViewPy : public Py::PythonExtension { public: static void init_type(void); // announce properties and methods @@ -41,7 +42,8 @@ public: Py::Object repr(); - Py::Object message(const Py::Tuple&); + Py::Object sendMessage(const Py::Tuple&); + Py::Object supportMessage(const Py::Tuple&); Py::Object fitAll(const Py::Tuple&); Py::Object setActiveObject(const Py::Tuple&); Py::Object getActiveObject(const Py::Tuple&); diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 17086f6eeb..2b378dfb2f 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -38,6 +38,7 @@ # include # include # include +# include # include # include # include @@ -513,13 +514,19 @@ void View3DInventor::printPreview() { QPrinter printer(QPrinter::ScreenResolution); printer.setFullPage(true); - printer.setPageSize(QPageSize(QPageSize::A4)); - printer.setPageOrientation(QPageLayout::Landscape); + hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + int initialDefaultPageSize = !QPrinterInfo::defaultPrinter().isNull() ? QPrinterInfo::defaultPrinter().defaultPageSize().id() : QPageSize::A4; + int defaultPageSize = hGrp->GetInt("DefaultPageSize", initialDefaultPageSize); + int defaultPageOrientation = hGrp->GetInt("DefaultPageOrientation", QPageLayout::Portrait); + printer.setPageSize(QPageSize(static_cast(defaultPageSize))); + printer.setPageOrientation(static_cast(defaultPageOrientation)); QPrintPreviewDialog dlg(&printer, this); connect(&dlg, SIGNAL(paintRequested (QPrinter *)), this, SLOT(print(QPrinter *))); dlg.exec(); + hGrp -> SetInt("DefaultPageSize", printer.pageLayout().pageSize().id()); + hGrp -> SetInt("DefaultPageOrientation", static_cast(printer.pageLayout().orientation())); } void View3DInventor::print(QPrinter* printer) diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index 4c18218c57..208b18b1b8 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -100,6 +100,7 @@ ViewProviderDocumentObject::ViewProviderDocumentObject() ViewProviderDocumentObject::~ViewProviderDocumentObject() { // Make sure that the property class does not destruct our string list + DisplayMode.setContainer(nullptr); DisplayMode.setEnums(0); } @@ -687,7 +688,7 @@ ViewProviderDocumentObject *ViewProviderDocumentObject::getLinkedViewProvider( std::string ViewProviderDocumentObject::getFullName() const { if(pcObject) return pcObject->getFullName() + ".ViewObject"; - return std::string(); + return std::string("?"); } bool ViewProviderDocumentObject::allowTreeOrderSwap(const App::DocumentObject *child1, const App::DocumentObject *child2) const diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index 9bfc78639e..47f3c15975 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -31,8 +31,11 @@ # include # include # include +# include #endif +#include + #include #include #include @@ -57,6 +60,7 @@ PropertyEditor::PropertyEditor(QWidget *parent) , committing(false) , delaybuild(false) , binding(false) + , checkDocument(false) { propertyModel = new PropertyModel(this); setModel(propertyModel); @@ -362,8 +366,10 @@ void PropertyEditor::drawBranches(QPainter *painter, const QRect &rect, const QM //painter->setPen(savedPen); } -void PropertyEditor::buildUp(PropertyModel::PropertyList &&props, bool checkDocument) +void PropertyEditor::buildUp(PropertyModel::PropertyList &&props, bool _checkDocument) { + checkDocument = _checkDocument; + if (committing) { Base::Console().Warning("While committing the data to the property the selection has changed.\n"); delaybuild = true; @@ -493,6 +499,7 @@ enum MenuAction { MA_Expression, MA_RemoveProp, MA_AddProp, + MA_EditPropGroup, MA_Transient, MA_Output, MA_NoRecompute, @@ -532,8 +539,20 @@ void PropertyEditor::contextMenuEvent(QContextMenuEvent *) { } } - if(props.size()) + if(props.size()) { menu.addAction(tr("Add property"))->setData(QVariant(MA_AddProp)); + unsigned count = 0; + for(auto prop : props) { + if(prop->testStatus(App::Property::PropDynamic) + && !boost::starts_with(prop->getName(),prop->getGroup())) + { + ++count; + } else + break; + } + if(count == props.size()) + menu.addAction(tr("Rename property group"))->setData(QVariant(MA_EditPropGroup)); + } bool canRemove = !props.empty(); unsigned long propType = 0; @@ -645,6 +664,22 @@ void PropertyEditor::contextMenuEvent(QContextMenuEvent *) { dlg.exec(); return; } + case MA_EditPropGroup: { + // This operation is not undoable yet. + const char *groupName = (*props.begin())->getGroup(); + if(!groupName) + groupName = "Base"; + QString res = QInputDialog::getText(Gui::getMainWindow(), + tr("Rename property group"), tr("Group name:"), + QLineEdit::Normal, QString::fromUtf8(groupName)); + if(res.size()) { + std::string group = res.toUtf8().constData(); + for(auto prop : props) + prop->getContainer()->changeDynamicProperty(prop,group.c_str(),0); + buildUp(PropertyModel::PropertyList(propList),checkDocument); + } + return; + } case MA_RemoveProp: { App::AutoTransaction committer("Remove property"); for(auto prop : props) { diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index c865b13c24..f9b55798af 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -124,6 +124,7 @@ private: bool committing; bool delaybuild; bool binding; + bool checkDocument; int transactionID = 0; diff --git a/src/Gui/propertyeditor/PropertyItem.cpp b/src/Gui/propertyeditor/PropertyItem.cpp index d3e9cd8076..1c56a23fcc 100644 --- a/src/Gui/propertyeditor/PropertyItem.cpp +++ b/src/Gui/propertyeditor/PropertyItem.cpp @@ -770,7 +770,7 @@ QVariant PropertyFontItem::value(const App::Property* prop) const void PropertyFontItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::String)) + if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); @@ -1270,7 +1270,7 @@ QVariant PropertyBoolItem::value(const App::Property* prop) const void PropertyBoolItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::Bool)) + if (hasExpression() || !value.canConvert(QVariant::Bool)) return; bool val = value.toBool(); QString data = (val ? QLatin1String("True") : QLatin1String("False")); @@ -1377,7 +1377,7 @@ QVariant PropertyVectorItem::value(const App::Property* prop) const void PropertyVectorItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; const Base::Vector3d& val = value.value(); QString data = QString::fromLatin1("(%1, %2, %3)") @@ -1652,7 +1652,7 @@ QVariant PropertyVectorDistanceItem::value(const App::Property* prop) const void PropertyVectorDistanceItem::setValue(const QVariant& variant) { - if (!variant.canConvert()) + if (hasExpression() || !variant.canConvert()) return; const Base::Vector3d& value = variant.value(); @@ -1869,7 +1869,7 @@ QVariant PropertyMatrixItem::toolTip(const App::Property* prop) const void PropertyMatrixItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; const Base::Matrix4D& val = value.value(); const int decimals=16; @@ -2644,7 +2644,7 @@ QVariant PropertyPlacementItem::toString(const QVariant& prop) const void PropertyPlacementItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; // Accept this only if the user changed the axis, angle or position but // not if >this< item loses focus @@ -2711,7 +2711,37 @@ void PropertyPlacementItem::propertyBound() PROPERTYITEM_SOURCE(Gui::PropertyEditor::PropertyEnumItem) PropertyEnumItem::PropertyEnumItem() + :m_enum(0) { + if(PropertyView::showAll()) { + m_enum = static_cast(PropertyStringListItem::create()); + m_enum->setParent(this); + m_enum->setPropertyName(QLatin1String(QT_TRANSLATE_NOOP("App::Property", "Enum"))); + this->appendChild(m_enum); + } +} + +void PropertyEnumItem::propertyBound() +{ + if (m_enum && isBound()) + m_enum->bind(App::ObjectIdentifier(getPath())<getTypeId().isDerivedFrom(App::PropertyEnumeration::getClassTypeId())) { + const App::PropertyEnumeration* prop_enum = static_cast(prop); + for(int i=0,last=prop_enum->getEnum().maxValue();i<=last;++i) + res.push_back(QString::fromUtf8(prop_enum->getEnums()[i])); + } + return res; } QVariant PropertyEnumItem::value(const App::Property* prop) const @@ -2719,25 +2749,40 @@ QVariant PropertyEnumItem::value(const App::Property* prop) const assert(prop && prop->getTypeId().isDerivedFrom(App::PropertyEnumeration::getClassTypeId())); const App::PropertyEnumeration* prop_enum = static_cast(prop); - const std::vector& value = prop_enum->getEnumVector(); - long currentItem = prop_enum->getValue(); - - if (currentItem < 0 || currentItem >= static_cast(value.size())) + if(!prop_enum->isValid()) return QVariant(QString()); - return QVariant(QString::fromUtf8(value[currentItem].c_str())); + return QVariant(QString::fromUtf8(prop_enum->getValueAsString())); } void PropertyEnumItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::StringList)) + if (hasExpression()) return; - QStringList items = value.toStringList(); - if (!items.isEmpty()) { - QByteArray val = items.front().toUtf8(); - std::string str = Base::Tools::escapedUnicodeFromUtf8(val); - QString data = QString::fromLatin1("u\"%1\"").arg(QString::fromStdString(str)); - setPropertyValue(data); + + QString data; + + if (value.type() == QVariant::StringList) { + QStringList values = value.toStringList(); + QTextStream str(&data); + str << "["; + for (QStringList::Iterator it = values.begin(); it != values.end(); ++it) { + QString text(*it); + text.replace(QString::fromUtf8("'"),QString::fromUtf8("\\'")); + + std::string pystr = Base::Tools::escapedUnicodeFromUtf8(text.toUtf8()); + pystr = Base::Interpreter().strToPython(pystr.c_str()); + str << "u\"" << pystr.c_str() << "\", "; + } + str << "]"; } + else if (value.canConvert(QVariant::String)) { + QByteArray val = value.toString().toUtf8(); + std::string str = Base::Tools::escapedUnicodeFromUtf8(val); + data = QString::fromLatin1("u\"%1\"").arg(QString::fromStdString(str)); + } + else + return; + setPropertyValue(data); } QWidget* PropertyEnumItem::createEditor(QWidget* parent, const QObject* receiver, const char* method) const @@ -2851,7 +2896,7 @@ QVariant PropertyStringListItem::value(const App::Property* prop) const void PropertyStringListItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::StringList)) + if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; @@ -2928,7 +2973,7 @@ QVariant PropertyFloatListItem::value(const App::Property* prop) const void PropertyFloatListItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::StringList)) + if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; @@ -3003,7 +3048,7 @@ QVariant PropertyIntegerListItem::value(const App::Property* prop) const void PropertyIntegerListItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::StringList)) + if (hasExpression() || !value.canConvert(QVariant::StringList)) return; QStringList values = value.toStringList(); QString data; @@ -3055,7 +3100,7 @@ QVariant PropertyColorItem::value(const App::Property* prop) const void PropertyColorItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; QColor col = value.value(); App::Color val; val.setValue(col); @@ -3347,7 +3392,7 @@ QVariant PropertyMaterialItem::value(const App::Property* prop) const void PropertyMaterialItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; Material mat = value.value(); @@ -3779,7 +3824,7 @@ QVariant PropertyMaterialListItem::value(const App::Property* prop) const void PropertyMaterialListItem::setValue(const QVariant& value) { - if (!value.canConvert()) + if (hasExpression() || !value.canConvert()) return; QVariantList list = value.toList(); @@ -3900,7 +3945,7 @@ QVariant PropertyFileItem::value(const App::Property* prop) const void PropertyFileItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::String)) + if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); @@ -3957,7 +4002,7 @@ QVariant PropertyPathItem::value(const App::Property* prop) const void PropertyPathItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::String)) + if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); @@ -4009,7 +4054,7 @@ QVariant PropertyTransientFileItem::value(const App::Property* prop) const void PropertyTransientFileItem::setValue(const QVariant& value) { - if (!value.canConvert(QVariant::String)) + if (hasExpression() || !value.canConvert(QVariant::String)) return; QString val = value.toString(); QString data = QString::fromLatin1("\"%1\"").arg(val); @@ -4267,7 +4312,6 @@ PropertyLinkListItem::PropertyLinkListItem() { } -// -------------------------------------------------------------------- PropertyItemEditorFactory::PropertyItemEditorFactory() { diff --git a/src/Gui/propertyeditor/PropertyItem.h b/src/Gui/propertyeditor/PropertyItem.h index 9483b4d3b6..c409a0be90 100644 --- a/src/Gui/propertyeditor/PropertyItem.h +++ b/src/Gui/propertyeditor/PropertyItem.h @@ -778,6 +778,8 @@ private: PropertyVectorDistanceItem* m_p; }; +class PropertyStringListItem; + /** * Edit properties of enum type. * \author Werner Mayer @@ -785,18 +787,26 @@ private: class GuiExport PropertyEnumItem: public PropertyItem { Q_OBJECT + Q_PROPERTY(QStringList Enum READ getEnum WRITE setEnum DESIGNABLE true USER true) PROPERTYITEM_HEADER virtual QWidget* createEditor(QWidget* parent, const QObject* receiver, const char* method) const; virtual void setEditorData(QWidget *editor, const QVariant& data) const; virtual QVariant editorData(QWidget *editor) const; + QStringList getEnum() const; + void setEnum(QStringList); + protected: virtual QVariant value(const App::Property*) const; virtual void setValue(const QVariant&); + virtual void propertyBound(); protected: PropertyEnumItem(); + +private: + PropertyStringListItem* m_enum; }; /** diff --git a/src/Mod/AddonManager/Resources/AddonManager.qrc b/src/Mod/AddonManager/Resources/AddonManager.qrc index 312e646a45..2c53e1db0a 100644 --- a/src/Mod/AddonManager/Resources/AddonManager.qrc +++ b/src/Mod/AddonManager/Resources/AddonManager.qrc @@ -58,6 +58,7 @@ icons/Silk_workbench_icon.svg icons/timber_workbench_icon.svg icons/ThreadProfile_workbench_icon.svg + icons/VendorParts_workbench_icon.svg icons/WebTools_workbench_icon.svg icons/workfeature_workbench_icon.svg icons/yaml-workspace_workbench_icon.svg diff --git a/src/Mod/AddonManager/Resources/icons/VendorParts_workbench_icon.svg b/src/Mod/AddonManager/Resources/icons/VendorParts_workbench_icon.svg new file mode 100644 index 0000000000..ce06f586b4 --- /dev/null +++ b/src/Mod/AddonManager/Resources/icons/VendorParts_workbench_icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/Mod/Draft/draftgeoutils/edges.py b/src/Mod/Draft/draftgeoutils/edges.py index 7bfda8256a..d3114b403f 100644 --- a/src/Mod/Draft/draftgeoutils/edges.py +++ b/src/Mod/Draft/draftgeoutils/edges.py @@ -164,36 +164,11 @@ def invert(shape): def findMidpoint(edge): - """Return the midpoint of a straight line or circular edge.""" - first = edge.Vertexes[0].Point - last = edge.Vertexes[-1].Point - - if geomType(edge) == "Circle": - center = edge.Curve.Center - radius = edge.Curve.Radius - if len(edge.Vertexes) == 1: - # Circle - dv = first.sub(center) - dv = dv.negative() - return center.add(dv) - - axis = edge.Curve.Axis - chord = last.sub(first) - perp = chord.cross(axis) - perp.normalize() - ray = first.sub(center) - apothem = ray.dot(perp) - sagitta = radius - apothem - startpoint = App.Vector.add(first, chord.multiply(0.5)) - endpoint = DraftVecUtils.scaleTo(perp, sagitta) - return App.Vector.add(startpoint, endpoint) - - elif geomType(edge) == "Line": - halfedge = (last.sub(first)).multiply(0.5) - return App.Vector.add(first, halfedge) - - else: + """Return the midpoint of an edge.""" + if edge.Length == 0: return None + else: + return edge.valueAt(edge.Curve.parameterAtDistance(edge.Length/2, edge.FirstParameter)) def getTangent(edge, from_point=None): @@ -248,4 +223,4 @@ def get_referenced_edges(property_value): isLine = is_line -## @} \ No newline at end of file +## @} diff --git a/src/Mod/Part/App/Geom2d/ArcOfConic2dPyImp.cpp b/src/Mod/Part/App/Geom2d/ArcOfConic2dPyImp.cpp index 6700dc71c8..cd4f261ce4 100644 --- a/src/Mod/Part/App/Geom2d/ArcOfConic2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/ArcOfConic2dPyImp.cpp @@ -59,13 +59,7 @@ int ArcOfConic2dPy::PyInit(PyObject* /*args*/, PyObject* /*kwds*/) Py::Object ArcOfConic2dPy::getLocation(void) const { Base::Vector2d loc = getGeom2dArcOfConicPtr()->getLocation(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.x)); - arg.setItem(1, Py::Float(loc.y)); - return method.apply(arg); + return Base::Vector2dPy::create(loc); } void ArcOfConic2dPy::setLocation(Py::Object arg) @@ -86,12 +80,7 @@ Py::Object ArcOfConic2dPy::getXAxis(void) const Handle(Geom2d_TrimmedCurve) curve = Handle(Geom2d_TrimmedCurve)::DownCast(getGeom2dArcOfConicPtr()->handle()); Handle(Geom2d_Conic) conic = Handle(Geom2d_Conic)::DownCast(curve->BasisCurve()); gp_Dir2d xdir = conic->XAxis().Direction(); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(xdir.X())); - arg.setItem(1, Py::Float(xdir.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(xdir.X(), xdir.Y()); } void ArcOfConic2dPy::setXAxis(Py::Object arg) @@ -109,12 +98,7 @@ Py::Object ArcOfConic2dPy::getYAxis(void) const Handle(Geom2d_TrimmedCurve) curve = Handle(Geom2d_TrimmedCurve)::DownCast(getGeom2dArcOfConicPtr()->handle()); Handle(Geom2d_Conic) conic = Handle(Geom2d_Conic)::DownCast(curve->BasisCurve()); gp_Dir2d ydir = conic->YAxis().Direction(); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(ydir.X())); - arg.setItem(1, Py::Float(ydir.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(ydir.X(), ydir.Y()); } void ArcOfConic2dPy::setYAxis(Py::Object arg) diff --git a/src/Mod/Part/App/Geom2d/BSplineCurve2dPyImp.cpp b/src/Mod/Part/App/Geom2d/BSplineCurve2dPyImp.cpp index 1c69f6211e..ad4e9dc098 100644 --- a/src/Mod/Part/App/Geom2d/BSplineCurve2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/BSplineCurve2dPyImp.cpp @@ -117,7 +117,6 @@ PyObject* BSplineCurve2dPy::increaseDegree(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -144,7 +143,6 @@ PyObject* BSplineCurve2dPy::increaseMultiplicity(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -162,7 +160,6 @@ PyObject* BSplineCurve2dPy::incrementMultiplicity(PyObject * args) curve->IncrementMultiplicity(start, end, mult); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -183,7 +180,6 @@ PyObject* BSplineCurve2dPy::insertKnot(PyObject * args) curve->InsertKnot(U,M,tol); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -224,7 +220,6 @@ PyObject* BSplineCurve2dPy::insertKnots(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -246,7 +241,6 @@ PyObject* BSplineCurve2dPy::removeKnot(PyObject * args) return PyBool_FromLong(ok ? 1 : 0); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -264,7 +258,6 @@ PyObject* BSplineCurve2dPy::segment(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -322,7 +315,6 @@ PyObject* BSplineCurve2dPy::setKnots(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -344,7 +336,6 @@ PyObject* BSplineCurve2dPy::getKnots(PyObject * args) return Py::new_reference_to(knots); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -369,7 +360,6 @@ PyObject* BSplineCurve2dPy::setPole(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -386,16 +376,9 @@ PyObject* BSplineCurve2dPy::getPole(PyObject * args) Standard_OutOfRange_Raise_if (index < 1 || index > curve->NbPoles(), "Pole index out of range"); gp_Pnt2d pnt = curve->Pole(index); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(pnt.X(), pnt.Y())); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -412,20 +395,13 @@ PyObject* BSplineCurve2dPy::getPoles(PyObject * args) curve->Poles(p); Py::List poles; - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (Standard_Integer i=p.Lower(); i<=p.Upper(); i++) { gp_Pnt2d pnt = p(i); - - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - poles.append(method.apply(arg)); + poles.append(Base::Vector2dPy::create(pnt.X(), pnt.Y())); } return Py::new_reference_to(poles); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -456,7 +432,6 @@ PyObject* BSplineCurve2dPy::getPolesAndWeights(PyObject * args) return Py::new_reference_to(poles); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -475,7 +450,6 @@ PyObject* BSplineCurve2dPy::setWeight(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -495,7 +469,6 @@ PyObject* BSplineCurve2dPy::getWeight(PyObject * args) return Py_BuildValue("d", weight); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -517,7 +490,6 @@ PyObject* BSplineCurve2dPy::getWeights(PyObject * args) return Py::new_reference_to(weights); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -536,7 +508,6 @@ PyObject* BSplineCurve2dPy::getResolution(PyObject * args) return Py_BuildValue("d",utol); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -558,7 +529,6 @@ PyObject* BSplineCurve2dPy::movePoint(PyObject * args) return Py_BuildValue("(ii)",first, last); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -575,7 +545,6 @@ PyObject* BSplineCurve2dPy::setNotPeriodic(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -592,7 +561,6 @@ PyObject* BSplineCurve2dPy::setPeriodic(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -610,7 +578,6 @@ PyObject* BSplineCurve2dPy::setOrigin(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -650,7 +617,6 @@ PyObject* BSplineCurve2dPy::getMultiplicities(PyObject * args) return Py::new_reference_to(mults); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -689,13 +655,7 @@ Py::Object BSplineCurve2dPy::getStartPoint(void) const Handle(Geom2d_BSplineCurve) c = Handle(Geom2d_BSplineCurve)::DownCast (getGeometry2dPtr()->handle()); gp_Pnt2d pnt = c->StartPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } Py::Object BSplineCurve2dPy::getEndPoint(void) const @@ -703,13 +663,7 @@ Py::Object BSplineCurve2dPy::getEndPoint(void) const Handle(Geom2d_BSplineCurve) c = Handle(Geom2d_BSplineCurve)::DownCast (getGeometry2dPtr()->handle()); gp_Pnt2d pnt = c->EndPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } Py::Object BSplineCurve2dPy::getFirstUKnotIndex(void) const @@ -769,7 +723,6 @@ PyObject* BSplineCurve2dPy::toBiArcs(PyObject * args) return Py::new_reference_to(list); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -890,7 +843,6 @@ PyObject* BSplineCurve2dPy::approximate(PyObject *args, PyObject *kwds) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -917,13 +869,8 @@ PyObject* BSplineCurve2dPy::getCardinalSplineTangents(PyObject *args, PyObject * bspline->getCardinalSplineTangents(interpPoints, parameter, tangents); Py::List vec; - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (gp_Vec2d it : tangents) { - arg.setItem(0, Py::Float(it.X())); - arg.setItem(1, Py::Float(it.Y())); - vec.append(method.apply(arg)); + vec.append(Base::Vector2dPy::create(it.X(), it.Y())); } return Py::new_reference_to(vec); } @@ -952,18 +899,13 @@ PyObject* BSplineCurve2dPy::getCardinalSplineTangents(PyObject *args, PyObject * bspline->getCardinalSplineTangents(interpPoints, parameters, tangents); Py::List vec; - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (gp_Vec2d it : tangents) { - arg.setItem(0, Py::Float(it.X())); - arg.setItem(1, Py::Float(it.Y())); - vec.append(method.apply(arg)); + vec.append(Base::Vector2dPy::create(it.X(), it.Y())); } return Py::new_reference_to(vec); } - return 0; + return nullptr; } PyObject* BSplineCurve2dPy::interpolate(PyObject *args, PyObject *kwds) @@ -1057,7 +999,6 @@ PyObject* BSplineCurve2dPy::interpolate(PyObject *args, PyObject *kwds) } } catch (Standard_Failure& e) { - std::string err = e.GetMessageString(); if (err.empty()) err = e.DynamicType()->Name(); PyErr_SetString(PartExceptionOCCError, err.c_str()); @@ -1137,7 +1078,6 @@ PyObject* BSplineCurve2dPy::buildFromPoles(PyObject *args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } diff --git a/src/Mod/Part/App/Geom2d/BezierCurve2dPyImp.cpp b/src/Mod/Part/App/Geom2d/BezierCurve2dPyImp.cpp index 798c0e4e6d..a1a22a9867 100644 --- a/src/Mod/Part/App/Geom2d/BezierCurve2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/BezierCurve2dPyImp.cpp @@ -113,7 +113,6 @@ PyObject* BezierCurve2dPy::insertPoleAfter(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -135,7 +134,6 @@ PyObject* BezierCurve2dPy::insertPoleBefore(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -153,7 +151,6 @@ PyObject* BezierCurve2dPy::removePole(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -171,7 +168,6 @@ PyObject* BezierCurve2dPy::segment(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -196,7 +192,6 @@ PyObject* BezierCurve2dPy::setPole(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -213,16 +208,9 @@ PyObject* BezierCurve2dPy::getPole(PyObject * args) Standard_OutOfRange_Raise_if (index < 1 || index > curve->NbPoles(), "Pole index out of range"); gp_Pnt2d pnt = curve->Pole(index); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(pnt.X(), pnt.Y())); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -239,20 +227,13 @@ PyObject* BezierCurve2dPy::getPoles(PyObject * args) curve->Poles(p); Py::List poles; - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (Standard_Integer i=p.Lower(); i<=p.Upper(); i++) { gp_Pnt2d pnt = p(i); - - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - poles.append(method.apply(arg)); + poles.append(Base::Vector2dPy::create(pnt.X(), pnt.Y())); } return Py::new_reference_to(poles); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -277,7 +258,6 @@ PyObject* BezierCurve2dPy::setPoles(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -296,7 +276,6 @@ PyObject* BezierCurve2dPy::setWeight(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -316,7 +295,6 @@ PyObject* BezierCurve2dPy::getWeight(PyObject * args) return Py_BuildValue("d", weight); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -338,7 +316,6 @@ PyObject* BezierCurve2dPy::getWeights(PyObject * args) return Py::new_reference_to(weights); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -357,7 +334,6 @@ PyObject* BezierCurve2dPy::getResolution(PyObject* args) return Py_BuildValue("d",utol); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -388,13 +364,7 @@ Py::Object BezierCurve2dPy::getStartPoint(void) const Handle(Geom2d_BezierCurve) c = Handle(Geom2d_BezierCurve)::DownCast (getGeometry2dPtr()->handle()); gp_Pnt2d pnt = c->StartPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } Py::Object BezierCurve2dPy::getEndPoint(void) const @@ -402,13 +372,7 @@ Py::Object BezierCurve2dPy::getEndPoint(void) const Handle(Geom2d_BezierCurve) c = Handle(Geom2d_BezierCurve)::DownCast (getGeometry2dPtr()->handle()); gp_Pnt2d pnt = c->EndPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } PyObject *BezierCurve2dPy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/Part/App/Geom2d/Conic2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Conic2dPyImp.cpp index 6b7517e0c8..3615865b05 100644 --- a/src/Mod/Part/App/Geom2d/Conic2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Conic2dPyImp.cpp @@ -57,13 +57,7 @@ int Conic2dPy::PyInit(PyObject* /*args*/, PyObject* /*kwds*/) Py::Object Conic2dPy::getLocation(void) const { Base::Vector2d loc = getGeom2dConicPtr()->getLocation(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.x)); - arg.setItem(1, Py::Float(loc.y)); - return method.apply(arg); + return Base::Vector2dPy::create(loc); } void Conic2dPy::setLocation(Py::Object arg) @@ -82,12 +76,7 @@ Py::Object Conic2dPy::getXAxis(void) const { Handle(Geom2d_Conic) conic = Handle(Geom2d_Conic)::DownCast(getGeom2dConicPtr()->handle()); gp_Dir2d xdir = conic->XAxis().Direction(); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(xdir.X())); - arg.setItem(1, Py::Float(xdir.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(xdir.X(), xdir.Y()); } void Conic2dPy::setXAxis(Py::Object arg) @@ -103,12 +92,7 @@ Py::Object Conic2dPy::getYAxis(void) const { Handle(Geom2d_Conic) conic = Handle(Geom2d_Conic)::DownCast(getGeom2dConicPtr()->handle()); gp_Dir2d ydir = conic->YAxis().Direction(); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(ydir.X())); - arg.setItem(1, Py::Float(ydir.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(ydir.X(), ydir.Y()); } void Conic2dPy::setYAxis(Py::Object arg) diff --git a/src/Mod/Part/App/Geom2d/Curve2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Curve2dPyImp.cpp index a157a6374f..c64aa88ec2 100644 --- a/src/Mod/Part/App/Geom2d/Curve2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Curve2dPyImp.cpp @@ -107,7 +107,6 @@ PyObject* Curve2dPy::reverse(PyObject * args) Py_Return; } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -187,7 +186,6 @@ PyObject* Curve2dPy::toShape(PyObject *args) return Py::new_reference_to(shape2pyshape(edge)); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -204,7 +202,6 @@ PyObject* Curve2dPy::toShape(PyObject *args) return Py::new_reference_to(shape2pyshape(edge)); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -225,7 +222,6 @@ PyObject* Curve2dPy::toShape(PyObject *args) return Py::new_reference_to(shape2pyshape(edge)); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -265,7 +261,6 @@ PyObject* Curve2dPy::toShape(PyObject *args) return Py::new_reference_to(shape2pyshape(edge)); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -285,7 +280,6 @@ PyObject* Curve2dPy::toShape(PyObject *args) return Py::new_reference_to(shape2pyshape(edge)); } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -321,14 +315,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) Py::List points; int nbPoints = discretizer.NbPoints (); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt2d p = adapt.Value (discretizer.Parameter (i)); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -351,14 +340,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) Py::List points; int nbPoints = discretizer.NbPoints (); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt2d p = adapt.Value (discretizer.Parameter (i)); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -379,14 +363,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) Py::List points; int nbPoints = discretizer.NbPoints (); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt p = discretizer.Value (i); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -409,14 +388,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) Py::List points; int nbPoints = discretizer.NbPoints (); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt p = discretizer.Value (i); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -437,14 +411,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) Py::List points; int nbPoints = discretizer.NbPoints (); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt2d p = adapt.Value (discretizer.Parameter (i)); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -464,15 +433,9 @@ PyObject* Curve2dPy::discretize(PyObject *args, PyObject *kwds) if (discretizer.NbPoints () > 0) { Py::List points; int nbPoints = discretizer.NbPoints (); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); for (int i=1; i<=nbPoints; i++) { gp_Pnt p = discretizer.Value (i); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p.X(), p.Y())); } return Py::new_reference_to(points); @@ -509,7 +472,6 @@ PyObject* Curve2dPy::length(PyObject *args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -535,7 +497,6 @@ PyObject* Curve2dPy::parameterAtDistance(PyObject *args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -554,17 +515,10 @@ PyObject* Curve2dPy::value(PyObject *args) if (!PyArg_ParseTuple(args, "d", &u)) return 0; gp_Pnt2d p = c->Value(u); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(p.X())); - arg.setItem(1, Py::Float(p.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(p.X(), p.Y())); } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -588,16 +542,10 @@ PyObject* Curve2dPy::tangent(PyObject *args) prop.Tangent(dir); } - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(dir.X())); - arg.setItem(1, Py::Float(dir.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(dir.X(), dir.Y())); } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -619,16 +567,10 @@ PyObject* Curve2dPy::normal(PyObject *args) Geom2dLProp_CLProps2d prop(c,u,2,Precision::Confusion()); prop.Normal(dir); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(dir.X())); - arg.setItem(1, Py::Float(dir.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(dir.X(), dir.Y())); } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -652,7 +594,6 @@ PyObject* Curve2dPy::curvature(PyObject *args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -674,16 +615,10 @@ PyObject* Curve2dPy::centerOfCurvature(PyObject *args) gp_Pnt2d pnt ; prop.CentreOfCurvature(pnt); - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return Py::new_reference_to(method.apply(arg)); + return Py::new_reference_to(Base::Vector2dPy::create(pnt.X(), pnt.Y())); } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -737,7 +672,6 @@ PyObject* Curve2dPy::toBSpline(PyObject * args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -791,7 +725,6 @@ PyObject* Curve2dPy::approximateBSpline(PyObject *args) } } catch (Standard_Failure& e) { - PyErr_SetString(PartExceptionOCCError, e.GetMessageString()); return 0; } @@ -874,11 +807,9 @@ PyObject* Curve2dPy::intersectCC(PyObject *args) double prec = Precision::Confusion(); if (!PyArg_ParseTuple(args, "O!|d", &(Part::Curve2dPy::Type), &p, &prec)) return 0; + Handle(Geom2d_Curve) curve2 = Handle(Geom2d_Curve)::DownCast(static_cast(p)->getGeometry2dPtr()->handle()); Py::List points; - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); Geom2dAPI_InterCurveCurve intersector(curve1, curve2, prec); if ((intersector.NbPoints() == 0) && (intersector.NbSegments() == 0)) { // No intersection @@ -888,9 +819,7 @@ PyObject* Curve2dPy::intersectCC(PyObject *args) // Cross intersections for (int i = 1; i <= intersector.NbPoints(); i++) { gp_Pnt2d p1 = intersector.Point(i); - arg.setItem(0, Py::Float(p1.X())); - arg.setItem(1, Py::Float(p1.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p1.X(), p1.Y())); } } if (intersector.NbSegments() > 0) { @@ -905,17 +834,13 @@ PyObject* Curve2dPy::intersectCC(PyObject *args) continue; gp_Pnt2d p1, p2; intersector2.Points(i, p1, p2); - - arg.setItem(0, Py::Float(p1.X())); - arg.setItem(1, Py::Float(p1.Y())); - points.append(method.apply(arg)); + points.append(Base::Vector2dPy::create(p1.X(), p1.Y())); } } return Py::new_reference_to(points); } } catch (Standard_Failure& e) { - PyErr_SetString(PyExc_RuntimeError, e.GetMessageString()); return 0; } diff --git a/src/Mod/Part/App/Geom2d/Ellipse2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Ellipse2dPyImp.cpp index a6ea215fc5..9dd7033d63 100644 --- a/src/Mod/Part/App/Geom2d/Ellipse2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Ellipse2dPyImp.cpp @@ -160,26 +160,14 @@ Py::Object Ellipse2dPy::getFocus1(void) const { Handle(Geom2d_Ellipse) ellipse = Handle(Geom2d_Ellipse)::DownCast(getGeom2dEllipsePtr()->handle()); gp_Pnt2d loc = ellipse->Focus1(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.X())); - arg.setItem(1, Py::Float(loc.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(loc.X(), loc.Y()); } Py::Object Ellipse2dPy::getFocus2(void) const { Handle(Geom2d_Ellipse) ellipse = Handle(Geom2d_Ellipse)::DownCast(getGeom2dEllipsePtr()->handle()); gp_Pnt2d loc = ellipse->Focus2(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.X())); - arg.setItem(1, Py::Float(loc.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(loc.X(), loc.Y()); } PyObject *Ellipse2dPy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/Part/App/Geom2d/Hyperbola2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Hyperbola2dPyImp.cpp index 8a0955eadc..067d682736 100644 --- a/src/Mod/Part/App/Geom2d/Hyperbola2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Hyperbola2dPyImp.cpp @@ -160,26 +160,14 @@ Py::Object Hyperbola2dPy::getFocus1(void) const { Handle(Geom2d_Hyperbola) hyperbola = Handle(Geom2d_Hyperbola)::DownCast(getGeom2dHyperbolaPtr()->handle()); gp_Pnt2d loc = hyperbola->Focus1(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.X())); - arg.setItem(1, Py::Float(loc.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(loc.X(), loc.Y()); } Py::Object Hyperbola2dPy::getFocus2(void) const { Handle(Geom2d_Hyperbola) hyperbola = Handle(Geom2d_Hyperbola)::DownCast(getGeom2dHyperbolaPtr()->handle()); gp_Pnt2d loc = hyperbola->Focus2(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.X())); - arg.setItem(1, Py::Float(loc.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(loc.X(), loc.Y()); } PyObject *Hyperbola2dPy::getCustomAttributes(const char* /*attr*/) const diff --git a/src/Mod/Part/App/Geom2d/Line2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Line2dPyImp.cpp index e9269d0cfe..47fc874f9e 100644 --- a/src/Mod/Part/App/Geom2d/Line2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Line2dPyImp.cpp @@ -126,13 +126,7 @@ Py::Object Line2dPy::getLocation(void) const Handle(Geom2d_Line) this_curve = Handle(Geom2d_Line)::DownCast (this->getGeom2dLinePtr()->handle()); gp_Pnt2d pnt = this_curve->Location(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } void Line2dPy::setLocation(Py::Object arg) @@ -180,13 +174,7 @@ Py::Object Line2dPy::getDirection(void) const Handle(Geom2d_Line) this_curve = Handle(Geom2d_Line)::DownCast (this->getGeom2dLinePtr()->handle()); gp_Dir2d dir = this_curve->Direction(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(dir.X())); - arg.setItem(1, Py::Float(dir.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(dir.X(), dir.Y()); } void Line2dPy::setDirection(Py::Object arg) diff --git a/src/Mod/Part/App/Geom2d/Line2dSegmentPyImp.cpp b/src/Mod/Part/App/Geom2d/Line2dSegmentPyImp.cpp index d7249de90b..11334584dd 100644 --- a/src/Mod/Part/App/Geom2d/Line2dSegmentPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Line2dSegmentPyImp.cpp @@ -201,13 +201,7 @@ Py::Object Line2dSegmentPy::getStartPoint(void) const Handle(Geom2d_TrimmedCurve) this_curve = Handle(Geom2d_TrimmedCurve)::DownCast (this->getGeom2dLineSegmentPtr()->handle()); gp_Pnt2d pnt = this_curve->StartPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } void Line2dSegmentPy::setStartPoint(Py::Object arg) @@ -261,13 +255,7 @@ Py::Object Line2dSegmentPy::getEndPoint(void) const Handle(Geom2d_TrimmedCurve) this_curve = Handle(Geom2d_TrimmedCurve)::DownCast (this->getGeom2dLineSegmentPtr()->handle()); gp_Pnt2d pnt = this_curve->EndPoint(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(pnt.X())); - arg.setItem(1, Py::Float(pnt.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(pnt.X(), pnt.Y()); } void Line2dSegmentPy::setEndPoint(Py::Object arg) diff --git a/src/Mod/Part/App/Geom2d/Parabola2dPyImp.cpp b/src/Mod/Part/App/Geom2d/Parabola2dPyImp.cpp index b012c97396..e41443318c 100644 --- a/src/Mod/Part/App/Geom2d/Parabola2dPyImp.cpp +++ b/src/Mod/Part/App/Geom2d/Parabola2dPyImp.cpp @@ -76,13 +76,7 @@ Py::Object Parabola2dPy::getFocus(void) const { Handle(Geom2d_Parabola) curve = Handle(Geom2d_Parabola)::DownCast(getGeometry2dPtr()->handle()); gp_Pnt2d loc = curve->Focus(); - - Py::Module module("__FreeCADBase__"); - Py::Callable method(module.getAttr("Vector2d")); - Py::Tuple arg(2); - arg.setItem(0, Py::Float(loc.X())); - arg.setItem(1, Py::Float(loc.Y())); - return method.apply(arg); + return Base::Vector2dPy::create(loc.X(), loc.Y()); } Py::Float Parabola2dPy::getParameter(void) const diff --git a/src/Mod/PartDesign/App/FeatureLoft.cpp b/src/Mod/PartDesign/App/FeatureLoft.cpp index 670b9aff5b..811fd972eb 100644 --- a/src/Mod/PartDesign/App/FeatureLoft.cpp +++ b/src/Mod/PartDesign/App/FeatureLoft.cpp @@ -123,7 +123,7 @@ App::DocumentObjectExecReturn *Loft::execute(void) if (subValues.empty()) throw Base::ValueError("Loft: No valid subelement linked in Part::Feature"); - shape = static_cast(obj)->Shape.getShape(). getSubShape(subValues[0].c_str()); + shape = static_cast(obj)->Shape.getShape().getSubShape(subValues[0].c_str()); } TopExp_Explorer ex; diff --git a/src/Mod/PartDesign/App/FeatureSketchBased.h b/src/Mod/PartDesign/App/FeatureSketchBased.h index bf3c1964d1..52e1b12f17 100644 --- a/src/Mod/PartDesign/App/FeatureSketchBased.h +++ b/src/Mod/PartDesign/App/FeatureSketchBased.h @@ -89,7 +89,7 @@ public: /** * Verifies the linked Object and returns the shape used as profile - * @param silent if profirle property is malformed and the parameter is true + * @param silent if profile property is malformed and the parameter is true * silently returns nullptr, otherwise throw a Base::Exception. * Default is false. */ diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp index bcbc1a3139..30e29bbc9a 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.cpp @@ -640,7 +640,8 @@ bool TaskPadParameters::getAlongSketchNormal(void) const bool TaskPadParameters::getCustom(void) const { - return ui->checkBoxDirection->isChecked(); + // index 2 is hardcoded to custom vector + return ui->directionCB->currentIndex() == 2 ? true : false; } std::string TaskPadParameters::getReferenceAxis(void) const diff --git a/src/Mod/PartDesign/Gui/TaskPadParameters.ui b/src/Mod/PartDesign/Gui/TaskPadParameters.ui index 0946923afe..026cd8cd77 100644 --- a/src/Mod/PartDesign/Gui/TaskPadParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPadParameters.ui @@ -49,6 +49,23 @@ + + + + Offset to face + + + + + + + Offset from face at which pad will end + + + false + + + @@ -224,27 +241,6 @@ measured along the specified direction - - - - - - Offset to face - - - - - - - Offset from face in which pad will end - - - false - - - - - @@ -336,6 +332,23 @@ measured along the specified direction
Gui/PrefWidgets.h
+ + changeMode + lengthEdit + offsetEdit + directionCB + checkBoxAlongDirection + checkBoxMidplane + checkBoxReversed + lengthEdit2 + buttonFace + lineFaceName + checkBoxUpdateView + checkBoxDirection + XDirectionEdit + YDirectionEdit + ZDirectionEdit + diff --git a/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp b/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp index a16d614ca1..9fbede1aa8 100644 --- a/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp +++ b/src/Mod/PartDesign/Gui/TaskPocketParameters.cpp @@ -650,7 +650,8 @@ bool TaskPocketParameters::getAlongSketchNormal(void) const bool TaskPocketParameters::getCustom(void) const { - return ui->checkBoxDirection->isChecked(); + // index 2 is hardcoded to custom vector + return ui->directionCB->currentIndex() == 2 ? true : false; } std::string TaskPocketParameters::getReferenceAxis(void) const diff --git a/src/Mod/PartDesign/Gui/TaskPocketParameters.ui b/src/Mod/PartDesign/Gui/TaskPocketParameters.ui index 322dba0fa5..4b1171b122 100644 --- a/src/Mod/PartDesign/Gui/TaskPocketParameters.ui +++ b/src/Mod/PartDesign/Gui/TaskPocketParameters.ui @@ -52,12 +52,15 @@ - Offset + Offset to face + + Offset from face at which pocket will end + false @@ -327,12 +330,18 @@ measured along the specified direction changeMode lengthEdit offsetEdit + directionCB + checkBoxAlongDirection checkBoxMidplane checkBoxReversed lengthEdit2 buttonFace lineFaceName checkBoxUpdateView + checkBoxDirection + XDirectionEdit + YDirectionEdit + ZDirectionEdit diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 265f8d7697..571453c1a1 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -1023,38 +1023,39 @@ void PropertySheet::addDependencies(CellAddress key) if (expression == 0) return; - for(auto &dep : expression->getDeps()) { + for(auto &var : expression->getIdentifiers()) { + for(auto &dep : var.first.getDep(true)) { + App::DocumentObject *docObj = dep.first; + App::Document *doc = docObj->getDocument(); - App::DocumentObject *docObj = dep.first; - App::Document *doc = docObj->getDocument(); + std::string docObjName = docObj->getFullName(); - std::string docObjName = docObj->getFullName(); + owner->observeDocument(doc); - owner->observeDocument(doc); + documentObjectToCellMap[docObjName].insert(key); + cellToDocumentObjectMap[key].insert(docObjName); + ++updateCount; - documentObjectToCellMap[docObjName].insert(key); - cellToDocumentObjectMap[key].insert(docObjName); - ++updateCount; + for(auto &name : dep.second) { + std::string propName = docObjName + "." + name; + FC_LOG("dep " << key.toString() << " -> " << name); - for(auto &props : dep.second) { - std::string propName = docObjName + "." + props.first; - FC_LOG("dep " << key.toString() << " -> " << propName); + // Insert into maps + propertyNameToCellMap[propName].insert(key); + cellToPropertyNameMap[key].insert(propName); - // Insert into maps - propertyNameToCellMap[propName].insert(key); - cellToPropertyNameMap[key].insert(propName); + // Also an alias? + if (docObj==owner && name.size()) { + auto j = revAliasProp.find(name); - // Also an alias? - if (docObj==owner && props.first.size()) { - std::map::const_iterator j = revAliasProp.find(props.first); + if (j != revAliasProp.end()) { + propName = docObjName + "." + j->second.toString(); + FC_LOG("dep " << key.toString() << " -> " << propName); - if (j != revAliasProp.end()) { - propName = docObjName + "." + j->second.toString(); - FC_LOG("dep " << key.toString() << " -> " << propName); - - // Insert into maps - propertyNameToCellMap[propName].insert(key); - cellToPropertyNameMap[key].insert(propName); + // Insert into maps + propertyNameToCellMap[propName].insert(key); + cellToPropertyNameMap[key].insert(propName); + } } } } @@ -1123,6 +1124,16 @@ void PropertySheet::removeDependencies(CellAddress key) void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const char *propName) { + auto itD = _Deps.find(const_cast(owner)); + if(itD!=_Deps.end() && itD->second) { + // Check for hidden reference. Because a hidden reference is not + // protected by cyclic dependency checking, we need to take special + // care to prevent it from misbehave. + Sheet *sheet = Base::freecad_dynamic_cast(getContainer()); + if(!sheet || sheet->testStatus(App::ObjectStatus::Recompute2)) + return; + } + // First, search without actual property name for sub-object/link // references, i.e indirect references. The dependencies of these // references are too complex to track exactly, so we only track the @@ -1135,7 +1146,7 @@ void PropertySheet::recomputeDependants(const App::DocumentObject *owner, const setDirty(cell); } - if (propName) { + if (propName && *propName) { // Now, we check for direct property references it = propertyNameToCellMap.find(fullName + propName); if (it != propertyNameToCellMap.end()) { @@ -1302,7 +1313,7 @@ void PropertySheet::hasSetValue() updateCount = 0; - std::set deps; + std::map deps; std::vector labels; unregisterElementReference(); UpdateElementReferenceExpressionVisitor v(*this); @@ -1384,8 +1395,9 @@ bool PropertySheet::adjustLink(const std::set &inList) { continue; try { bool need_adjust = false; - for(auto docObj : expr->getDepObjects()) { - if (docObj && docObj != owner && inList.count(docObj)) { + for(auto &v : expr->getDepObjects()) { + auto docObj = v.first; + if (v.second && docObj && docObj!=owner && inList.count(docObj)) { need_adjust = true; break; } diff --git a/src/Mod/Spreadsheet/Gui/AppSpreadsheetGui.cpp b/src/Mod/Spreadsheet/Gui/AppSpreadsheetGui.cpp index 8b276dbf5e..fd0e01d708 100644 --- a/src/Mod/Spreadsheet/Gui/AppSpreadsheetGui.cpp +++ b/src/Mod/Spreadsheet/Gui/AppSpreadsheetGui.cpp @@ -118,8 +118,10 @@ PyMOD_INIT_FUNC(SpreadsheetGui) CreateSpreadsheetCommands(); SpreadsheetGui::ViewProviderSheet::init(); + SpreadsheetGui::ViewProviderSheetPython::init(); SpreadsheetGui::Workbench::init(); SpreadsheetGui::SheetView::init(); + SpreadsheetGui::SheetViewPy::init_type(); // register preference page new Gui::PrefPageProducer ("Spreadsheet"); diff --git a/src/Mod/Spreadsheet/Gui/CMakeLists.txt b/src/Mod/Spreadsheet/Gui/CMakeLists.txt index fbfde0a87d..61ff766fa2 100644 --- a/src/Mod/Spreadsheet/Gui/CMakeLists.txt +++ b/src/Mod/Spreadsheet/Gui/CMakeLists.txt @@ -9,10 +9,12 @@ include_directories( ) generate_from_xml(SpreadsheetViewPy) +generate_from_xml(ViewProviderSpreadsheetPy) # The XML files set(SpreadsheetGui_XML_SRCS SpreadsheetViewPy.xml + ViewProviderSpreadsheetPy.xml ) set(SpreadsheetGui_LIBS @@ -71,6 +73,7 @@ SET(SpreadsheetGui_SRCS LineEdit.cpp ViewProviderSpreadsheet.cpp ViewProviderSpreadsheet.h + ViewProviderSpreadsheetPyImp.cpp Resources/Spreadsheet.qrc SpreadsheetView.cpp SpreadsheetView.h diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index 4103c2fd91..3895df45ca 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include "qtcolorpicker.h" @@ -419,6 +420,16 @@ QModelIndexList SheetView::selectedIndexes() const return ui->cells->selectionModel()->selectedIndexes(); } +void SpreadsheetGui::SheetView::select(App::CellAddress cell, QItemSelectionModel::SelectionFlags flags) +{ + ui->cells->selectionModel()->select(model->index(cell.row(), cell.col()), flags); +} + +void SpreadsheetGui::SheetView::select(App::CellAddress topLeft, App::CellAddress bottomRight, QItemSelectionModel::SelectionFlags flags) +{ + ui->cells->selectionModel()->select(QItemSelection(model->index(topLeft.row(), topLeft.col()), model->index(bottomRight.row(), bottomRight.col())), flags); +} + void SheetView::deleteSelection() { ui->cells->deleteSelection(); @@ -429,10 +440,15 @@ QModelIndex SheetView::currentIndex() const return ui->cells->currentIndex(); } +void SpreadsheetGui::SheetView::setCurrentIndex(App::CellAddress cell) const +{ + ui->cells->setCurrentIndex(model->index(cell.row(), cell.col())); +} + PyObject *SheetView::getPyObject() { if (!pythonObject) - pythonObject = new SpreadsheetViewPy(this); + pythonObject = new SheetViewPy(this); Py_INCREF(pythonObject); return pythonObject; @@ -443,4 +459,76 @@ void SheetView::deleteSelf() Gui::MDIView::deleteSelf(); } +// ---------------------------------------------------------- + +void SheetViewPy::init_type() +{ + behaviors().name("SheetViewPy"); + behaviors().doc("Python binding class for the Sheet view class"); + // you must have overwritten the virtual functions + behaviors().supportRepr(); + behaviors().supportGetattr(); + behaviors().supportSetattr(); + + add_varargs_method("getSheet", &SheetViewPy::getSheet, "getSheet()"); + behaviors().readyType(); +} + +SheetViewPy::SheetViewPy(SheetView *mdi) + : base(mdi) +{ +} + +SheetViewPy::~SheetViewPy() +{ +} + +Py::Object SheetViewPy::repr() +{ + std::ostringstream s_out; + if (!getSheetViewPtr()) + throw Py::RuntimeError("Cannot print representation of deleted object"); + s_out << "SheetView"; + return Py::String(s_out.str()); +} + +// Since with PyCXX it's not possible to make a sub-class of MDIViewPy +// a trick is to use MDIViewPy as class member and override getattr() to +// join the attributes of both classes. This way all methods of MDIViewPy +// appear for SheetViewPy, too. +Py::Object SheetViewPy::getattr(const char * attr) +{ + if (!getSheetViewPtr()) + throw Py::RuntimeError("Cannot print representation of deleted object"); + std::string name( attr ); + if (name == "__dict__" || name == "__class__") { + Py::Dict dict_self(BaseType::getattr("__dict__")); + Py::Dict dict_base(base.getattr("__dict__")); + for (auto it : dict_base) { + dict_self.setItem(it.first, it.second); + } + return dict_self; + } + + try { + return BaseType::getattr(attr); + } + catch (Py::AttributeError& e) { + e.clear(); + return base.getattr(attr); + } +} + +SheetView* SheetViewPy::getSheetViewPtr() +{ + return qobject_cast(base.getMDIViewPtr()); +} + +Py::Object SheetViewPy::getSheet(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::asObject(new Spreadsheet::SheetPy(getSheetViewPtr()->getSheet())); +} + #include "moc_SpreadsheetView.cpp" diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h index 141cdfe59b..03691eace0 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h @@ -24,6 +24,7 @@ #define SpreadsheetView_H #include +#include #include #include "SheetModel.h" #include @@ -73,8 +74,14 @@ public: QModelIndexList selectedIndexes() const; + void select(App::CellAddress cell, QItemSelectionModel::SelectionFlags flags); + + void select(App::CellAddress topLeft, App::CellAddress bottomRight, QItemSelectionModel::SelectionFlags flags); + QModelIndex currentIndex() const; + void setCurrentIndex(App::CellAddress cell) const; + void deleteSelection(); PyObject *getPyObject(void); @@ -111,6 +118,25 @@ protected: std::map newRowSizes; }; +class SheetViewPy : public Py::PythonExtension +{ +public: + using BaseType = Py::PythonExtension; + static void init_type(); + + SheetViewPy(SheetView *mdi); + ~SheetViewPy(); + + Py::Object repr(); + Py::Object getattr(const char *); + Py::Object getSheet(const Py::Tuple&); + + SheetView* getSheetViewPtr(); + +protected: + Gui::MDIViewPy base; +}; + } // namespace SpreadsheetModGui #endif // SpreadsheetView_H diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetViewPyImp.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetViewPyImp.cpp index d4a6c0e9e1..08b47d8e5d 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetViewPyImp.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetViewPyImp.cpp @@ -2,6 +2,7 @@ #include "SpreadsheetViewPy.h" #include "SpreadsheetViewPy.cpp" +#include "ui_Sheet.h" #include diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp index d78143413d..f98b8d9710 100644 --- a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.cpp @@ -36,6 +36,7 @@ #include "ViewProviderSpreadsheet.h" #include "SpreadsheetView.h" +#include "ViewProviderSpreadsheetPy.h" #include #include @@ -181,3 +182,22 @@ void ViewProviderSheet::updateData(const App::Property* prop) if (view) view->updateCell(prop); } + +PyObject *ViewProviderSheet::getPyObject() +{ + if (!pyViewObject) + pyViewObject = new ViewProviderSpreadsheetPy(this); + pyViewObject->IncRef(); + return pyViewObject; +} + +// Python feature ----------------------------------------------------------------------- + +namespace Gui { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(SpreadsheetGui::ViewProviderSheetPython, SpreadsheetGui::ViewProviderSheet) +/// @endcond + +// explicit template instantiation +template class SpreadsheetGuiExport ViewProviderPythonFeatureT; +} diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h index 9602254d9a..beffa9ecb1 100644 --- a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheet.h @@ -26,6 +26,7 @@ #define SPREADSHEET_ViewProviderImagePlane_H #include +#include #include namespace Spreadsheet { @@ -67,6 +68,10 @@ public: virtual Gui::MDIView *getMDIView() const override; + inline SheetView* getView() const { return view; } + + PyObject *getPyObject() override; + protected: SheetView* showSpreadsheetView(); void updateData(const App::Property *prop) override; @@ -74,6 +79,8 @@ private: QPointer view; }; +typedef Gui::ViewProviderPythonFeatureT ViewProviderSheetPython; + } //namespace Spreadsheet diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPy.xml b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPy.xml new file mode 100644 index 0000000000..d14f891c03 --- /dev/null +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPy.xml @@ -0,0 +1,50 @@ + + + + + + ViewProviderSheet class + + + + returns a list with the selected ranges of cells + + + + + returns a list with the selected cells + + + + + select(index, flags): Select the specified cell using the given QItemSelectionModel.SelectionFlag set +select(topLeft, bottomRight, flags): Select the specified range using the given QItemSelectionModel.SelectionFlag set + + + + + Get the current active cell + + + + + Set the current active cell + + + + + Get access to the sheet view + + + + diff --git a/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPyImp.cpp b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPyImp.cpp new file mode 100644 index 0000000000..bea7958ee6 --- /dev/null +++ b/src/Mod/Spreadsheet/Gui/ViewProviderSpreadsheetPyImp.cpp @@ -0,0 +1,115 @@ +#include "PreCompiled.h" + +#include "ViewProviderSpreadsheetPy.h" +#include "ViewProviderSpreadsheetPy.cpp" +#include + +#include "SpreadsheetView.h" + +using namespace SpreadsheetGui; + +// returns a string which represents the object e.g. when printed in python +std::string ViewProviderSpreadsheetPy::representation(void) const +{ + return std::string(""); +} + + +PyObject* ViewProviderSpreadsheetPy::selectedRanges(PyObject* /*obj*/) +{ + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView *sheetView = vp->getView(); + std::vector ranges = sheetView->selectedRanges(); + Py::List list; + for (const auto &range : ranges) + { + list.append(Py::String(range.rangeString())); + } + + return Py::new_reference_to(list); +} + +PyObject* ViewProviderSpreadsheetPy::selectedCells(PyObject* /*obj*/) +{ + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView *sheetView = vp->getView(); + QModelIndexList cells = sheetView->selectedIndexes(); + Py::List list; + for (const auto &cell : cells) { + list.append(Py::String(App::CellAddress(cell.row(), cell.column()).toString())); + } + + return Py::new_reference_to(list); +} + +PyObject* ViewProviderSpreadsheetPy::select(PyObject* _args) +{ + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView* sheetView = vp->getView(); + + Py::Sequence args(_args); + + const char* cell; + const char* topLeft; + const char* bottomRight; + int flags = 0; + if (args.size() == 2 && PyArg_ParseTuple(_args, "si", &cell, &flags)) { + sheetView->select(App::CellAddress(cell), static_cast(flags)); + } + else if (args.size() == 3 && PyArg_ParseTuple(_args, "ssi", &topLeft, &bottomRight, &flags)) { + sheetView->select(App::CellAddress(topLeft), App::CellAddress(bottomRight), static_cast(flags)); + } + else { + if (args.size() == 2) + throw Base::TypeError("Expects the arguments to be a cell name (e.g. 'A1') and QItemSelectionModel.SelectionFlags"); + else if (args.size() == 3) + throw Base::TypeError("Expects the arguments to be a cell name (e.g. 'A1'), a second cell name (e.g. 'B5'), and QItemSelectionModel.SelectionFlags"); + else + throw Base::TypeError("Wrong arguments to select: specify either a cell, or two cells (for a range), and QItemSelectionModel.SelectionFlags"); + } + Py_RETURN_NONE; +} + +PyObject* ViewProviderSpreadsheetPy::currentIndex(PyObject* /*_args*/) +{ + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView* sheetView = vp->getView(); + auto index = sheetView->currentIndex(); + Py::String str(App::CellAddress(index.row(), index.column()).toString()); + return Py::new_reference_to(str); +} + +PyObject* ViewProviderSpreadsheetPy::setCurrentIndex(PyObject* args) +{ + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView* sheetView = vp->getView(); + + const char* cell; + if (PyArg_ParseTuple(args, "s", &cell)) { + sheetView->setCurrentIndex(App::CellAddress(cell)); + } + Py_RETURN_NONE; +} + +PyObject* ViewProviderSpreadsheetPy::getView(PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return nullptr; + + ViewProviderSheet* vp = this->getViewProviderSheetPtr(); + SheetView* sheetView = vp->getView(); + if (sheetView) + return sheetView->getPyObject(); + Py_RETURN_NONE; +} + +PyObject *ViewProviderSpreadsheetPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + + +int ViewProviderSpreadsheetPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +}