I've had another try at implementing expressions for properties now, and I have a (start for a) solution I would like to present. I've pushed it to github: https://github.com/eivindkv/free-cad-co ... pressions2
This is somewhat related to the Spreadsheet discussion about having a PropertyController. Adding expressions would easily kill the need for a way to push property values from a spreadsheet, as it could be pulled from it instead.
The commit is rather large, mostly dues to tests/use-cases, and consists of the following:
1) Property dependencies:
- A way to describe dependencies between properties. This is completely within the Property.h/cpp files, and is basically implemented as an extra field in the Property class. I have added a Path class here, to be able to describe dependencies on sub-elements of a property, e.g a position of a Placement, or a single element of an array.
- The DocumentObject class now takes the dependencies into account when the computation graph is done, by investigating the properties' dependencies. This is also done in its isTouched function. This very much resembles ickby's solution (discussed in a different but recent thread here).
A notable difference from ickby's solution is that a property can have an arbitrary number of dependencies in my implementation.
I have taken my Expression classes from the Spreadsheet module, and slightly modified them. This is how the "raw" expressions are done. One notable change is that the syntax to refererence a property from a different document object is changed to object:property, not object.property, e.g to reference the Length of a Pad object, one uses 'Pad:Length'. This is to make the 'object' unique, so it cannot be interpreted as a compound property with a member.
To collect them and get them evaluated correctly and at the right time, they are all stored in a PropertyExpressionEngine object. This is basically a map with Path -> Expression, where Path is a path to a property in a DocumentObject (its owner). The ExpressionEngine is responsible for figuring out if there are internal cycles among th expressions, and also the correct evalutaion order.
From Python, expressions can be set using the setExpression method in DocumentObjects, like App.ActiveDocument.Pad.setExpression('Length', '2 * 10mm')
3) Support in Gui::InputField
This is added by the function bind(object, path). When this is invoked, the input field gets bound to this document object and path, and the underlying property gets instant support for expressions.
4) Support in Pad and Pocket. I have added bind(...) to these two for testing purposes.
5) Support in Sketcher. I have added bind(...) here to be able to use expressions for constraint datums. Datums are accessed by their name in other expressions. The Sketcher Workbench is particularly interesting, because the datum values are stored in an array, but these elements should from a user's perspective be identified by its name. This is done by reimplementing setValue/getValue/getPropertyByPath from DocumentObject (these are new and virtual functions in this class).
A small example on how to use: Create a rectangle with a constraint on width and height. Rename constraints to Width and Height, instead of their default names like Constraint9. To make a box where the height is always half of its width, edit the height constraint, and input 'Width/2'. The constraint should be recomputed automatically, and it will also change whenever you change the Width constraint.
Another example: To make a pocket always be 5mm above the bottom of a pad, its Length can be set to 'Pad:Length - 5mm'.
6) The generic propery editor switches the field to read-only if an expression is attached to it (this is for test only, ideally the expression should be show in an editable way here).
I have also added Save/Restore functions, but these do not work as intended yet, and they might or might not crash when invoked (it depends on how complex the relationships are in the model). Also, for the time being, the code is not very forgiving if you try to delete objects that have dependencies. So, there are some rough edges here and there, but this is more a proof of concept. Let me know what you think, if this is a wanted feature, and if this is a viable implementation strategy.