-
-
Notifications
You must be signed in to change notification settings - Fork 75
Core Changes
Here is a list of core changes sorted by C++ namespace, and classes. With only very few exception, most of the core changes are exposed to Python as well.
The property status bits are expanded for dynamic access control, just like
file attributes in file system. It will be persisted when the document is
saved. This allows for more flexible control of the properties of an object.
For example, the programmer can now set a Trasient
bit on the Shape
of
a Part::Feature
derived object to make it not persistent, but dynamically
generated when restore, which is the more efficient way of handling OCCT
compound. Python code can use the following code to manipulate status bits of
a property dynamically. This function are implemented in PropertyContainerPy
class.
# make some property immutable, i.e. read-only in python
obj.setPropertyStatus(property_name, "Immutable")
# remove immutable from some property
obj.setPropertyStatus(property_name, "-Immutable")
# add multiple status bit
obj.setPropertyStatus(property_name, ["Immutable", "Hidden"])
# get property status of some proeprty
obj.getPropertyStatus(property_name)
# get a list of available property status name
obj.getPropertyStatus()
Currently supported property status are,
-
Touched
: this property status cannot be set usingsetPropertyStatus()
through python, but can be returned bygetPropertyStatus()
. Usetouch()
instead. -
Immutable
: mark the property as read-only in python. C++ code is not affected. -
ReadOnly
: make the property read-only in the property view window. -
Hidden
: hide the property from property view window. -
Transient
: do not store the property when saving the document. -
MaterialEdit
: enable material editing in property view. -
NoMaterialListEdit
: disable material list editing in property view. -
Output
: property marked withoutput
will not trigger its container'sonChanged()
function -
LockDynamic
: prevent dynamic property from being removed. -
NoModify
: prevent callingGui::Document::setModified()
on property change.
Class PropertyLists
, and most of its derived classed has been refactored for
more code reuse. The original motivation is to add a TouchList
function,
which allows the programmer to discover exactly which entry/entires has been
changed to save potentially expensive operations.
A new class, PropertyListsBase
, is added as an abstract parent class that is
NOT derived from Property
. This is to make it possible for other list like
property class to reuse the list handling code without having to derive
from PropertyLists
. One example is the PropertyLinkList
class described
later. This class, and all other link type property classes, are changed to
derived from a new PropertyLinkBase
.
PropertyListBase
provides a helper function _setPyObject()
to set value
from a Python object. It accepts Python object as a tuple
, list
, or
newly supported dict(index:value)
. For dict
, it also supports appending by
using index of -1. The implementation of _setPyObject()
extract the Python
objects for items from the input Python sequence or dictionary, put them into
C++ vector, and then calls the virtual function setPyValues()
for further
assignment. The reason of the underscore in _setPyObject()
is to avoid name
clash with Property::setPyObject()
, as PropertyListBase
is NOT derived
from Property
.
PropertyListT
is a template class that implements the generic type logic of
list handling, and provides the implementation of setPyValues()
. The
implementation is designed to be compatible with original various list
properties. It unifies the API for all list type properties,
-
get/setSize()
-
getValue()/getValues()
, same function for returning the whole list, provided for backward compatibility -
setValue(const_reference value)
, clear the list and store one value into the list. -
setValue/setValues(list)
, set whole list. -
operator[](int)
, indexer operator to return one value -
_set1Value()
, a helper function for implementingset1Value()
. It is not public because the original list properties have non-uniform behaviors for this function. Some will touch the container, while others will not. It is made protected to force derived class to provider its ownset1Value()
. -
setPyObject()
, for property assignment from Python object. It first tries to assign the Python object as a single item. If failed, then callPropertyListBase::_setPyObject()
to assign it as a list. -
getPyValue()
, a template pure virtual function for derived class to provide logic of converting a Python object into a single item C++ object. -
setPyValues()
, convert vector of Python object of items into vector of C++ object. It relies ongetPyValue()
for converting.
TouchList
is supported by API getTouchList()
which returns a set of indices
of the changed values. Python code can use
obj.getPropertyTouchList(prop_name)
to obtain the touch list. PropertyListT
will take care of maintaining the touch list on property changes.
Current FreeCAD lacks a proper abstraction of the link type properties. The involved core logic enumerates all link property types and deals with them separately. However, link type properties become a lot more complicated due to the introduction of external links and topological naming. The need of a proper abstraction become a must.
The main difficulty of providing a common abstract class for link property is
that the list type link property is derived from PropertyLists
, while others
are derived directly from Property
. The above refactor of PropertyLists
solved this problem by isolating the list handling logic to a non property
class.
A new class PropertyLinkBase
, derived from Proeprty
, is used as the common
parent class of all link type properties. It defines the following API
interface,
-
updateElementReference()
, called to update geometry element reference due to model changes. See here for more information -
referenceChanged()
, check if element reference has been changed after document restore. If so, the core will mark the object for recompute to regenerate the element mapping. -
getLinks()
, return the linked object(s) of this property, and optionally any subname references, including the geometry element references. -
breakLink()
, reset the link property .
A new link scope, Hidden
, has been added to allow some form of cyclic
dependency. The getLinks()
API above will not return linked object if the
property has Hidden
link scope. And various link property with this scope
will not call DocumentObject::_addBackLink()
. Therefore, the dependency
information of Hidden
link scope will be hidden from the dependency checking
logic in the core. However, the link property with Hidden
scope still enjoys
benefits like auto element reference update, and auto link breaking from the
core. An example use case is the DocumentObject::ColoredElements
property
which holds the element reference for coloring. This PropertyLinkSubHidden
property links to its own container.
A lot of logic has been added in various link properties to correctly handle
copying/importing/exporting objects from/to multiple documents. Basically, to
avoid name clashing of objects from different documents, the object name will
be changed to <ObjectName>@<DocumentName>
when exporting, and then strip off
the postfix when importing. Any new style geometry element reference will also
be stripped during exporting, and be marked for regeneration when importing.
A new link type property, PropertyXLink
, is added
to support linking of object from external document. It is derived from
PropertyLink
which makes it a drop-in replacement in case external linking is
required. No equivalent external link support for other type of link property,
such as PropertyLinkList
, because of the added complexity of managing
external links. To link multiple external objects, it is recommended to group
the objects in the external document first, and then use a single object with
PropertyXLink
to link them.
A few of new APIs are added to App::Application
class.
-
addPendingDocument()
, used byPropertyXLink
to pend external documents when opening document with external links, so that the linked documents can be automatically opened together with the main document. -
get/set/closeActiveTransaction()
, these APIs are part of the implementation of the new core functionality called Auto Transaction. The other part exists inApp::Document
. Programmer can now callApplication::setActiveTransaction()
to set a potential transaction with a given name. The transaction is only created when there is any actual changes. Moreover, each transaction now has an associated integer ID.setActiveTransaction()
will create a new ID each time it is called. Each changed document will check for active transaction by callinggetActiveTransaction()
and create the actual transaction with the current active transaction ID. The net effect is that, all modified documents during a single logical operation will use the same ID, so that all related transactions can be undo/redo together like a single logical transaction. This function is essential to support editing document with external links.To make the new functionality transparent and yet backward compatible in exiting code,
App::Document::open/commit/abortTransaction()
has been modified to call insteadApplication::set/closeActiveTransaction()
. The actual transaction operation is done byApp::Document::_open/_commit/_abortTranaction()
. This behavior is by default on, and can be turned off by a boolean parameterBaseApp/Preference/Document/AutoTransaction
-
checkLinkDepth/getLinksTo/hasLinksTo()
, a few helper functions to obtainLink
object across all opened documents.
Although not part of Application
, ObjectLabelObserver
was implemented in
Application.cpp
. This class is removed, and label duplication detection logic
is moved into PropertyString
for more efficiency, and also to allow object
itself to control labeling policy. See description below for more
details.
A new property, ShowHidden
, has been added to tell tree view whether to show
hidden object of this document. And each ViewProviderDocumentObject
has a new
property, ShowInTree
, to mark if this object should be hidden in tree view.
These are added to make it more flexible for user to organize the tree view
hierarchy.
Another new property, UseHasher
, is added to control whether to use hasher
to shorten element names generated by topological naming.
API changes are listed below,
-
getObjectByID()
, a new API to retrieve object based on its integer ID, which is auto generated by the document for each new object. The ID is unique within the document, it is mainly used for encoding shape history for topological naming. -
_buildDependencyList()
, helper function to obtain a list of all dependent objects of the given object(s). It supports external linked object, meaning that the obtained object list may include objects from other document. -
getDependencyList()
, replace the original implementation with_buildDependencyList()
-
getDependentDocuments()
, get a list of documents that are dependent to this document. -
mustExecute()
, check if any of the objects in this document must be executed. This function will account for external dependencies, so that it will report true if any externally dependent objects are touched. TheRecompute
button on the toolbar will be activated or deactivated by checking with this function. -
recompute()
, there are several changes to recomputation logic,-
recompute()
now accepts an optional input vector of objects, in order to support partial recomputation. Only the given objects, and all their dependent objects will be recomputed. And since it usesgetDependencyList()
to obtain the dependent object list, the recomputation may consist objects from multiple documents. -
recompute()
accepts a second parameterforce
to force recompute regardless ofSkipRecompute
flag of its owner document. Because of the support of partial recompute, it is now possible to force recompute part of the objects without affecting other objects. So when the user sets theSkipRecompute
of some document, he can still edit object, and after finished, only recompute the editing object and all its dependent objects. Note that without thisforce
recomputation functionality, many object will report error when finish editing if its owner document is marked asSkipRecompute
, because some editing logic expects the recomputation to work, and will check the recomputation result for verification.The force recompute is not initated from
App
namespace directly, but fromGui
namespace through a newsignalSkipRecompute
.Gui::Document
will catch this signal inGui::Document::slotSkipRecompute()
, and check if the recomputation request meets the following criteria,- The given list of recomputing objects must not be greater than one; and
- The document is the active document; and
- If we are the editing document, then force recompute the editing object; or
- If no object is given, then force recompute the active object of this document.
The above slightly convoluted logic is designed such that existing object editing code can now work when
SkipRecompute
is active, without any modification. -
Add support for some form of dependency inversion using a two-pass iteration. An example of dependency inversion is
Sketcher::SketchExport
object, which is a child object of someSketcher::SketchObject
. However it requires the parent being update first in order to obtain the updated exported geometry.The two-pass recomputation works like this. The first pass proceed as normal. After its done, any object that is still touched will be marked with a newly added
ObjectStatus::Recompute2
flag. The second pass will then be launched. Those objects that need dependency inversion shall check its dependent objects for this flag, and do not touch them if found. If there are still objects touched after the second pass, an error message will be printed.In the above
SketchExport
example, the parentSketchObject
will update all its childrenSketchExport
in itsexecute()
function in the first recomputation pass. Since all children (i.e. dependent) objects are recomputed before its parent. ThoseSketchExport
object will still be touched after the first pass, and will be marked withRecompute2
in the second pass.SketchObject
will then skip updating its children and complete the second pass without any problem. -
Error handling logic is changed. When some object reports error during recomputation, this object and all objects in its
InListRecursive
will be removed from pending object list, after which recomputation resumes as normal.
-
-
recomputeFeature()
has an extra argumentrecursive
. When set to true it will recompute the given object and all its dependent objects. -
open/commit/abortTransaction()
, andundo/redo()
has been modified to support grouped transactions. See here for more details. -
isExporting()
, a new function added to distinguish different type of exporting. It can return the following value,-
NotExporting
-
Exporting
, normal exporting with all selected objects being copied. -
ExportKeepExternal
, like normal export but keep the external linked object as it is. This signals the various link properties to not auto rename its external references.
-
-
copyObject()
andexportObjects()
have been modified to be aware of external linked objects, and offer user a choice of whether to copy those external objects or not. The object name mapping logic has been moved from helper classMergeDocuments
to various link properties. -
importLinks()
, with a given list of objects, this new function will copy all external referenced object into this document, and update all external linking property with internal reference, including any intermediate reference inside the subname. Consider aPropertyLinkSub
containing a subname reference ofgroup1.group2.cube
, where thegroup2
is externally linked by aLink
object. After import, the object name may be changed due to name clash, to saygroup3
.importLinks()
will make sure to update the subname reference togroup1.group3.cube
as expected. -
addObject()
has an extra argumentviewType
to allow overriding the objects view provider. For example,Gui::ViewProviderLink
is designed to work for any type of object that contains anApp::LinkBaseExtension
. This makes it possible to change aPart::FeaturePython
object's view provider toViewProviderLink
, so that the object can behave like aLink
object and still be used by other object that only acceptPart::Feature
derived objects. See here for and example. -
restore(), importObjects()
, modified to unified logic of finishing restoring objects by calling a new helper functionafterRestore()
. -
afterRestore()
, called after objects are restored either from physical document or clipboard. It first sort the object usinggetDependencyList()
, and then calls object'sonDocumentRestored()
function in the correct order, in order to make it possible for object to dynamically generate content during restoring from its dependent object. When calling each object'sonDocumentRestored()
function, it will also trigger the corresponding view object'sfinsihRestoring()
using a newsignalFinishRestoreObject()
.
A new property, Visibility
, has been added to make it possible to set
and get visibility status of an object in App
namespace. This makes it
possible to determine the children visibilities in some group object without
loading GUI, for example, to generate a compound shape representation of
a group.
Here is a list of changed/added APIs,
-
getID()
, obtain the internal integer ID of this object. -
getViewProviderNameOverride()
, obtain the user specified overridden view provider.App::Document::addObject()
allows user to override the view provider when creating an object. This overridden view provider type is persisted when saving the document, and can be retrieved using this function. -
getExportName()
, obtain a suitable object name for exporting, depending on the exporting type reported by Document::isExporting() -
getLinkedObject()
, obtain the linked object, and optionally with the accumulated transformation matrix. For non-Link
type object, or, aLink
type object that is not linked, this function shall return the object itself. So it is always safe to call this function, and expecting it to return a valid object. In case there are multiple levels of linking involved, this function allows the caller to retrieve the linked object recursively.In most cases, programmer does not need to explicitly call this function to be able work with
Link
, which will automatically delegate most of the APIs to linked object. One particular use case of this function is that when given a subname reference, saygroup1.group2.cubeLink
, if the last objectcubeLink
is aLink
object that links to the actualcube
, theresolve()
function will return thatLink
as the resolved object, not the actualcube
. The caller can usegetLinkedObject()
to obtain the linked object. -
getSubObject()
, obtain a sub-object through a given subname reference, and optionally return the accumulated transformation matrix, and associated Python object. This is the most important API, created specifically to makeLink
work with group type objects.The default implementation in
DocumentObject
is to search theOutList
of the object for the next hierarchy sub-object. And once found, recursively calls the sub-object'sgetSubObject()
for the next hierarchy. Group type object may override this behavior. Non group type object may also override this function to return the associated Python object when called with an empty subname reference, i.e. when reference to the object itself, or with a sub-element reference, i.e. a reference that is not ending with a dot. For example,Part::Feature
overrides this function to return the object's shape, or sub-shape through the Python object.Link
type object overrides this function to delegate the call to the linked object. -
getSubObjects()
, return a list of subname references for all the child sub-objects. The purpose of this function is mostly for exporting a group type object. Each subname reference returned may contain more than one hierarchy. For example, TheAssembly
container inAssembly3
workbench uses this function to skip one hierarchy that contains constraints and stuff during exporting. The caller is expected to callgetSubObject()
with the returned subname reference to obtain the actual child sub-objects. -
canLinkProperties()
, tellsGui::PropertyView
whether to show the properties of the linked object together with properties ofLink
object itself. For example,App::Link
will only allow linking property when it is not linked to a sub-element, such as a face or an edge. -
hasChildElement()
,set/isElementVisible()
, controls the children visibility for a group type object. Because the child object may be claimed by other objects, it is essential to have independent control of children visibilities. These APIs are designed to abstract how group manages the child visibility. For example,App::LinkGroup
stores the children visibilities as aPropertyBoolList
-
resolve()
, helper function to parse subname reference and resolve the final object, and optionally the immediate parent of the final object, the final object reference name (for callingset/isElementVisible()
), and the sub-element reference if there is one. -
resolveRelativeLink()
, helper function to adjust a subname reference in case the two given object belongs to the same parent. See the code comments for an example. -
recomputeFeature()
, has an extra argument,recursive
, to recompute the dependent objects together with this object if set to true. -
getInListEx()
, obtain the (optionally recursive)InList
of this object. This function exists before FreeCAD completely switched to the new DAG handling code. It usesOutList
of other objects to calculate the recursiveInList
. The algorithm takes external links into account. -
getOutList()
, is slightly modified to keep an internal cache, so that it does not have to re-scan all properties every time to construct theOutList
. The cache is invalidated when any property changes. -
allowDuplicateLabel()
,onBeforeChangeLable()
, new APIs to let object control if it allows its label to be a duplicate with others.PropertyString
, once detected it is a label, will call its owner'sallowDuplicateLabel()
to see if duplicate label is allowed. If not so, and the label is about to be changed,PropertyString
will call its owner'sonBeforeChangeLable()
function to notify the new label. For example,App::Link
supports label inside subname reference, and will auto adjust all affected label subname reference inonBeforeChangeLabel()
function.
The following new extension point as been added for the new APIs of
DocumentObject
, which are used by App::LinkBaseExtension
extensionGetLinkedObject()
extensionGetSubObject()
extensionGetSubObjects()
extensionHasChildElement()
extensionIsElementVisible()
extensionSetElementVisible()
Here is a list of new APIs that can be overridden with Python code. The syntax is shown here in Python.
-
mustExecute(obj)
, returnTrue
to indicate the object must be executed. -
allowDuplicateLabel(obj)
, returnTrue
to enable duplicated label of this object regardless of preference setting. -
onBeforeChangeLabel(obj,newLabel)
, called when label is about to change to the string given bynewLabel
. The object can customize label change by returning a string that is different fromnewLabel
. -
getViewProviderName(obj)
, return a string of the view provider type of this object. This allows Python code to override the view provider of an object. -
getSubObject(obj,subname,retType,matrix,transform,depth)
, return the sub object referenced bysubname
. Python code is expected to resolve the first hierarchy referenced insubname
among its children, and then recursively calls the resolved child'sgetSubObject()
to continue down the hierarchy. ThegetSubObject()
exposed byDocumentObjectPy
has almost exactly the same argument as this function. The meaning of the argument is described below,-
obj
, this object -
subname
, dot separated subname reference. -
retType
, indicating the requested return type of this function- 1 indicates a return type of
tuple(subObj,matrix)
, wheresubObj
is the resolve sub-object, andmatrix
is the accumulated transformation matrix. - 2 indicates a return type of
tuple(subObj,matrix,pyObj)
, where the actual type ofpyObj
is implementation dependent. For example,Part::Feature
derived object returns theShape
inpyObj
. - 0 means to return only
pyObj
. This value is only meaningful when callingDocumentObjectPy.getSubObject()
, but never forFeaturePython.getSubObject()
.
- 1 indicates a return type of
-
matrix
is the input transformation matrix. -
transform
, indicate whether to accumulate the placement of this object into the transformation matrix. The purpose of this argument is so that aLink
object can choose whether or not to override the placement of its linked object. -
depth
, is an integer indicating the current recursive level. It is used to detect cyclic references. Increase thisdepth
value when calling child object'sgetSubObject()
.
-
-
getLinkedObject(obj,recurse,matrix,transform,depth)
, it has similar argument asgetSubObject()
, exceptrecurse
, which indicates whether to follow the link recursively in case of multi-level linking. -
getSubObjects(obj)
, return a list of subname string referencing the child objects for exporting. -
hasChildElement(obj)
, returnTrue
means this is a group type object -
isElementVisible(obj,subname)
, return -1 if the object does not support children visibility control, and 1 if the child referenced insubname
is visible, 0 if hidden. For performance reason,subname
will only contain one hierarchy, i.e. reference to the immediate child object. -
setElementVisible(obj,subname)
, change child object visibility -
canLinkProperties(obj)
, returnTrue
to allowPropertyView
to display linked object properties together with the object's own properties.
There are also quite a few API changes for supporting the new Topological Naming feature. Please refer to this and this document for more details.
-
getModeSwitch()
,getTransformNode()
, exposes the internalCoin3D
node used by the view provider. It is used byViewProviderLink
for 3D representation sharing. -
getDefaultMode()
, expose the current display mode regardless of the object's visibility. Used byViewProviderLink
to link object with independent visibility control. -
getElementPicked()
, this new function is the core API for implementing independent selection of instances of the same object. The default implementation is to call the originalgetElement()
function, which makes this new API backward compatible. For hierarchical view provider, such as,ViewProviderLink
or group type view provider, the implementation can follow theCoin3D SoPath
within the inputSoPickedPoint
to disambiguate the user selection, an then translate the path to a subname reference as output. -
getDetailPath()
, this is the reverse function of the abovegetElementPicked()
. It accepts a subname reference as input, and output theCoin3D SoPath
, along with theCoin3D SoDetail
to locate user selected theCoin3D
node. -
partialRender()
, new helper function to activate partial rendering -
beforeDelete()
, new API that is guaranteed to be called before the object is about to be deleted, either when the object is being removed, or the document is about to be closed. -
canDragAndDropObject()
, new API to tell tree view whether this object supports removing the dropped object from its original parent. Current FreeCAD supports holdingCTRL
key while dropping to signal the user's intention to drop the object without removing it from its original parent. For some type object, such asLink
, it sometimes does not make sense to remove the dropping object from its original parent. -
canDropObjectEx()
,dropObjectEx()
, new API for dropping object with object's hierarchical information, pass in as a subname reference and top parent object. It is added to support relative linking functionality ofLink
object, where it links directly to the top parent, and indirectly to the dropping object through a subname reference. In other words, the linking to the dropping object is relative to the given top parent. -
canRemoveChildrenFromRoot()
, this function tells tree view to not remove children claimed by this object from the root. This API is originally added to demonstrate the fact thatLinkGroup
does not remove object from the global coordinate system, by leaving the object added to the group at the root level. You can toggle the visibility of the instances independent of each other. -
update()
, this function is made virtual, and is overridden inViewProviderDocumentObject
to correctly handle change of the newly addedVisibility
property ofDocumentObject
. -
get/setElementColors()
, new API for managing sub-object/element colors. They are implemented inPartGui::ViewProviderPartExt
for setting colors of faces, edges, and vertices, and also inGui::ViewProviderLink
to override colors of its linked object. -
startEditing()
, this function is made virtual so thatViewProviderLink
can redirect the call to its linked object. -
setRenderCacheMode()
, new API for configuring render caching. Currently only implemented inPartGui::ViewProviderPartExt
. -
signalChangeIcon
, new signal used to inform tree view of icon change, exposed to Python through a method of the same name.
-
showInTree()
, override to let user control the tree item visibility. It simply reports the value of a newly added property,ShowInTree
. User can hide the item by right click the item in the tree view, and select menu action Hide item. To review the hidden items, right click any item, or the document item, and select Show hidden item. -
OnTopWhenSelected
, a new enumeration property added to provide always-on-top effect when selected. The actual visual effect is implemented inView3DInventorViewer
. Supported values are,-
Disabled
, default value. -
Enabled
, make the object always-on-top when either the whole object or any sub-element is selected. -
Object
, on-top only when the whole object is selected -
Element
, on-top only when any sub-element is selected
-
-
reattach()
, new virtual API to be called when an object removal is undone. -
update()
, override to handle change of the newly addedVisibility
fromDocumentObject
. -
canDropObjectEx()
, override to disable dropping of external object, that is, object from an external document. -
getElementPicked(), getDetailPath()
, override to support hierarchical selection of group type object, i.e. those object returns a non null node ingetChildRoot()
. -
getBoundingBox()
, new function to obtain object bounding box usingCoin3D SoGetBoundingBoxAction
, regardless of object's visibility -
forceUpdate(), isUpdateForced()
, new API added because some object (e.g.Part::Feature
) has an optimization to disable visual update when the object is hidden, andViewProviderLink
needs a way to force update the visual in case there is a visibleLink
to that hidden object.
New extension point corresponding to the newly added APIs,
extensionBeforeDelete()
extensionCanDragAndDropObject()
extensionCanDropObjectEx()
extensionDropObjectEx()
-
extensionModeSwitchChange()
, this extension function is called bysetModeSwith()
andsetOverrideMode()
function ofViewProvider
extensionStartRestoring()
extensionFinishRestoring()
extensionGetElementPicked()
extensionGetDetailPath()
Here is a list of new APIs that can be overridden with Python code. The syntax is shown here in Python.
-
getElementPicked(pp) -> String
, expect to return a string as the subname reference given the pick point. ReturnNone
means no selection is found with this object.pp
here is an object ofpivy.coin.SoPickedPoint
. -
getDetailPath(subname,path,append) -> SoDetail|Bool
, expect to return either an object ofpivy.coin.SoDetail
orBoolean
given a subname reference.True
means the whole object is referenced, or else no reference is found.-
subname
: a String containing the subname reference -
path
:pivy.coin.SoFullPath
, on input this is current path of the selection, as this function maybe recursively called by a higher hierarchy. On output, appendSoNode
of the current hierarchy. -
append
: ifTrue
, then append the view provider root node and switch node. Or else, append node below the switch node. This is used byViewProviderLink
, because it will replace the root and switch node.
-
-
setEditViewer(obj, viewer, mode) -> Bool
-
unsetEditViewer(obj, viewer)
-
canRemoveChildFromRoot() -> Bool
-
canDragAndDropObject(obj) -> Bool
-
canDropObjectEx(obj,parent,subname) -> Bool
-
obj
: the dropping object -
parent
: the top parent of the dropping object -
subname
: the subname reference representing the hierarchy path leading from top parent to the dropping object.
-
-
dropObjectEx(obj,parent,subname)
, this function as the same argument ascanDropObjectEx()
above. -
startRestoring(), finishRestoring()
, these functions are modified to call C++ object counterpart as well as Python code, to make sure the view provider extension has a chance of handling restoring. -
getIcon()
, add support for Python code to directly return aQIcon
Python object. The actual implementation of extractingQIcon
from Python object is inGui::PythonWrapper::toQIcon()
.
-
signalShowHidden
, new signal triggered when a document changes itsShowHidden
property, i.e. whether to show the hidden object in tree view. -
editDocument(), setEditDocument()
, for getting and setting the current editing document.
-
signalShowHidden
, new signal triggered when this document changes itsShowHidden
property, i.e. whether to show the hidden object in tree view. -
signalShowItem
, new signal triggered when an object of this document changes itsShowInTree
property. -
getViewProviderByPathFromHead()
, new API to return the first view provider found given aCoin3D SoPath
, used to support context-aware hierarchical selection of group type object. This function obsoletesgetViewProviderByPathFromTail()
, because the default implementation inViewProviderDocumentObject::getElementPicked()
is able to walk down the hierarchy by itself. -
getViewProvider(SoNode*)
, obtain a view provider given its rootCoin3D
node. The private classGui::DocumentP
now maintains a map (_CoinMap
) from root node to view provider to accelerate view provider lookup. -
setEdit(), set/getEditingTransform(), get/setInEdit()
, these APIs are either new or modified to support in-place editing.Link
type object makes it possible for the same object to appear in multiple placements, and even across document boundary. These APIs, together withView3DInvertorVewer::setupEditingRoot()
, allows the user to edit an object out of its original placement.It works like this.
setEdit()
is modified to accept an additional argument,subname
, in order to support editing sub-object. Ifsubname
is null, then the subname reference will be deduced from the current selection by queryingGui::SelectionSingleton
.setEdit()
uses the subname to obtain the current object transformation, by callingApp::Document::getSubObject()
, and the resulting transformation matrix is exposed throughgetEditingTransform()
, but can be overridden by user code throughsetEditingTransformation()
.An editing root node is added to each
View3DInventorViewer
scene graph.ViewProvider
that supports editing in multiple placements can either,-
Add any editing helper nodes manually into the editing root by calling
View3DInventorViewer::setupEditingRoot(node,matrix)
with some transformation obtained fromgetEditingTransform()
or through other meaning. This method is demonstrated inGui::ViewProviderDragger
. -
Simply calls
setupEditingRoot(0,0)
which will causeView3DInventorViewer
to transfer all children nodes of the editing view provider root node into the editing root with a replaced transformation node initialized using transformation obtained fromgetEditingTransform()
. Node changes will be automatically reverted after editing is done. This method has the side effect of automatically hiding all other non editing instance of the editing object, which is desired in some use cases, while the first method keeps all other instances untouched. This method of editing is demonstrated inSketcherGui::ViewProviderSketch
The ideal place to call
View3DInventorViewer::setupEditingRoot()
is inViewProvider::setEditViewer()
function. -
-
handleChildren3D()
, improve efficiency of node tree building of group type view provider. -
checkTransactiveID()
, private helper function to check and warn user if a multi-step undo/redo is about to break a grouped transaction. -
undo(), redo()
, modified to check other document for grouped transaction by callingcheckTransactionID()
-
isPerformingTransaction()
, unlikeApp::Document::isPerformingTransaction()
, this function will reportTrue
while undo/redo this and possibly other document's grouped transaction. -
beforeDelete()
, new function that will be called before this document is about to be deleted. It will call all its children view provider's newViewProvider::beforeDelete()
API. -
save()
, modified to check for dependent external documents, and ask user for permission to save them before saving the current document, so that the external file time stamp is up-to-date when current document is saved. -
Restore()
, add toggling view providerisRestoring
flag. -
slotFinishRestore()
, catch the newApp::Document::signalFinishRestoreObject()
signal and calls theViewProvider::finishRestoring()
, and reset itsisRestoring
flag. This unifies the logic of both restoring from physical document and importing from clipboard, and fixed the missing call ofViewProvider::finishRestoring()
when importing. -
slotFinishRestoreDocument()
, modified to check for any object that is still touched, and not resetModified
status if found. -
slotNewObject()
, modified to call the newDocumentObject::getViewProviderStored()
function to allow Python code to override C++ view provider type. And also call the new APIViewProviderDocumentObject::reattach()
in case the new object is the result of undoing a previous delete operation. -
slotDeleteObject()
, modified to handle the new in-place editing view provider, and call the newViewProvider::beforeDelete()
API.
Because the addition of external Link
, Python code can not always use
App.ActiveDocument.getObject()
function to obtain the correct object in
commands. Great effort has been spent to correct this coding habit in various
workbenches, such as Part
, PartDesign
, and Sketcher
. The following helper
macros, declared in Gui/Command.h
, are provided to ease the migration.
-
FCMD_DOC_CMD(doc,cmd)
, execute the string command incmd
using C++ document objectdoc
, it expands to the following simplified code with safety checking omitted. Note thatcmd
can be a composite expression joined by operator<<
.
std::ostringstream ss;
ss << "App.getDocument('" << doc->getName() << "')." << cmd;
Gui::Command::runCommand(Gui::Command::Doc,ss.str().c_str());
// example
// FCMD_DOC_CMD(doc,"addObject('Part::Feature','" << name << "')")
// issue command
// App.getDocument(doc_name).addObject('Part::Feature',name)
-
FCMD_OBJ_DOC_CMD(obj,cmd)
, equivalent to the following macro call, with safety check on object pointerobj
.
FCMD_DOC_CMD(obj->getDocument(),cmd)`,
-
FCMD_OBJ_CMD(obj,cmd)
, execute a command with an object, expands roughly to,
std::ostringstream ss;
ss << "App.getDocument('" << obj->getDocument()->getName() << "').getObject('"
<< obj->getNameInDocument() << "')." << cmd;
Gui::Command::runCommand(Gui::Command::Doc,ss.str().c_str());
// example
// double length = 100.0;
// FCMD_OBJ_CMD(cube,"Length = " << length);
// issues command
App.getDocument(doc_name).getObject(obj_name).Length = 100.0
-
FCMD_VOBJ_CMD(obj,cmd)
, same as above, but use view object instead of object. Note that the input argumentobj
is still required to be a document object.
std::ostringstream ss;
ss << "Gui.getDocument('" << obj->getDocument()->getName() << "').getObject('"
<< obj->getNameInDocument() << "')." << cmd;
Gui::Command::runCommand(Gui::Command::Gui,ss.str().c_str());
// example
// bool visibility = true;
// FCMD_VOBJ_CMD(obj, "Visibility = " << (visibility?"True":"False"));
// issues command
Gui.getDocument(doc_name).getObject(obj_name).Visibility = True
-
FCMD_OBJ_CMD2(cmd,obj,...)
, same purpose asFCMD_OBJ_CMD()
but use conventional C variadic function, instead of C++ stream,
Gui::Command::doCommand(Gui::Command::Doc,"App.getDocument('%s').getObject('%s')." cmd,
obj->getDocument()->getName(),obj->getNameInDocument(),## __VA_ARGS__);
// example
// double length = 100.0;
// FCMD_OBJ_CMD2("Length = %g", cube, length);
-
FCMD_OBJ_CMD2(cmd,obj,...)
, same purpose asFCMD_VOBJ_CMD()
but use conventional C variadic function, instead of C++ stream,
Gui::Command::doCommand(Gui::Command::Gui,"Gui.getDocument('%s').getObject('%s')." cmd,
obj->getDocument()->getName(),obj->getNameInDocument(),## __VA_ARGS__);
// example
// bool visibility = true;
// FCMD_VOBJ_CMD2("Visibility = %s", obj, visibility?"True":"False");
-
FCMD_OBJ_HIDE(obj), FCMD_OBJ_SHOW(obj)
, shortcut of toggle visibility using a document object with the newly addedVisibility
property. They simply expands to,
FCMD_OBJ_CMD(obj,"Visibility=True");
FCMD_OBJ_CMD(obj,"Visibility=False");
Below is a list of API changes of class Gui::Command
,
-
isActive()
function is made public. So that other class can create context menus with only active commands. An example usage is inGui::Workbench::createLinkMenu()
-
getObjectCmd(obj,prefix=0,postfix=0)
, static helper function to generate Python code of accessing the given object. In case the object passed in is invalid or null, it returns a string of "None". For example,
getObjectCmd(obj, "[", "]")
will generate,
"[ App.getDocument(doc_name).getObject(obj_name) ]"
-
getUniqueObjectName()
, accepts an extra optional argumentobj
of a pointer to document object. Ifobj
is non-zero, it will call the object's owner document'sgetUniqueObjectName()
function, or else the active document's. -
The following APIs are renamed with a proceeding underscore to accept extra arguments of source file and line number for better debugging purpose. To maintain source code compatibility, macros are added with the same name as the original APIs
doCommand()
runCommand()
assureWorkbench()
copyVisual()
There are two changes in MainWindow
-
Action status are no longer updated using a periodic timer, this is to prevent lengthy Python code hogging processing time. Instead, the actions are updated in response to various signals. A single shot timer is used to limit the frequency of action status checking. A new function
updateActions()
is added for others to trigger the action update. Search the code for calling of this function to see which signals or events are responsible for triggering the update. -
Originally, the
actionLabel
is used for display console log message inMainWindow
status bar. And,QStatusBar::showMessage()
is used to display a temporary message, e.g. tree status message, and 3D view pre-selection, etc.The problem is that
QStatusBar
temporary message takes precedence over normal message that displayed inside its child widget (i.e.actionLabel
in this case), which means important messages may be easily overwritten by not so important information, like toolbar tips. To fix this problem, the message display location are swapped, such thatQStatusBar::showMessage()
is now used for console message, andactionLabel
for the rest. The user now has a chance to catch the console message, especially errors and warnings, without the report view.
The changes in Gui::SelectionSingleton
is among the most critical ones in order to
make Link
work. The goal of the design change is first to make sure existing
code remains working without modification, and then allow new code to obtain
full object hierarchy information of the user selection. See
here for more details.
Most of the APIs are extended by introducing an extra integer argument,
resolve
, which has the following meaning,
-
0, no auto resolving, the returned selected object is the top level parent of the selection, and the hierarchical path of the actual selected object is returned as a subname reference.
-
1, as the default value, meaning auto resolving for backward compatibility.
Gui::SelectionSingleton
will walk down the hierarchy and return the actual selected object without selection context. Non-object sub-element reference is still kept in the subname reference as before. -
2, like 1, but translate the sub-element reference to new style mapped element name, if there is one. This is used to make the new topological naming backward compatible. See here for more details
Signal for selection change is extended in a different way by adding two new signals, as shown below
-
signaleSelectionChanges
, original selection change signal, for auto resolved object. Same asresolve=1
; -
signalSelectionChanges2
, no auto resolve, i.e.resolve=0
; -
signalSelectionChange3
, resolve with mapped element, i.e.resolve=2
.
The APIs extended with the resolve
argument are listed below,
addSelectionGate()
countObjectsOfType()
getObjectsOfType()
getSelection()
getSelectionEx()
getComplementeSelection()
hasSelection()
The extra resolve
argument is also added to the following class constructor
-
SelectionObserver
, in addition toresolve
, another extra augment,attached
is added to allow caller decide if the new constructed observer is attached or not. -
SelectionObserverPython
Picked list for holding a list of alternative objects picked by mouse selection in the 3D view. User code get access to picked list through the following APIs,
-
needPickedList()
, check if picked list is activated. User activates the picked list by select an checkbox inSelectionView
. -
enablePickedList()
, allow user code to enable picked list. -
hasPickedList()
, check if picked list is empty. -
getPickedList()
, obtain the picked list with the same format asgetSelection()
-
getPickedListEx()
, obtain the picked list with same format asgetSelectionEx()
The actual implementation of constructing the picked list is in
SoFCUnifiedSelection
, and is passed to SelectionSingleton
using the
following API of Selection
with an extra argument, pickedList
,
-
addSelection()
, -
rmvSelection()
,
The following screen cast shows the picked list in action.
-
setVisible()
, a helper function to set/unset/toggle visibility of the current selected object. Because the introduction ofLink
, the same object can appear in multiple places, some may have separate visibility control, but some not. For example, there a twoLink
objects that links to the same group, and user selects the same child object under these two linked group. Although the same object is selected, but they in different selection context, and thus, exists as separate entries inSelection
. However, when toggling visibility, we must only count them as one entry, or else the double toggling will negate each other, resulting no visibility changes.setVisible()
is created to handle these kind of complex scenario. -
updateSelection()
, new API to re-synchronize the selection and preselection status of a given object. For example, it is called bysetVisible()
to update view provider selection highlight when its visibility is toggled. -
setPreselect()
, accepts an extra integer argument,signal
with the following meaning,- 0, default, for informing a preselection has been made;
- 1, user initiated preselection;
- 2, reserved for preselection initiated by mouse over tree view item.
This is the class encoding the message of selection change. Current
implementation of the class uses raw pointer to refer to the internal list of
selected document and object names. This may cause problem when user code
decides to add or remove the current selection while handling a selection
change event. To make it more robust, SelectionChanges
now holds all names
inside the class itself.
A few new message types are introduced, mostly for internal uses,
-
SetPreselectSignal
, indicating that the caller wants to initiate a preselection, unlike existingSetPreselect
, which is for informing a preselection has been made. This message is handled inSoFCUnifiedSelection::doAction
to make is easy for user code to initiate preselection. -
Show/HideSelection
, for synchronizing view object's selection visual highlight when its visibility status changes. This fixes the problem of missing selection highlight when an object is made visible. It is handled inView3DInventorViewer::onSelectionChanged()
-
PickedListChanged
, for selection changes in the newPickedList
fromSelection
. This message is meant to be used by user code. A new handler functionpickedListChanged()
is added toSelectionObserverPython
for this purpose.
The parent class is changed from Gui::SelectionSingleton::ObserverType
to
Gui::SelectionObserver
, so that SelectionView
can obtain the full hierarchy
information of the selection. In addition, the observer will be auto detached
when the selection view widget is hidden to improve overall performance.
Add support for toggling PickedList
function, and displaying of the current
picked list.
Gui::TreeWidget
does not really expose any APIs for use by other classes. So
here we will mainly describes the GUI changes, with a brief mentioning of the
implementation changes, which is significant because of the introduction of
selection context.
The internal object map has changed its key from the object name to object
pointer, because the map may store external object brought in by a Link
, which
may have the same name as objects from the linking document. Currently, object
map is still maintained per document, which may result in some inefficiency, but
has the advantage of a clear logic separation. Future development may explore
the possibility of a single unified map for objects of all opened documents.
There are actually two tree view in FreeCAD. The other tree view is from the
combo view. For easy debugging, each TreeWidget
instance is given a name. And
its selection observer is detached when invisible.
Tree item status update is no longer triggered by a periodic timer for the
sake of both performance and easy debugging. It is now triggered by various
signals and events, similar to MainWindow::updateActions()
. Search the code
for calling of updateStatus()
to find out the actual trigger. A single-shot
timer is used to limited the frequency of update.
Three options are exposed through context menu Tree view options,
-
Pre-selection, enable pre-select of object in 3D view when mouse over tree item. The actual preselection logic is implemented by
View3DInventorViewer::checkGroupOnTop()
-
Sync selection, auto scroll to the tree item when the corresponding object is selected in 3D view.
-
Sync view, auto activate the owner document and 3D view of the object selected in tree view.
The following new context menu actions are added when an object item is selected,
-
Show hidden items, toggle whether to show hidden items in an document.
-
Hide/Show item, toggle the visibility of object item in the tree view.
-
Recompute object, recompute only the selected objects and their dependencies.
The tree view will now catch signalRecomputed
, and auto expand and scroll to
the first object that reports error during recomputation.
New context aware drag and drop support, including dragging object across document boundary.
Add support for Link
object, which display the properties of the linked object
together with the ones from link itself.
Added timer to limit the frequency of handling selection change, which fixes the severe slow down on large selection.
Clear property view on undo/redo to avoid potential crash.
Dynamically detach selection observer on hidden.
Added support for PropertyXLink
by adding a combo box to switch between
documents for linking to external objects.
Change list view to tree view in order to support linking into a sub-object.
The parent class is changed from Gui::SelectionSingleton::ObserverType
to
Gui::SelectionObserver
, so that SelectionView
can obtain the full hierarchy
information of the selection.
Added a On-Top Coin3D
group node in the scene graph to support the new
ViewProvider::OnTopWhenSelected
property, and object highlight (which is also
made on-top) on mouse over tree view item. The logic is implemented in function
checkGroupOnTop()
, and called in response to various selection change event.
The 3D rendering of the on-top object relies on a new Coin3D
node class
SoFCPathAnnotation
, which is similar to Coin3D SoAnnotation
but is capable
of rendering a specific Coin3D
path, in order to show the correct object
hierarchy.
The following APIs are added to support in-place editing.
-
setupEditingRoot()
, called to initiate in-place editing. -
resetEditingRoot()
, revert scene graph changes after in-place editing. -
setEditingTransform()
, setup the editing transformation matrix. -
getPointOnRay()
, this helper function is moved fromGui::ViewProvider
here because of the newly added in-place editing may move all its children node into the editing root node ofView3DInventorViewer
. The current only user ofgetPointOnRay()
,SketcherGui::ViewProviderSketch
, supports in-place editing this way.
This class is the key to make selection context to work in 3D view. The
selection and pre-selection logic is refactored and moved from doAction()
into dedicated functions, setSelection()
and setHighlight()
. The
implementation looks for the first view provider root node found in the
Coin3D
path inside a SoPickedPoint
, and calls that view provider's
getElementPicked()
to obtained selection hierarchy context. See
here for more details.
New support for PickedList
is implemented by exploring the picked point list
from Coin3D SoHandleEventAction
.
This is the legacy class for handling selection in the past. It is still used
to handle selection of non-Part
derived objects, such as
ViewProviderOriginFeature
. In order to have the same selection context
support as SoFCUnifiedSelection
, the selection handling logic is skipped in
its handleEvent()
function, but instead, handled inside
SoFCUnifiedSelection
like everyone else, thus truly unifies 3D view
selection logic. A new attribute, useNewSelection
is added to control this
behavior.
This class, derived from Coin3D SoSeparator
, stores the actual selection
context, and allows other shape classes to dynamically look up the current
selection context, and property render selection and preselection highlight.
Three shape classes, SoBrepPointSet
, SoBrepEdgeSet
, and SoBrepFaceSet
(also SoFCSelection
, for selection rendering of non-Part
derived object)
are modified to support context-aware selection rendering.
A new task class to allow user specifying element colors. It uses the new API
ViewProvider::get/setElementColors()
. It is currently used by
ViewProviderLink
to override element color, and also
PartGui::ViewProviderPartExt
, which replaces the original TaskFaceColors
.
The Part
module does the actual geometry modeling.
It has been modified so that other modules can obtain geometry shape through
Link
. The most import function here is a static helper function
Part::Feature::getTopoShape()
. It allows one to obtain a geometry shape from
a object that is not derived from Part::Feature
, including Link
, and group
type object. For group object, getTopoShape()
relies on the new
DocumentObject:getSubObjects()
function to obtain the children object, and
creates a compound shape on demand. To improve performance, it uses internal
shape caches to reduce number of rebuilds.
getTopoShape()
is exposed to Python as Part.getShape()
. Please checkout
its doc string for more details.
PartDesign
and Sketcher
modules are modified to support the new
Topological Naming functionality.
New class _DraftLink
, along with its companion _ViewProviderDraftLink
, are
added As a demonstration of how to use App::LinkBaseExtension
in Python to
implement Link
type object. _Array
and _PathArray
are modified to derive
from this class to support linked array. A new attribute is added to turn on
link function when the object is created. Linked array behave almost the same
as original array object, with the additional benefit of external link.
Moreover, when ShowElement
property is turned on, each element of the array
is revealed as a child object to let user customize the placement and material.
Class ImportOCAF2
and ExportOCAF2
has been added as a new implementation of
STEP
importer/exporter, which uses Link
and LinkGroup
to greatly improve
performance of handling nested assembly structures. Proper support for edge
and visibility has been added, as well as support of assembly component
instance styling override using Link's
color and visibility override
function. New options are exposed to STEP
import/export preference page,
including options to access original upstream importer.