Arch: Lightweight mode for Arch References

This commit is contained in:
Yorik van Havre
2019-07-11 19:37:10 -03:00
parent ba9d945308
commit 8932d9296e
3 changed files with 248 additions and 69 deletions

View File

@@ -25,7 +25,11 @@ __author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
import FreeCAD,os,zipfile,re,sys
import FreeCAD
import os
import zipfile
import re
import sys
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
@@ -91,14 +95,23 @@ class ArchReference:
obj.addProperty("App::PropertyFile","File","Reference",QT_TRANSLATE_NOOP("App::Property","The base file this component is built upon"))
if not "Part" in pl:
obj.addProperty("App::PropertyString","Part","Reference",QT_TRANSLATE_NOOP("App::Property","The part to use from the base file"))
if not "TransientReference" in pl:
obj.addProperty("App::PropertyBool","TransientReference","Reference",QT_TRANSLATE_NOOP("App::Property","If True, the shape will be discarded when turning visibility off, resulting in a lighter file, but with an additional loading time when turning the object back on"))
if not "ReferenceMode" in pl:
obj.addProperty("App::PropertyEnumeration","ReferenceMode","Reference",QT_TRANSLATE_NOOP("App::Property","The way the referenced objects are included in the current document. 'Normal' includes the shape, 'Transient' discards the shape when the object is switched off (smaller filesize), 'Lightweight' does not import the shape but only the OpenInventor representation"))
obj.ReferenceMode = ["Normal","Transient","Lightweight"]
if "TransientReference" in pl:
if obj.TransientReference:
obj.ReferenceMode = "Transient"
obj.removeProperty("TransientReference")
FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" TransientReference property to ReferenceMode\n")
self.Type = "Reference"
def onDocumentRestored(self,obj):
ArchReference.setProperties(self,obj)
self.reload = False
if obj.ReferenceMode == "Lightweight":
if obj.ViewObject and obj.ViewObject.Proxy:
obj.ViewObject.Proxy.loadInventor(obj)
def __getstate__(self):
@@ -112,26 +125,31 @@ class ArchReference:
if prop in ["File","Part"]:
self.reload = True
elif prop == "TransientReference":
if obj.TransientReference:
elif prop == "ReferenceMode":
if obj.ReferenceMode == "Normal":
if obj.ViewObject and obj.ViewObject.Proxy:
obj.ViewObject.Proxy.unloadInventor(obj)
if (not obj.Shape) or obj.Shape.isNull():
self.reload = True
obj.touch()
else:
if obj.ViewObject:
obj.ViewObject.Visibility = False
else:
self.reload = False
import Part
pl = obj.Placement
obj.Shape = Part.Shape()
obj.Placement = pl
elif obj.ReferenceMode == "Transient":
if obj.ViewObject and obj.ViewObject.Proxy:
obj.ViewObject.Proxy.unloadInventor(obj)
self.reload = False
elif obj.ReferenceMode == "Lightweight":
self.reload = False
import Part
pl = obj.Placement
obj.Shape = Part.Shape()
obj.Placement = pl
if obj.ViewObject and obj.ViewObject.Proxy:
obj.ViewObject.Proxy.loadInventor(obj)
def execute(self,obj):
pl = obj.Placement
filename = self.getFile(obj)
if filename and obj.Part and self.reload:
if filename and obj.Part and self.reload and obj.ReferenceMode in ["Normal","Transient"]:
self.parts = self.getPartsList(obj)
if self.parts:
zdoc = zipfile.ZipFile(filename)
@@ -175,7 +193,7 @@ class ArchReference:
else:
# search for subpaths in current folder
altfile = None
subdirs = splitall(os.path.dirname(filename))
subdirs = self.splitall(os.path.dirname(filename))
for i in range(len(subdirs)):
subpath = [currentdir]+subdirs[-i:]+[basename]
altfile = os.path.join(*subpath)
@@ -185,6 +203,8 @@ class ArchReference:
return filename
def getPartsList(self,obj,filename=None):
"returns a list of Part-based objects in a FCStd file"
parts = {}
filename = self.getFile(obj,filename)
@@ -226,6 +246,8 @@ class ArchReference:
def getColors(self,obj):
"returns the DiffuseColor of the referenced object"
filename = self.getFile(obj)
if not filename:
return None
@@ -269,6 +291,24 @@ class ArchReference:
return colors
return None
def splitall(self,path):
"splits a path between its components"
allparts = []
while 1:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts
class ViewProviderArchReference:
@@ -380,7 +420,7 @@ class ViewProviderArchReference:
vobj.Object.Proxy.reload = True
vobj.Object.Proxy.execute(vobj.Object)
else:
if hasattr(vobj.Object,"TransientReference") and vobj.Object.TransientReference:
if hasattr(vobj.Object,"ReferenceMode") and vobj.Object.ReferenceMode == "Transient":
vobj.Object.Proxy.reload = False
import Part
pl = vobj.Object.Placement
@@ -420,11 +460,127 @@ class ViewProviderArchReference:
if self.Object.File:
FreeCAD.openDocument(self.Object.File)
def loadInventor(self,obj):
"loads an openinventor file and replace the root node of this object"
# check inventor contents
ivstring = self.getInventorString(obj)
if not ivstring:
FreeCAD.Console.PrintWarning("Unable to get lightWeight node for object referenced in "+obj.Label+"\n")
return
from pivy import coin
inputnode = coin.SoInput()
inputnode.setBuffer(ivstring)
lwnode = coin.SoDB.readAll(inputnode)
if not isinstance(lwnode,coin.SoSeparator):
FreeCAD.Console.PrintError("Invalid lightWeight node for object referenced in "+obj.Label+"\n")
return
if lwnode.getNumChildren() < 2:
FreeCAD.Console.PrintError("Invalid lightWeight node for object referenced in "+obj.Label+"\n")
return
flatlines = lwnode
shaded = lwnode.getChild(0)
wireframe = lwnode.getChild(1)
# check node contents
rootnode = obj.ViewObject.RootNode
if rootnode.getNumChildren() < 3:
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
return
switch = rootnode.getChild(2)
if switch.getNumChildren() != 4:
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
return
# keep a copy of the original nodes
self.orig_flatlines = switch.getChild(0).copy()
self.orig_shaded = switch.getChild(1).copy()
self.orig_wireframe = switch.getChild(2).copy()
# replace root node of object
switch.replaceChild(0,flatlines)
switch.replaceChild(1,shaded)
switch.replaceChild(2,wireframe)
def unloadInventor(self,obj):
"restore original nodes"
if (not hasattr(self,"orig_flatlines")) or (not self.orig_flatlines):
return
if (not hasattr(self,"orig_shaded")) or (not self.orig_shaded):
return
if (not hasattr(self,"orig_wireframe")) or (not self.orig_wireframe):
return
# check node contents
rootnode = obj.ViewObject.RootNode
if rootnode.getNumChildren() < 3:
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
return
switch = rootnode.getChild(2)
if switch.getNumChildren() != 4:
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
return
# replace root node of object
switch.replaceChild(0,self.orig_flatlines)
switch.replaceChild(1,self.orig_shaded)
switch.replaceChild(2,self.orig_wireframe)
# discard old content
self.orig_flatlines = None
self.orig_shaded = None
self.orig_wireframe = None
def getInventorString(self,obj):
"locates and loads an iv file saved together with an object, if existing"
filename = obj.Proxy.getFile(obj)
if not filename:
return None
part = obj.Part
if not obj.Part:
return None
zdoc = zipfile.ZipFile(filename)
if not "Document.xml" in zdoc.namelist():
return None
ivfile = None
with zdoc.open("Document.xml") as docf:
writemode1 = False
writemode2 = False
for line in docf:
if sys.version_info.major >= 3:
line = line.decode("utf8")
if ("<Object name=" in line) and (part in line):
writemode1 = True
elif writemode1 and ("<Property name=\"SavedInventor\"" in line):
writemode1 = False
writemode2 = True
elif writemode2 and ("<FileIncluded file=" in line):
n = re.findall('file=\"(.*?)\"',line)
if n:
ivfile = n[0]
break
if not ivfile:
return None
if not ivfile in zdoc.namelist():
return None
f = zdoc.open(ivfile)
buf = f.read()
if sys.version_info.major >= 3:
buf = buf.decode("utf8")
f.close()
buf = buf.replace("lineWidth 2","lineWidth "+str(int(obj.ViewObject.LineWidth)))
return buf
class ArchReferenceTaskPanel:
'''The editmode TaskPanel for Axis objects'''
'''The editmode TaskPanel for Reference objects'''
def __init__(self,obj):
@@ -539,21 +695,7 @@ class ArchReferenceCommand:
FreeCAD.ActiveDocument.commitTransaction()
FreeCADGui.doCommand("obj.ViewObject.Document.setEdit(obj.ViewObject, 0)")
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Reference', ArchReferenceCommand())
def splitall(path):
allparts = []
while 1:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts