This class was created by realthunder during the `LinkMerge`,
in de66e563e0, to demonstrate how to use the `App::Link`
objects to create Link aware arrays.
It is used by `draftobject.array` (ortho, polar, circular)
and `draftobject.patharray` to create respective Link arrays.
This class is a bit mysterious. We need more documentation
on how the properties are being set, and how the code interacts
with the arrays that use it.
222 lines
8.7 KiB
Python
222 lines
8.7 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2019 Zheng, Lei (realthunder)<realthunder.dev@gmail.com>*
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
"""Provides the object code for the Draft Link object.
|
|
|
|
This class was created by realthunder during the `LinkMerge`
|
|
to demonstrate how to use the `App::Link` objects to create
|
|
Link aware arrays.
|
|
It is used by `draftobject.array` (ortho, polar, circular)
|
|
and `draftobject.patharray` to create respective Link arrays.
|
|
|
|
NOTE: this class is a bit mysterious. We need more documentation
|
|
on how the properties are being set, and how the code interacts with
|
|
the arrays that use it.
|
|
"""
|
|
## @package draftlink
|
|
# \ingroup DRAFT
|
|
# \brief Provides the object code for the Draft Link object.
|
|
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
|
|
import FreeCAD as App
|
|
import lazy_loader.lazy_loader as lz
|
|
|
|
from draftobjects.base import DraftObject
|
|
from draftutils.messages import _wrn
|
|
|
|
# Delay import of module until first use because it is heavy
|
|
Part = lz.LazyLoader("Part", globals(), "Part")
|
|
DraftGeomUtils = lz.LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
|
|
|
|
|
class DraftLink(DraftObject):
|
|
"""New class to use the App::Link objects in arrays.
|
|
|
|
Introduced by realthunder.
|
|
This is subclassed by `draftobjects.array.Array`
|
|
and by `draftobjects.patharray.PathArray`.
|
|
"""
|
|
|
|
def __init__(self, obj, tp):
|
|
self.use_link = False if obj else True
|
|
super(DraftLink, self).__init__(obj, tp)
|
|
if obj:
|
|
self.attach(obj)
|
|
|
|
def __getstate__(self):
|
|
"""Return a tuple of all serializable objects or None."""
|
|
return self.__dict__
|
|
|
|
def __setstate__(self, state):
|
|
"""Set some internal properties for all restored objects."""
|
|
if isinstance(state, dict):
|
|
self.__dict__ = state
|
|
else:
|
|
self.use_link = False
|
|
super(DraftLink, self).__setstate__(state)
|
|
|
|
def attach(self, obj):
|
|
"""Set up the properties when the object is attached."""
|
|
if self.use_link:
|
|
obj.addExtension('App::LinkExtensionPython', None)
|
|
self.linkSetup(obj)
|
|
|
|
def canLinkProperties(self, _obj):
|
|
"""Link properties.
|
|
|
|
TODO: add more explanation. C++ override???"""
|
|
return False
|
|
|
|
def linkSetup(self, obj):
|
|
"""Set up the link properties on attachment."""
|
|
obj.configLinkProperty('Placement', LinkedObject='Base')
|
|
if hasattr(obj, 'ShowElement'):
|
|
# Rename 'ShowElement' property to 'ExpandArray' to avoid conflict
|
|
# with native App::Link
|
|
obj.configLinkProperty('ShowElement')
|
|
showElement = obj.ShowElement
|
|
obj.addProperty("App::PropertyBool",
|
|
"ExpandArray",
|
|
"Draft",
|
|
QT_TRANSLATE_NOOP("App::Property",
|
|
"Show array element as "
|
|
"children object"))
|
|
obj.ExpandArray = showElement
|
|
obj.configLinkProperty(ShowElement='ExpandArray')
|
|
obj.removeProperty('ShowElement')
|
|
else:
|
|
obj.configLinkProperty(ShowElement='ExpandArray')
|
|
|
|
if getattr(obj, 'ExpandArray', False):
|
|
obj.setPropertyStatus('PlacementList', 'Immutable')
|
|
else:
|
|
obj.setPropertyStatus('PlacementList', '-Immutable')
|
|
|
|
if not hasattr(obj, 'LinkTransform'):
|
|
obj.addProperty('App::PropertyBool',
|
|
'LinkTransform',
|
|
' Link')
|
|
|
|
if not hasattr(obj, 'ColoredElements'):
|
|
obj.addProperty('App::PropertyLinkSubHidden',
|
|
'ColoredElements',
|
|
' Link')
|
|
obj.setPropertyStatus('ColoredElements', 'Hidden')
|
|
|
|
obj.configLinkProperty('LinkTransform', 'ColoredElements')
|
|
|
|
def getViewProviderName(self, _obj):
|
|
"""Override the view provider name."""
|
|
if self.use_link:
|
|
return 'Gui::ViewProviderLinkPython'
|
|
return ''
|
|
|
|
def migrate_attributes(self, obj):
|
|
"""Migrate old attribute names to new names if they exist.
|
|
|
|
This is done to comply with Python guidelines or fix small issues
|
|
in older code.
|
|
"""
|
|
if hasattr(self, "useLink"):
|
|
# This is only needed for some models created in 0.19
|
|
# while it was in development. Afterwards,
|
|
# all models should use 'use_link' by default
|
|
# and this won't be run.
|
|
self.use_link = bool(self.useLink)
|
|
_wrn("Migrating 'useLink' to 'use_link', "
|
|
"{} ({})\n".format(obj.Label, obj.TypeId))
|
|
del self.useLink
|
|
|
|
def onDocumentRestored(self, obj):
|
|
"""Execute code when the document in restored."""
|
|
self.migrate_attributes(obj)
|
|
|
|
if self.use_link:
|
|
self.linkSetup(obj)
|
|
else:
|
|
obj.setPropertyStatus('Shape', '-Transient')
|
|
|
|
if obj.Shape.isNull():
|
|
if getattr(obj, 'PlacementList', None):
|
|
self.buildShape(obj, obj.Placement, obj.PlacementList)
|
|
else:
|
|
self.execute(obj)
|
|
|
|
def buildShape(self, obj, pl, pls):
|
|
"""Build the shape of the link object."""
|
|
if self.use_link:
|
|
if not getattr(obj, 'ExpandArray', True) or obj.Count != len(pls):
|
|
obj.setPropertyStatus('PlacementList', '-Immutable')
|
|
obj.PlacementList = pls
|
|
obj.setPropertyStatus('PlacementList', 'Immutable')
|
|
obj.Count = len(pls)
|
|
|
|
if obj.Base:
|
|
shape = Part.getShape(obj.Base)
|
|
if shape.isNull():
|
|
_err_msg = ("'{}' cannot build shape "
|
|
"from '{}'\n".format(obj.Label, obj.Base.Label))
|
|
raise RuntimeError(_err_msg)
|
|
else:
|
|
shape = shape.copy()
|
|
shape.Placement = App.Placement()
|
|
base = []
|
|
for i, pla in enumerate(pls):
|
|
vis = getattr(obj, 'VisibilityList', [])
|
|
if len(vis) > i and not vis[i]:
|
|
continue
|
|
|
|
# 'I' is a prefix for disambiguation
|
|
# when mapping element names
|
|
base.append(shape.transformed(pla.toMatrix(),
|
|
op='I{}'.format(i)))
|
|
|
|
if getattr(obj, 'Fuse', False) and len(base) > 1:
|
|
obj.Shape = base[0].multiFuse(base[1:]).removeSplitter()
|
|
else:
|
|
obj.Shape = Part.makeCompound(base)
|
|
|
|
if not DraftGeomUtils.isNull(pl):
|
|
obj.Placement = pl
|
|
|
|
if self.use_link:
|
|
return False # return False to call LinkExtension::execute()
|
|
|
|
def onChanged(self, obj, prop):
|
|
"""Execute when a property changes."""
|
|
if not getattr(self, 'use_link', False):
|
|
return
|
|
|
|
if prop == 'Fuse':
|
|
if obj.Fuse:
|
|
obj.setPropertyStatus('Shape', '-Transient')
|
|
else:
|
|
obj.setPropertyStatus('Shape', 'Transient')
|
|
elif prop == 'ExpandArray':
|
|
if hasattr(obj, 'PlacementList'):
|
|
if obj.ExpandArray:
|
|
obj.setPropertyStatus('PlacementList', '-Immutable')
|
|
else:
|
|
obj.setPropertyStatus('PlacementList', 'Immutable')
|
|
|
|
|
|
_DraftLink = DraftLink
|