ShowWB: fix header uniformity

Make headers uniform + remote trailing whitespace
This commit is contained in:
luzpaz
2023-01-21 18:02:30 +00:00
committed by Uwe
parent 0e67943ea3
commit 937d226f4f
13 changed files with 223 additions and 239 deletions

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2018 *
# * Copyright (c) 2018 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -27,24 +26,24 @@ class Container(object):
"""Container class: a unified interface for container objects, such as Group, Part, Body, or Document.
This is a temporary implementation."""
Object = None #DocumentObject or Document, the actual container
def __init__(self, obj):
self.Object = obj
def self_check(self):
if self.Object is None:
raise ValueError("Null!")
if not isAContainer(self.Object, links_too= True):
raise NotAContainerError(self.Object)
def getAllChildren(self):
"""Returns all objects directly contained by the container. all = static + dynamic."""
return self.getStaticChildren() + self.getDynamicChildren()
def getStaticChildren(self):
"""Returns children tightly bound to the container, such as Origin. The key thing
"""Returns children tightly bound to the container, such as Origin. The key thing
about them is that they are not supposed to be removed or added from/to the container."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
@@ -66,7 +65,7 @@ class Container(object):
"""Returns dynamic children, i.e. the stuff that can be removed from the container."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
# find all objects not contained by any Part or Body
result = set(container.Objects)
@@ -92,28 +91,28 @@ class Container(object):
return result
raise RuntimeError("getDynamicChildren: unexpected container type!")
def isACS(self):
"""isACS(): returns true if the container forms internal coordinate system."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
return True #Document is a special thing... is it a CS or not is a matter of coding convenience.
return True #Document is a special thing... is it a CS or not is a matter of coding convenience.
elif container.hasExtension('App::GeoFeatureGroupExtension'):
return True
elif container.hasChildElement(): # Link
return True
else:
return False
def isAVisGroup(self):
"""isAVisGroup(): returns True if the container consumes viewproviders of children, and thus affects their visibility."""
self.self_check()
container = self.Object
if container.isDerivedFrom('App::Document'):
return True #Document is a special thing... Return value is a matter of coding convenience.
return True #Document is a special thing... Return value is a matter of coding convenience.
elif container.hasExtension('App::GeoFeatureGroupExtension'):
return True
elif container.isDerivedFrom('App::Origin'):
@@ -122,7 +121,7 @@ class Container(object):
return True
else:
return False
def getCSChildren(self):
if not self.isACS():
raise TypeError("Container is not a coordinate system")
@@ -148,22 +147,22 @@ class Container(object):
def hasObject(self, obj):
"""Returns True if the container contains specified object directly."""
return obj in self.getAllChildren()
def hasObjectRecursive(self, obj):
return self.Object in ContainerChain(obj)
def _getMetacontainerChildren(container, isrightcontainer_func):
"""Gathers up children of metacontainer - a container structure formed by containers of specific type.
"""Gathers up children of metacontainer - a container structure formed by containers of specific type.
For example, coordinate systems form a kind of container structure.
container: instance of Container class
isrightcontainer_func: a function f(cnt)->bool, where cnt is a Container object."""
result = []
list_traversing_now = [container] #list of Container instances
list_to_be_traversed_next = [] #list of Container instances
visited_containers = set([container.Object]) #set of DocumentObjects
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for itcnt in list_traversing_now:
@@ -175,19 +174,19 @@ def _getMetacontainerChildren(container, isrightcontainer_func):
if not isrightcontainer_func(newcnt):
list_to_be_traversed_next.append(newcnt)
list_traversing_now = list_to_be_traversed_next
return result
def isAContainer(obj, links_too = False):
'''isAContainer(obj, links_too): returns True if obj is an object container, such as
Group, Part, Body. The important characteristic of an object being a
container is that it can be activated to receive new objects. Documents
'''isAContainer(obj, links_too): returns True if obj is an object container, such as
Group, Part, Body. The important characteristic of an object being a
container is that it can be activated to receive new objects. Documents
are considered containers, too.
If links_too, App::Link objects are considered containers, too. Then, container tree
If links_too, App::Link objects are considered containers, too. Then, container tree
isn't necessarily a tree.'''
if obj.isDerivedFrom('App::Document'):
return True
if obj.hasExtension('App::GroupExtension'):
@@ -208,27 +207,27 @@ def ContainerOf(obj):
if cnt is not None and dep is not cnt:
raise ContainerTreeError("Container tree is not a tree")
cnt = dep
if cnt is None:
if cnt is None:
return obj.Document
return cnt
def getVisGroupOf(obj):
chain = VisGroupChain(obj)
return chain[-1]
#from Part-o-magic... over-engineered, but proven to work
def ContainerChain(feat):
'''ContainerChain(feat): container path to feat (not including feat itself).
Last container directly contains the feature.
'''ContainerChain(feat): container path to feat (not including feat itself).
Last container directly contains the feature.
Example of output: [<document>,<SuperPart>,<Part>,<Body>]'''
if feat.isDerivedFrom('App::Document'):
return []
list_traversing_now = [feat]
set_of_deps = set()
list_of_deps = []
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for feat in list_traversing_now:
@@ -241,7 +240,7 @@ def ContainerChain(feat):
if len(list_to_be_traversed_next) > 1:
raise ContainerTreeError("Container tree is not a tree")
list_traversing_now = list_to_be_traversed_next
return [feat.Document] + list_of_deps[::-1]
def CSChain(feat):

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2016 *
# * Copyright (c) 2016 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -22,13 +21,13 @@
# ***************************************************************************/
def getAllDependencies(feat):
'''getAllDependencies(feat): gets all features feat depends on, directly or indirectly.
Returns a list, with deepest dependencies last. feat is not included in the list, except
'''getAllDependencies(feat): gets all features feat depends on, directly or indirectly.
Returns a list, with deepest dependencies last. feat is not included in the list, except
if the feature depends on itself (dependency loop).'''
list_traversing_now = [feat]
set_of_deps = set()
list_of_deps = []
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for feat in list_traversing_now:
@@ -37,19 +36,19 @@ def getAllDependencies(feat):
set_of_deps.add(dep)
list_of_deps.append(dep)
list_to_be_traversed_next.append(dep)
list_traversing_now = list_to_be_traversed_next
return list_of_deps
def getAllDependent(feat):
'''getAllDependent(feat): gets all features that depend on feat, directly or indirectly.
Returns a list, with deepest dependencies last. feat is not included in the list, except
'''getAllDependent(feat): gets all features that depend on feat, directly or indirectly.
Returns a list, with deepest dependencies last. feat is not included in the list, except
if the feature depends on itself (dependency loop).'''
list_traversing_now = [feat]
set_of_deps = set()
list_of_deps = []
while len(list_traversing_now) > 0:
list_to_be_traversed_next = []
for feat in list_traversing_now:
@@ -58,7 +57,7 @@ def getAllDependent(feat):
set_of_deps.add(dep)
list_of_deps.append(dep)
list_to_be_traversed_next.append(dep)
list_traversing_now = list_to_be_traversed_next
return list_of_deps

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -25,7 +24,7 @@ class SceneDetail(object):
"""SceneDetail class: abstract class for tempovis scene save/restore plug-in. An implementation must provide:
* data storage (as "data" attribute of the object)
* constructor (preferably, with value for stored data as optional argument)
* methods to apply values to actual scene (apply_data),
* methods to apply values to actual scene (apply_data),
* ...and to read out the state of the detail in the actual scene (scene_value)
* keying, for identifying two detail instances that affect the exact same thing
* class_id string, which is required for keying
@@ -34,30 +33,30 @@ class SceneDetail(object):
* info on if the modification affects what is saved to disk, and thus should be undone temporarily for file writing.
"""
class_id = ''
data = None
doc = None
## Storage field for TV. Mild restore means that the saved state won't be restored
# if the current state differs from the state requested via last TempoVis.modify(...) call.
# This is a default, it may be changed per-instance.
mild_restore = False
## Storage field for TV. Mild restore means that the saved state won't be restored
# if the current state differs from the state requested via last TempoVis.modify(...) call.
# This is a default, it may be changed per-instance.
mild_restore = False
def set_doc(self, doc):
self.doc = doc
# <interface>
key = None #a string or something alike to use to store/find the entry in TempoVis. For example, a string "{object_name}.{property_name}".
key = None #a string or something alike to use to store/find the entry in TempoVis. For example, a string "{object_name}.{property_name}".
affects_persistence = False #True indicate that the changes will be recorded if the doc is saved, and that this detail should be restored for saving
def scene_value(self):
"""scene_value(): returns the value from the scene"""
raise NotImplementedError()
def apply_data(self, val):
"""apply a value to scene"""
raise NotImplementedError()
## Equality test. Override if data attributes can't be directly compared
def __eq__(self, other):
if isinstance(other, self.__class__):
@@ -66,13 +65,12 @@ class SceneDetail(object):
raise TypeError('{self} can\'t be compared with {other}'
.format(self= repr(self), other= repr(other)))
# </interface>
# <utility>
@property
def full_key(self):
return (self.class_id, self.doc.Name if self.doc else None, self.key)
def __ne__(self, other):
return not self.__eq__(other)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -28,11 +27,11 @@ import FreeCADGui
class Camera(SceneDetail):
"""Camera(doc): TempoVis plugin for saving and restoring camera."""
class_id = 'SDCamera'
def __init__(self, doc):
self.doc = doc
self.key = 'the_cam'
def _viewer(self):
gdoc = FreeCADGui.getDocument(self.doc.Name)
v = gdoc.activeView()
@@ -42,7 +41,7 @@ class Camera(SceneDetail):
def scene_value(self):
return self._viewer().getCamera()
def apply_data(self, val):
self._viewer().setCamera(val)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -28,12 +27,12 @@ import FreeCADGui as Gui
class ClipPlane(SceneDetail):
"""ClipPlane(document, enable = None, placement = None, offset = 0.0):
TempoVis plugin for applying clipping plane to whole project except edit root.
enable can be 0 for disable, 1 for enable, and -1 for toggle
enable can be 0 for disable, 1 for enable, and -1 for toggle
(FIXME: toggle value support is a hack for while we can't read out the current state)."""
class_id = 'SDClipPlane'
key = ''
def __init__(self, document, enable = None, placement = None, offset = 0.0):
self.doc = document
if enable is not None:
@@ -42,10 +41,10 @@ class ClipPlane(SceneDetail):
dir = placement.Rotation.multVec(App.Vector(0,0,1))
placement.Base = placement.Base + dir*offset
self.data = (enable, placement)
def scene_value(self):
return (0, None) #hack. For until there is a way to easily query the plane, this should be good enough.
def apply_data(self, val):
enable, pla = val
if enable != 0:

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -31,14 +30,14 @@ class ObjectClipPlane(SceneDetail):
class_id = 'SDObjectClipPlane'
propname = ''
objname = ''
def __init__(self, object, enable = None, placement = None, offset = 0.0):
self.objname = object.Name
self.doc = object.Document
self.key = self.objname
self.key = self.objname
if enable is not None:
self.data = self.val(enable, placement, offset)
def scene_value(self):
vp = self.doc.getObject(self.objname).ViewObject
cp = getClipPlaneNode(vp, make_if_missing= False)
@@ -50,7 +49,7 @@ class ObjectClipPlane(SceneDetail):
D = pln.getDistanceFromOrigin()
normal = tuple(pln.getNormal())
return enable, (normal, D)
def apply_data(self, val):
enable, pldef = val
vp = self.doc.getObject(self.objname).ViewObject
@@ -62,12 +61,12 @@ class ObjectClipPlane(SceneDetail):
v, d = pldef
cp.plane.setValue(coin.SbPlane(coin.SbVec3f(*v), d))
cp.on.setValue(enable)
def val(self, enable, placement = None, offset = 0.0):
"""val(enable, placement = None, offset = 0.0): constructs a value from convenient
parameters. Placement is in global CS. The cutting will be by XY plane of Placement;
"""val(enable, placement = None, offset = 0.0): constructs a value from convenient
parameters. Placement is in global CS. The cutting will be by XY plane of Placement;
the stuff in negative Z is visible and the stuff in positive Z is invisible."""
pldef = None
if enable:
obj = self.doc.getObject(self.objname)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -24,7 +23,7 @@
from Show.SceneDetail import SceneDetail
class Pickability(SceneDetail):
"""Pickability(object, pickstyle = None):Plugin for TempoVis for altering pick style
"""Pickability(object, pickstyle = None):Plugin for TempoVis for altering pick style
of objects (i.e., selectability).
pickstyle may be:
PS_REGULAR = 0 # selectable
@@ -33,17 +32,17 @@ class Pickability(SceneDetail):
class_id = 'SDPickability'
propname = ''
objname = ''
def __init__(self, object, pickstyle = None):
self.objname = object.Name
self.doc = object.Document
self.key = self.objname
self.key = self.objname
if pickstyle is not None:
self.data = pickstyle
def scene_value(self):
return getPickStyle(self.doc.getObject(self.objname).ViewObject)
def apply_data(self, val):
setPickStyle(self.doc.getObject(self.objname).ViewObject, val)
@@ -65,7 +64,7 @@ def getPickStyleNode(viewprovider, make_if_missing = True):
pick_style.style.setValue(coin.SoPickStyle.SHAPE)
viewprovider.RootNode.insertChild(pick_style, 0)
return pick_style
def getPickStyle(viewprovider):
ps = getPickStyleNode(viewprovider, make_if_missing= False)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -25,13 +24,13 @@ from Show.SceneDetail import SceneDetail
class VProperty(SceneDetail):
"""VProperty(object, propname, val = None): plugin for TempoVis to alter ViewProvider properties"""
class_id = 'SDVProperty'
affects_persistence = True
propname = ''
objname = ''
mild_restore = True
def __init__(self, object, propname, val = None):
self.objname = object.Name
self.propname = propname
@@ -40,9 +39,9 @@ class VProperty(SceneDetail):
self.data = val
if propname == 'LinkVisibility': #seems to not be a property
self.affects_persistence = False
def scene_value(self):
return getattr(self.doc.getObject(self.objname).ViewObject, self.propname)
def apply_data(self, val):
setattr(self.doc.getObject(self.objname).ViewObject, self.propname, val)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -26,19 +25,19 @@ from Show.SceneDetail import SceneDetail
import FreeCADGui
class Workbench(SceneDetail):
"""Workbench(wb = None): Plugin for TempoVis for changing active workbench.
"""Workbench(wb = None): Plugin for TempoVis for changing active workbench.
wb: string, a name of a workbench (e.g. 'SketcherWorkbench')"""
class_id = 'SDWorkbench'
mild_restore = True
def __init__(self, wb = None):
self.key = 'workbench'
if wb is not None:
self.data = wb
def scene_value(self):
return FreeCADGui.activeWorkbench().name()
def apply_data(self, val):
FreeCADGui.activateWorkbench(val)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -20,11 +19,11 @@
# * Suite 330, Boston, MA 02111-1307, USA *
# * *
# ***************************************************************************/
def is3DObject(obj):
"""is3DObject(obj): tests if the object has some 3d geometry.
"""is3DObject(obj): tests if the object has some 3d geometry.
TempoVis is made only for objects in 3d view, so all objects that don't pass this check are ignored by TempoVis."""
# See "Gui Problem Sketcher and TechDraw" https://forum.freecadweb.org/viewtopic.php?f=3&t=22797
# observation: all viewproviders have transform node, then a switch node. If that switch node contains something, the object has something in 3d view.

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -26,18 +25,18 @@ import FreeCAD
class TVObserver(object):
def __init__(self):
FreeCAD.addDocumentObserver(self)
def stop(self):
FreeCAD.removeDocumentObserver(self)
def slotStartSaveDocument(self, doc, filepath):
from . import TVStack
TVStack._slotStartSaveDocument(doc)
def slotFinishSaveDocument(self, doc, filepath):
from . import TVStack
TVStack._slotFinishSaveDocument(doc)
def slotDeletedDocument(self, doc):
from . import TVStack
TVStack._slotDeletedDocument(doc)

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2019 *
# * Copyright (c) 2019 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -29,7 +28,7 @@ class TVStack(object):
index_LUT = None # Key = id(tempovis_instance). Value = index in the stack.
stack = None #list of weakrefs to TV instances. Using weakrefs, so that TempoVis can self-destruct if forgotten.
document = None
_rewind_tv = None
def __init__(self, document):
@@ -44,11 +43,11 @@ class TVStack(object):
idtv = id(tv)
ref = weakref.ref(tv, (lambda _, idtv=idtv, self=self : self._destruction(idtv)))
self.stack.insert(index, ref)
self.rebuild_index(index)
tv._inserted(self, index)
def _destruction(self, idtv):
# the tempovis itself is destroyed already. Purge it from the stack (it should have withdrawn itself, but just in case).
try:
@@ -60,7 +59,7 @@ class TVStack(object):
self.stack.pop(index)
self.index_LUT.pop(idtv)
self.rebuild_index(index)
def withdraw(self, tv):
idtv = id(tv)
index = self.index_LUT[idtv]
@@ -68,21 +67,21 @@ class TVStack(object):
self.index_LUT.pop(idtv)
self.rebuild_index(index)
tv = ref()
if tv:
tv._withdrawn(self, index)
def value_after(self, tv, detail):
"""value_after(tv, detail): returns tuple (tv1, detail), or None.
Here, tv1 is the tv that remembers the value, and detail is reference to recorded
data in tv1. None is returned, if no TVs in the stack after the provided one have
"""value_after(tv, detail): returns tuple (tv1, detail), or None.
Here, tv1 is the tv that remembers the value, and detail is reference to recorded
data in tv1. None is returned, if no TVs in the stack after the provided one have
recorded a change to this detail.
tv can be None, then, the function returns the original value of the detail, or
tv can be None, then, the function returns the original value of the detail, or
None, if the current value matches the original."""
from . import mTempoVis
index = self.index_LUT[id(tv)] if tv is not None else -1
for tvref in self.stack[index + 1 : ]:
tv = tvref()
@@ -90,14 +89,14 @@ class TVStack(object):
if tv.has(detail):
return (tv, tv.data[detail.full_key])
return None
def rebuild_index(self, start = 0):
if start == 0:
self.index_LUT = {}
for i in range(start, len(self.stack)):
self.index_LUT[id(self.stack[i]())] = i
def purge_dead(self):
"""removes dead TV instances from the stack"""
n = 0
@@ -108,18 +107,18 @@ class TVStack(object):
if n > 0:
self.rebuild_index()
return n
def dissolve(self):
"""silently cleans all TVs, so that they won't restore."""
for ref in self.stack:
if ref() is not None:
ref().forget()
def unwindForSaving(self):
from . import mTempoVis
self.rewindAfterSaving() #just in case there was a failed save before.
details = {} #dict of detail original values. Key = detail key; value = detail instance with data representing the original value
for ref in self.stack:
tv = ref()
@@ -127,26 +126,26 @@ class TVStack(object):
if not key in details:
if detail.affects_persistence:
details[detail.full_key] = detail
self._rewind_tv = mTempoVis.TempoVis(self.document, None)
for key, detail in details.items():
self._rewind_tv.modify(detail)
def rewindAfterSaving(self):
if self._rewind_tv is not None:
self._rewind_tv.restore()
self._rewind_tv = None
def getSplitSequence(self, tv):
"""getSplitSequence(tv): returns (list_before, list_after), neither list includes tv."""
index = self.index_LUT[id(tv)]
def deref(lst):
def deref(lst):
return [ref() for ref in lst]
return deref(self.stack[0:index]), deref(self.stack[index+1:])
def __getitem__(self, index):
return self.stack[index]()
def __len__(self):
return len(self.stack)
@@ -157,23 +156,23 @@ class TVStack(object):
def __reversed__(self):
for ref in reversed(self.stack):
yield ref()
def restoreAll(self):
for ref in reversed(self.stack):
ref().restore()
def byTag(self, tag):
return [ref() for ref in self.stack if ref().tag == tag]
def mainStack(document, create_if_missing = True):
"""mainStack(document, create_if_missing = True):returns the main TVStack instance for provided document"""
docname = document.Name
if create_if_missing:
if not docname in global_stacks:
global_stacks[docname] = TVStack(document)
return global_stacks.get(docname, None)
def _slotDeletedDocument(document):

View File

@@ -1,6 +1,5 @@
#/***************************************************************************
# * Copyright (c) Victor Titov (DeepSOIC) *
# * (vv.titov@gmail.com) 2016 *
# * Copyright (c) 2016 Victor Titov (DeepSOIC) <vv.titov@gmail.com> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
@@ -21,7 +20,7 @@
# * *
# ***************************************************************************/
# module is named mTempoVis, because Show.TimpoVis exposes the class as its member, and hides the module TempoVis.py.
# module is named mTempoVis, because Show.TimpoVis exposes the class as its member, and hides the module TempoVis.py
from . import Containers
@@ -37,7 +36,7 @@ Log = lambda msg: App.Console.PrintLog(msg + "\n")
from copy import copy
S_EMPTY = 0 # TV is initialized, but no changes were done through it
S_ACTIVE = 1 # TV has something to be undone
S_ACTIVE = 1 # TV has something to be undone
S_RESTORED = 2 # TV has been restored
S_INTERNAL = 3 # TV instance is being used by another TV instance as a redo data storage
@@ -61,15 +60,15 @@ class TempoVis(object):
then restoring all visibilities after editing.
Constructors:
TempoVis(document, stack = MAINSTACK, **kwargs): creates a new TempoVis.
TempoVis(document, stack = MAINSTACK, **kwargs): creates a new TempoVis.
document: required. Objects not belonging to the document can't be modified via TempoVis.
stack: optional. Which stack to insert this new TV into. Can be:
a TVStack instance (then, the new TV is added to the top of the stack),
MAINSTACK special value (a global stack for the document will be used), or
a TVStack instance (then, the new TV is added to the top of the stack),
MAINSTACK special value (a global stack for the document will be used), or
None (then, the TV is not in any stack, and can be manually instertd into one if desired).
Any additional keyword args are assigned as attributes. You can use it to immediately set a tag, for example.'''
document = None
@@ -77,47 +76,47 @@ class TempoVis(object):
data = None # dict. key = ("class_id","key"), value = instance of SceneDetail
data_requested = None #same as data, but stores (wanted) values passed to modify()
state = S_EMPTY
tag = '' #stores any user-defined string for identification purposes
def _init_attrs(self):
'''initialize member variables to empty values (needed because we can't use mutable initial values when initializing member variables in class definition)'''
self.data = {}
self.data_requested = {}
#<core interface>
def __init__(self, document, stack = MAINSTACK, **kwargs):
self._init_attrs()
self.document = document
if stack is MAINSTACK:
stack = TVStack.mainStack(document)
if stack is None:
pass
else:
stack.insert(self)
for key,val in kwargs.items():
setattr(self, key, val)
def __del__(self):
if self.state == S_ACTIVE:
self.restore(ultimate= True)
def has(self, detail):
'''has(self, detail): returns True if this TV has this detail value saved.
example: tv.has(VProperty(obj, "Visibility"))'''
return detail.full_key in self.data
def stored_val(self, detail):
'''stored_val(self, detail): returns value of detail remembered by this TV. If not, raises KeyError.'''
return self.data[detail.full_key].data
def save(self, detail, mild_restore = False):
'''save(detail, mild_restore = False):saves the scene detail to be restored.
'''save(detail, mild_restore = False):saves the scene detail to be restored.
The detail is saved only once; repeated calls are ignored.
mild_restore: internal, do not use.'''
self._change()
@@ -133,36 +132,36 @@ class TempoVis(object):
stored_dt.mild_restore = False
def modify(self, detail, mild_restore = None):
'''modify(detail, mild_restore = True): modifies scene detail through this TV.
'''modify(detail, mild_restore = True): modifies scene detail through this TV.
The value is provided as an instance of SceneDetail implementation.
The procedure takes care to account for the stack - that is, if in a TV applied
later than this one this detail was changed too, the value saved therein is altered,
The procedure takes care to account for the stack - that is, if in a TV applied
later than this one this detail was changed too, the value saved therein is altered,
rather than applied to the scene.
mild_restore: if True, when restoring later, checks if the value was changed
mild_restore: if True, when restoring later, checks if the value was changed
by user after last call to modify(), and doesn't restore if it was changed.
Example: tv.modify(VProperty(obj, "Visibility", True))'''
self._change()
if mild_restore is not None:
detail.mild_restore = mild_restore
# save current
self.save(detail, detail.mild_restore)
# apply
tv1, curr = self._value_after(detail)
if tv1 is not None:
tv1.data[detail.full_key].data = detail.data
else:
detail.apply_data(detail.data)
# and record.
if detail.mild_restore:
self.data_requested[detail.full_key] = copy(detail)
def restoreDetail(self, detail, ultimate = False):
'''restoreDetail(detail, ultimate = False): restores a specific scene detail.
ultimate: if true, the saved value is cleaned out.
@@ -173,15 +172,15 @@ class TempoVis(object):
self._restore_detail(detail)
if ultimate:
self.forgetDetail(detail)
def forgetDetail(self, detail):
'''forgetDetail(detail): ditches a saved detail value, making the change done through this TV permanent.'''
self.data.pop(detail.full_key, None)
self.data_requested.pop(detail.full_key, None)
def forget(self):
'''forget(self): clears this TV, making all changes done through it permanent.
'''forget(self): clears this TV, making all changes done through it permanent.
Also, withdraws the TV from the stack.'''
self.state = S_EMPTY
self.data = {}
@@ -190,15 +189,15 @@ class TempoVis(object):
def restore(self, ultimate = True):
'''restore(ultimate = True): undoes all changes done through this tempovis / restores saved scene details.
ultimate: if true, the saved values are cleaned out, and the TV is withdrawn from
ultimate: if true, the saved values are cleaned out, and the TV is withdrawn from
the stack. If false, the TV will still remember stuff, and restore can be called again.
'''
if self.state == S_RESTORED:
return
if self.state != S_INTERNAL and ultimate:
self.state = S_RESTORED
self.state = S_RESTORED
for key, detail in self.data.items():
try:
self._restoreDetail(detail)
@@ -209,9 +208,9 @@ class TempoVis(object):
self.data = {}
if self.is_in_stack:
self.stack.withdraw(self)
#</core interface>
#<stack interface>
def _inserted(self, stack, index):
'''calles when this tv is inserted into a stack'''
@@ -224,15 +223,15 @@ class TempoVis(object):
return self.stack is not None
#</stack interface>
#<convenience functions>
#<convenience functions>
def modifyVPProperty(self, doc_obj_or_list, prop_names, new_value = JUST_SAVE, mild_restore = None):
'''modifyVPProperty(doc_obj_or_list, prop_names, new_value = JUST_SAVE, mild_restore = None): modifies
prop_name property of ViewProvider of doc_obj_or_list, and remembers
original value of the property. Original values will be restored upon
TempoVis deletion, or call to restore().
mild_restore: test if user changed the value manually when restoring the TV.'''
if self.state == S_RESTORED:
Wrn("Attempting to use a TV that has been restored. There must be a problem with code.")
return
@@ -264,7 +263,7 @@ class TempoVis(object):
def restoreVPProperty(self, doc_obj_or_list, prop_names):
'''restoreVPProperty(doc_obj_or_list, prop_name, new_value): restores specific property changes.'''
from .SceneDetails.VProperty import VProperty
if not hasattr(doc_obj_or_list, '__iter__'):
doc_obj_or_list = [doc_obj_or_list]
if not isinstance(prop_names,(tuple,list)):
@@ -279,7 +278,7 @@ class TempoVis(object):
def saveBodyVisibleFeature(self, doc_obj_or_list):
"""saveBodyVisibleFeature(self, doc_obj_or_list): saves Visibility of currently
"""saveBodyVisibleFeature(self, doc_obj_or_list): saves Visibility of currently
visible feature, for every body of PartDesign features in the provided list."""
if not hasattr(doc_obj_or_list, '__iter__'):
doc_obj_or_list = [doc_obj_or_list]
@@ -297,7 +296,7 @@ class TempoVis(object):
return objs
def show(self, doc_obj_or_list, links_too = True, mild_restore = None):
'''show(doc_obj_or_list, links_too = True): shows objects (sets their Visibility to True).
'''show(doc_obj_or_list, links_too = True): shows objects (sets their Visibility to True).
doc_obj_or_list can be a document object, or a list of document objects.
If links_too is True, all Links of the objects are also hidden, by setting LinkVisibility attribute of each object.'''
doc_obj_or_list = self._3D_objects(doc_obj_or_list)
@@ -320,15 +319,15 @@ class TempoVis(object):
from .Containers import isAContainer
from .DepGraphTools import getAllDependencies, getAllDependent
if subname:
# a link-path was provided. doc_obj has nothing to do with the object we want
# a link-path was provided. doc_obj has nothing to do with the object we want
# to collect dependencies from. So, replace it with the one pointed by link-path.
cnt_chain = doc_obj.getSubObjectList(subname)
doc_obj = cnt_chain[-1].getLinkedObject()
# cnt_chain can either end with the object (e.g. if a sketch is in a part, and
# a link is to a part), or it may be a Link object (if we have a straight or
# cnt_chain can either end with the object (e.g. if a sketch is in a part, and
# a link is to a part), or it may be a Link object (if we have a straight or
# even nested Link to the sketch).
#
# I don't know why do we need that isAContainer check here, but I'm leaving it,
# I don't know why do we need that isAContainer check here, but I'm leaving it,
# realthunder must be knowing his business --DeepSOIC
cnt_chain = [ o for o in cnt_chain
if o==cnt_chain[-1] or isAContainer(o, links_too= True) ]
@@ -364,7 +363,7 @@ class TempoVis(object):
self._change()
from .SceneDetails.Camera import Camera
self.save(Camera(self.document))
def restoreCamera(self, ultimate = False):
from .SceneDetails.Camera import Camera
dt = Camera(self.document)
@@ -373,17 +372,17 @@ class TempoVis(object):
def setUnpickable(self, doc_obj_or_list, actual_pick_style = 2): #2 is coin.SoPickStyle.UNPICKABLE
'''setUnpickable(doc_obj_or_list, actual_pick_style = 2): sets object unpickable (transparent to clicks).
doc_obj_or_list: object or list of objects to alter (App)
actual_pick_style: optional parameter, specifying the actual pick style:
actual_pick_style: optional parameter, specifying the actual pick style:
0 = regular, 1 = bounding box, 2 (default) = unpickable.
Implementation detail: uses SoPickStyle node. If viewprovider already has a node
of this type as direct child, one is used. Otherwise, new one is created and
inserted as the very first node, and remains there even after restore()/deleting
Implementation detail: uses SoPickStyle node. If viewprovider already has a node
of this type as direct child, one is used. Otherwise, new one is created and
inserted as the very first node, and remains there even after restore()/deleting
tempovis. '''
from .SceneDetails.Pickability import Pickability
from .ShowUtils import is3DObject
if not hasattr(doc_obj_or_list, '__iter__'):
doc_obj_or_list = [doc_obj_or_list]
for doc_obj in doc_obj_or_list:
@@ -392,20 +391,20 @@ class TempoVis(object):
dt = Pickability(doc_obj, actual_pick_style)
self.modify(dt)
def clipPlane(self, doc_obj_or_list, enable, placement, offset = 0.02):
def clipPlane(self, doc_obj_or_list, enable, placement, offset = 0.02):
'''clipPlane(doc_obj_or_list, enable, placement, offset): slices off the object with a clipping plane.
doc_obj_or_list: object or list of objects to alter (App)
enable: True if you want clipping, False if you want to remove clipping:
enable: True if you want clipping, False if you want to remove clipping:
placement: XY plane of local coordinates of the placement is the clipping plane. The placement must be in document's global coordinate system.
offset: shifts the plane. Positive offset reveals more of the object.
Implementation detail: uses SoClipPlane node. If viewprovider already has a node
of this type as direct child, one is used. Otherwise, new one is created and
Implementation detail: uses SoClipPlane node. If viewprovider already has a node
of this type as direct child, one is used. Otherwise, new one is created and
inserted as the very first node. The node is left, but disabled when tempovis is restoring.'''
from .SceneDetails.ObjectClipPlane import ObjectClipPlane
from .ShowUtils import is3DObject
if not hasattr(doc_obj_or_list, '__iter__'):
doc_obj_or_list = [doc_obj_or_list]
for doc_obj in doc_obj_or_list:
@@ -413,14 +412,14 @@ class TempoVis(object):
continue
dt = ObjectClipPlane(doc_obj, enable, placement, offset)
self.modify(dt)
@staticmethod
def allVisibleObjects(aroundObject):
'''allVisibleObjects(aroundObject): returns list of objects that have to be toggled invisible for only aroundObject to remain.
'''allVisibleObjects(aroundObject): returns list of objects that have to be toggled invisible for only aroundObject to remain.
If a whole container can be made invisible, it is returned, instead of its child objects.'''
from .ShowUtils import is3DObject
from . import Containers
chain = Containers.VisGroupChain(aroundObject)
result = []
for i in range(len(chain)):
@@ -434,13 +433,13 @@ class TempoVis(object):
if container.isChildVisible(obj):
result.append(obj)
return result
def sketchClipPlane(self, sketch, enable = None, reverted = False):
'''sketchClipPlane(sketch, enable = None): Clips all objects by plane of sketch.
'''sketchClipPlane(sketch, enable = None): Clips all objects by plane of sketch.
If enable argument is omitted, calling the routine repeatedly will toggle clipping plane.'''
from .SceneDetails.ClipPlane import ClipPlane
editDoc = Gui.editDocument()
if editDoc is None:
doc = sketch.Document
@@ -457,14 +456,14 @@ class TempoVis(object):
if enable: # clip plane shall be disabled so new placement can be applied
self.modify(ClipPlane(doc, 0))
self.modify(ClipPlane(doc, toggle, pla, 0.02))
sketch.ViewObject.SectionView = enable if enable is not None else not sketch.ViewObject.SectionView
def activateWorkbench(self, wb_name):
from .SceneDetails.Workbench import Workbench
self.modify(Workbench(wb_name))
#</convenience functions>
#<internals>
@@ -482,7 +481,7 @@ class TempoVis(object):
else:
#modify saved detail of higher TV
tv1.data[detail.full_key].data = p.data
def _purge_milds(self, detail):
"""_purge_milds(detail): wipes out detail from earlier TVs if the detail is mild-restore."""
if not self.is_in_stack:
@@ -502,13 +501,13 @@ class TempoVis(object):
self.state = S_ACTIVE
if self.state == S_RESTORED:
Wrn("Attempting to use a TV that has been restored. There must be a problem with code.")
self.tv_redo = None
self.tv_redo = None
def _value_after(self, detail, query_scene = False):
'''_value_current(detail): returns (tv, detail1). SceneDetail instance holds "current" value of
scene detail (current from the context of this TV; i.e. either the current scene
'''_value_current(detail): returns (tv, detail1). SceneDetail instance holds "current" value of
scene detail (current from the context of this TV; i.e. either the current scene
status, or the saved state from upper TVs).
If no upper TV has saved the detail value, returns either (None, None), or
If no upper TV has saved the detail value, returns either (None, None), or
(None, detail1) if query_scene is True, where detail1 holds value from the scene.'''
def scene_value():
if query_scene:
@@ -517,7 +516,7 @@ class TempoVis(object):
return (None, cpy)
else:
return (None, None)
if self.is_in_stack:
va = self.stack.value_after(self, detail)
if va is None:
@@ -526,20 +525,18 @@ class TempoVis(object):
return va
else:
return scene_value()
def _3D_objects(self, doc_obj_or_list):
"""_3D_objects(doc_obj_or_list): returns list of objects that are in 3d view."""
from .ShowUtils import is3DObject
if not hasattr(doc_obj_or_list, '__iter__'):
doc_obj_or_list = [doc_obj_or_list]
return [obj for obj in doc_obj_or_list if is3DObject(obj)]
def __getstate__(self):
return None
def __setstate__(self, state):
self._init_attrs()