Arch: Upgraded reference tool
* Fixed translations * Reference objects can now use whole file contents instead of having to choose a part * Support for DXF files * Support for IFC files (if NativeIFC is available) * General code cleanup
This commit is contained in:
committed by
Yorik van Havre
parent
f4df8d0c10
commit
56ebe6a7b8
@@ -53,10 +53,9 @@ else:
|
||||
|
||||
|
||||
|
||||
def makeReference(filepath=None,partname=None,name=None):
|
||||
def makeReference(filepath=None, partname=None, name=None):
|
||||
|
||||
|
||||
"makeReference([filepath],[partname],[name]): Creates an Arch Reference object"
|
||||
"""makeReference([filepath],[partname],[name]): Creates an Arch Reference object"""
|
||||
|
||||
if not FreeCAD.ActiveDocument:
|
||||
FreeCAD.Console.PrintError("No active document. Aborting\n")
|
||||
@@ -78,52 +77,61 @@ def makeReference(filepath=None,partname=None,name=None):
|
||||
|
||||
class ArchReference:
|
||||
|
||||
"""The Arch Reference object"""
|
||||
|
||||
"The Arch Reference object"
|
||||
|
||||
def __init__(self,obj):
|
||||
def __init__(self, obj):
|
||||
|
||||
obj.Proxy = self
|
||||
ArchReference.setProperties(self,obj)
|
||||
ArchReference.setProperties(self, obj)
|
||||
self.Type = "Reference"
|
||||
self.reload = True
|
||||
|
||||
def setProperties(self,obj):
|
||||
|
||||
def setProperties(self, obj):
|
||||
|
||||
pl = obj.PropertiesList
|
||||
if not "File" in pl:
|
||||
obj.addProperty("App::PropertyFile","File","Reference",QT_TRANSLATE_NOOP("App::Property","The base file this component is built upon"))
|
||||
t = QT_TRANSLATE_NOOP("App::Property","The base file this component is built upon")
|
||||
obj.addProperty("App::PropertyFile","File","Reference",t)
|
||||
if not "Part" in pl:
|
||||
obj.addProperty("App::PropertyString","Part","Reference",QT_TRANSLATE_NOOP("App::Property","The part to use from the base file"))
|
||||
t = QT_TRANSLATE_NOOP("App::Property","The part to use from the base file")
|
||||
obj.addProperty("App::PropertyString","Part","Reference",t)
|
||||
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"))
|
||||
t = 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.addProperty("App::PropertyEnumeration","ReferenceMode","Reference",t)
|
||||
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")
|
||||
t = translate("Arch", "TransientReference property to ReferenceMode")
|
||||
FreeCAD.Console.PrintMessage(translate("Arch","Upgrading")+" "+obj.Label+" "+t+"\n")
|
||||
if not "FuseArch" in pl:
|
||||
obj.addProperty("App::PropertyBool","FuseArch", "Reference", QT_TRANSLATE_NOOP("App::Property","Fuse objects of same material"))
|
||||
t = QT_TRANSLATE_NOOP("App::Property","Fuse objects of same material")
|
||||
obj.addProperty("App::PropertyBool","FuseArch", "Reference", t)
|
||||
self.Type = "Reference"
|
||||
|
||||
def onDocumentRestored(self,obj):
|
||||
|
||||
ArchReference.setProperties(self,obj)
|
||||
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 dumps(self):
|
||||
|
||||
return None
|
||||
|
||||
def loads(self,state):
|
||||
|
||||
def loads(self, state):
|
||||
|
||||
return None
|
||||
|
||||
def onChanged(self,obj,prop):
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
|
||||
if prop in ["File","Part"]:
|
||||
self.reload = True
|
||||
@@ -147,32 +155,112 @@ class ArchReference:
|
||||
if obj.ViewObject and obj.ViewObject.Proxy:
|
||||
obj.ViewObject.Proxy.loadInventor(obj)
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
def execute(self, obj):
|
||||
|
||||
import Part
|
||||
pl = obj.Placement
|
||||
filename = self.getFile(obj)
|
||||
if filename and obj.Part and self.reload and obj.ReferenceMode in ["Normal","Transient"]:
|
||||
if filename and self.reload and obj.ReferenceMode in ["Normal","Transient"]:
|
||||
self.parts = self.getPartsList(obj)
|
||||
if self.parts:
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if zdoc:
|
||||
if obj.Part in self.parts:
|
||||
if self.parts[obj.Part][1] in zdoc.namelist():
|
||||
f = zdoc.open(self.parts[obj.Part][1])
|
||||
shapedata = f.read()
|
||||
f.close()
|
||||
shapedata = shapedata.decode("utf8")
|
||||
shape = self.cleanShape(shapedata,obj,self.parts[obj.Part][2])
|
||||
obj.Shape = shape
|
||||
if not pl.isIdentity():
|
||||
obj.Placement = pl
|
||||
if filename.lower().endswith(".fcstd"):
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if zdoc:
|
||||
if obj.Part:
|
||||
if obj.Part in self.parts:
|
||||
if self.parts[obj.Part][1] in zdoc.namelist():
|
||||
f = zdoc.open(self.parts[obj.Part][1])
|
||||
shapedata = f.read()
|
||||
f.close()
|
||||
shapedata = shapedata.decode("utf8")
|
||||
shape = self.cleanShape(shapedata,obj,self.parts[obj.Part][2])
|
||||
obj.Shape = shape
|
||||
if not pl.isIdentity():
|
||||
obj.Placement = pl
|
||||
else:
|
||||
t = translate("Arch","Part not found in file")
|
||||
FreeCAD.Console.PrintError(t+"\n")
|
||||
else:
|
||||
print("Part not found in file")
|
||||
shapes = []
|
||||
for part in self.parts.values():
|
||||
f = zdoc.open(part[1])
|
||||
shapedata = f.read()
|
||||
f.close()
|
||||
shapedata = shapedata.decode("utf8")
|
||||
shape = self.cleanShape(shapedata,obj)
|
||||
shapes.append(shape)
|
||||
if shapes:
|
||||
obj.Shape = Part.makeCompound(shapes)
|
||||
elif filename.lower().endswith(".ifc"):
|
||||
ifcfile = self.getIfcFile(filename)
|
||||
if not ifcfile:
|
||||
return
|
||||
try:
|
||||
import ifc_tools
|
||||
import ifc_generator
|
||||
except:
|
||||
t = translate("Arch","NativeIFC not available - unable to process IFC files")
|
||||
FreeCAD.Console.PrintError(t+"\n")
|
||||
return
|
||||
elements = self.getIFCElements(obj, ifcfile)
|
||||
shape, colors = ifc_generator.generate_shape(ifcfile, elements, cached=True)
|
||||
if shape:
|
||||
placement = shape.Placement
|
||||
obj.Shape = shape
|
||||
obj.Placement = placement
|
||||
if colors:
|
||||
ifc_tools.set_colors(obj, colors)
|
||||
elif filename.lower().endswith(".dxf"):
|
||||
# create a special parameter set to control the DXF importer
|
||||
loc = "User parameter:BaseApp/Preferences/Mod/Arch"
|
||||
hGrp = FreeCAD.ParamGet(loc).GetGroup("RefDxfImport")
|
||||
hGrp.SetBool("dxfUseDraftVisGroups", False)
|
||||
hGrp.SetBool("dxfGetOriginalColors", False)
|
||||
hGrp.SetBool("groupLayers", True)
|
||||
hGrp.SetFloat("dxfScaling", 1.0)
|
||||
hGrp.SetBool("dxftext", False)
|
||||
hGrp.SetBool("dxfImportPoints", True)
|
||||
hGrp.SetBool("dxflayout", False)
|
||||
hGrp.SetBool("dxfstarblocks", False)
|
||||
doc = obj.Document
|
||||
oldobjs = list(doc.Objects)
|
||||
import Import
|
||||
Import.readDXF(filename, doc.Name, True, loc + "/RefDxfImport")
|
||||
newobjs = [o for o in doc.Objects if o not in oldobjs]
|
||||
shapes = [o.Shape for o in newobjs if o.isDerivedFrom("Part::Feature")]
|
||||
if len(shapes) == 1:
|
||||
obj.Shape = shapes[0]
|
||||
elif len(shapes) > 1:
|
||||
obj.Shape = Part.makeCompound(shapes)
|
||||
names = [o.Name for o in newobjs]
|
||||
for n in names:
|
||||
doc.removeObject(n)
|
||||
self.reload = False
|
||||
|
||||
def cleanShape(self,shapedata,obj,materials):
|
||||
|
||||
"cleans the imported shape"
|
||||
def getIFCElements(self, obj, ifcfile):
|
||||
|
||||
"""returns IFC elements for this object"""
|
||||
|
||||
try:
|
||||
import ifc_generator
|
||||
except:
|
||||
t = translate("Arch","NativeIFC not available - unable to process IFC files")
|
||||
FreeCAD.Console.PrintError(t+"\n")
|
||||
return
|
||||
if obj.Part:
|
||||
element = ifcfile[int(obj.Part)]
|
||||
else:
|
||||
element = ifcfile.by_type("IfcProject")[0]
|
||||
elements = ifc_generator.get_decomposed_elements(element)
|
||||
elements = ifc_generator.filter_types(elements)
|
||||
return elements
|
||||
|
||||
|
||||
def cleanShape(self, shapedata, obj, materials=None):
|
||||
|
||||
"""cleans the imported shape"""
|
||||
|
||||
import Part
|
||||
shape = Part.Shape()
|
||||
@@ -193,7 +281,7 @@ class ArchReference:
|
||||
break
|
||||
else:
|
||||
shapes.append(edge)
|
||||
print("solids:",len(shape.Solids),"mattable:",materials)
|
||||
#print("solids:",len(shape.Solids),"mattable:",materials)
|
||||
for key,solindexes in materials.items():
|
||||
if key == "Undefined":
|
||||
# do not join objects with no defined material
|
||||
@@ -212,31 +300,38 @@ class ArchReference:
|
||||
try:
|
||||
shape = shape.removeSplitter()
|
||||
except Exception:
|
||||
print(obj.Label,": error removing splitter")
|
||||
t = translate("Arch","Error removing splitter")
|
||||
FreeCAD.Console.PrintError(obj.Label+": "+t+"\n")
|
||||
return shape
|
||||
|
||||
|
||||
def exists(self,filepath):
|
||||
|
||||
"case-insensitive version of os.path.exists. Returns the actual file path or None"
|
||||
"""case-insensitive version of os.path.exists. Returns the actual file path or None"""
|
||||
|
||||
if os.path.exists(filepath):
|
||||
return filepath
|
||||
base, ext = os.path.splitext(filepath)
|
||||
for e in [".fcstd",".FCStd",".FCSTD"]:
|
||||
if os.path.exists(base + e):
|
||||
return base + e
|
||||
# check for uppercase/lowercase extensions
|
||||
p, e = os.path.splitext(filepath)
|
||||
if os.path.exists(p + e.lower()):
|
||||
return p + e.lower()
|
||||
if os.path.exists(p + e.upper()):
|
||||
return p + e.upper()
|
||||
return None
|
||||
|
||||
|
||||
def getFile(self,obj,filename=None):
|
||||
|
||||
"gets a valid file, if possible"
|
||||
"""gets a valid file, if possible"""
|
||||
|
||||
if not filename:
|
||||
filename = obj.File
|
||||
if not filename:
|
||||
return None
|
||||
if not filename.lower().endswith(".fcstd"):
|
||||
return None
|
||||
if not filename.lower().endswith(".ifc"):
|
||||
if not filename.lower().endswith(".dxf"):
|
||||
return None
|
||||
if not self.exists(filename):
|
||||
# search for the file in the current directory if not found
|
||||
basename = os.path.basename(filename)
|
||||
@@ -258,15 +353,56 @@ class ArchReference:
|
||||
return None
|
||||
return self.exists(filename)
|
||||
|
||||
def getPartsList(self,obj,filename=None):
|
||||
|
||||
"returns a list of Part-based objects in a FCStd file"
|
||||
def getPartsList(self, obj, filename=None):
|
||||
|
||||
"""returns a list of Part-based objects in a file"""
|
||||
|
||||
filename = self.getFile(obj, filename)
|
||||
if not filename:
|
||||
return None
|
||||
if filename.lower().endswith(".fcstd"):
|
||||
return self.getPartsListFCSTD(obj, filename)
|
||||
elif filename.lower().endswith(".ifc"):
|
||||
return self.getPartsListIFC(obj, filename)
|
||||
elif filename.lower().endswith(".dxf"):
|
||||
return self.getPartsListDXF(obj, filename)
|
||||
|
||||
|
||||
def getPartsListDXF(self, obj, filename):
|
||||
|
||||
"""returns a list of Part-based objects in a DXF file"""
|
||||
|
||||
# support layers
|
||||
#with open(filename) as f:
|
||||
# txt = f.read()
|
||||
return {}
|
||||
|
||||
|
||||
def getPartsListIFC(self, obj, filename):
|
||||
|
||||
"""returns a list of Part-based objects in a IFC file"""
|
||||
|
||||
ifcfile = self.getIfcFile(filename)
|
||||
if not ifcfile:
|
||||
return None
|
||||
structs = ifcfile.by_type("IfcSpatialElement")
|
||||
res = {}
|
||||
for s in structs:
|
||||
n = s.Name
|
||||
if not n:
|
||||
n = ""
|
||||
name = "#" + str(s.id()) + " " + n + "(" + s.is_a() + ")"
|
||||
res[str(s.id())] = [name, s, None]
|
||||
return res
|
||||
|
||||
|
||||
def getPartsListFCSTD(self, obj, filename):
|
||||
|
||||
"""returns a list of Part-based objects in a FCStd file"""
|
||||
|
||||
parts = {}
|
||||
materials = {}
|
||||
filename = self.getFile(obj,filename)
|
||||
if not filename:
|
||||
return parts
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
with zdoc.open("Document.xml") as docf:
|
||||
name = None
|
||||
@@ -313,9 +449,25 @@ class ArchReference:
|
||||
writemode = False
|
||||
return parts
|
||||
|
||||
def getColors(self,obj):
|
||||
|
||||
"returns the DiffuseColor of the referenced object"
|
||||
def getIfcFile(self, filename):
|
||||
|
||||
"""Gets an IfcOpenShell object"""
|
||||
|
||||
try:
|
||||
import ifcopenshell
|
||||
except:
|
||||
t = translate("Arch","NativeIFC not available - unable to process IFC files")
|
||||
FreeCAD.Console.PrintError(t+"\n")
|
||||
return None
|
||||
if not getattr(self, "ifcfile", None):
|
||||
self.ifcfile = ifcopenshell.open(filename)
|
||||
return self.ifcfile
|
||||
|
||||
|
||||
def getColors(self, obj):
|
||||
|
||||
"""returns the DiffuseColor of the referenced object"""
|
||||
|
||||
filename = self.getFile(obj)
|
||||
if not filename:
|
||||
@@ -323,42 +475,43 @@ class ArchReference:
|
||||
part = obj.Part
|
||||
if not obj.Part:
|
||||
return None
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if not "GuiDocument.xml" in zdoc.namelist():
|
||||
return None
|
||||
colorfile = None
|
||||
with zdoc.open("GuiDocument.xml") as docf:
|
||||
writemode1 = False
|
||||
writemode2 = False
|
||||
for line in docf:
|
||||
line = line.decode("utf8")
|
||||
if ("<ViewProvider name=" in line) and (part in line):
|
||||
writemode1 = True
|
||||
elif writemode1 and ("<Property name=\"DiffuseColor\"" in line):
|
||||
writemode1 = False
|
||||
writemode2 = True
|
||||
elif writemode2 and ("<ColorList file=" in line):
|
||||
n = re.findall('file=\"(.*?)\"',line)
|
||||
if n:
|
||||
colorfile = n[0]
|
||||
break
|
||||
if not colorfile:
|
||||
return None
|
||||
if not colorfile in zdoc.namelist():
|
||||
return None
|
||||
colors = []
|
||||
cf = zdoc.open(colorfile)
|
||||
buf = cf.read()
|
||||
cf.close()
|
||||
for i in range(1,int(len(buf)/4)):
|
||||
colors.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0))
|
||||
if colors:
|
||||
return colors
|
||||
return None
|
||||
colors = None
|
||||
if filename.lower().endswith(".fcstd"):
|
||||
zdoc = zipfile.ZipFile(filename)
|
||||
if not "GuiDocument.xml" in zdoc.namelist():
|
||||
return None
|
||||
colorfile = None
|
||||
with zdoc.open("GuiDocument.xml") as docf:
|
||||
writemode1 = False
|
||||
writemode2 = False
|
||||
for line in docf:
|
||||
line = line.decode("utf8")
|
||||
if ("<ViewProvider name=" in line) and (part in line):
|
||||
writemode1 = True
|
||||
elif writemode1 and ("<Property name=\"DiffuseColor\"" in line):
|
||||
writemode1 = False
|
||||
writemode2 = True
|
||||
elif writemode2 and ("<ColorList file=" in line):
|
||||
n = re.findall('file=\"(.*?)\"',line)
|
||||
if n:
|
||||
colorfile = n[0]
|
||||
break
|
||||
if not colorfile:
|
||||
return None
|
||||
if not colorfile in zdoc.namelist():
|
||||
return None
|
||||
colors = []
|
||||
cf = zdoc.open(colorfile)
|
||||
buf = cf.read()
|
||||
cf.close()
|
||||
for i in range(1,int(len(buf)/4)):
|
||||
colors.append((buf[i*4+3]/255.0,buf[i*4+2]/255.0,buf[i*4+1]/255.0,buf[i*4]/255.0))
|
||||
return colors
|
||||
|
||||
|
||||
def splitall(self,path):
|
||||
|
||||
"splits a path between its components"
|
||||
"""splits a path between its components"""
|
||||
|
||||
allparts = []
|
||||
while 1:
|
||||
@@ -378,28 +531,34 @@ class ArchReference:
|
||||
class ViewProviderArchReference:
|
||||
|
||||
|
||||
"A View Provider for the Arch Reference object"
|
||||
"""A View Provider for the Arch Reference object"""
|
||||
|
||||
|
||||
def __init__(self,vobj):
|
||||
|
||||
vobj.Proxy = self
|
||||
self.setProperties(vobj)
|
||||
|
||||
|
||||
def setProperties(self,vobj):
|
||||
|
||||
pl = vobj.PropertiesList
|
||||
if not "TimeStamp" in pl:
|
||||
vobj.addProperty("App::PropertyFloat","TimeStamp","Reference",QT_TRANSLATE_NOOP("App::Property","The latest time stamp of the linked file"))
|
||||
t = QT_TRANSLATE_NOOP("App::Property","The latest time stamp of the linked file")
|
||||
vobj.addProperty("App::PropertyFloat","TimeStamp","Reference",t)
|
||||
vobj.setEditorMode("TimeStamp",2)
|
||||
if not "UpdateColors" in pl:
|
||||
vobj.addProperty("App::PropertyBool","UpdateColors","Reference",QT_TRANSLATE_NOOP("App::Property","If true, the colors from the linked file will be kept updated"))
|
||||
t = QT_TRANSLATE_NOOP("App::Property","If true, the colors from the linked file will be kept updated")
|
||||
vobj.addProperty("App::PropertyBool","UpdateColors","Reference",t)
|
||||
vobj.UpdateColors = True
|
||||
|
||||
|
||||
def getIcon(self):
|
||||
|
||||
import Arch_rc
|
||||
return ":/icons/Arch_Reference.svg"
|
||||
|
||||
|
||||
def attach(self,vobj):
|
||||
|
||||
self.Object = vobj.Object
|
||||
@@ -409,29 +568,35 @@ class ViewProviderArchReference:
|
||||
s = params.get_param_arch("ReferenceCheckInterval")
|
||||
self.timer.start(1000*s)
|
||||
|
||||
|
||||
def dumps(self):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def loads(self,state):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def updateData(self,obj,prop):
|
||||
|
||||
if (prop == "Shape") and hasattr(obj.ViewObject,"UpdateColors") and obj.ViewObject.UpdateColors:
|
||||
if obj.Shape and not obj.Shape.isNull():
|
||||
colors = obj.Proxy.getColors(obj)
|
||||
if colors:
|
||||
obj.ViewObject.DiffuseColor = colors
|
||||
from DraftGui import todo
|
||||
todo.delay(self.recolorize,obj.ViewObject)
|
||||
if (prop == "Shape"):
|
||||
if hasattr(obj.ViewObject,"UpdateColors") and obj.ViewObject.UpdateColors:
|
||||
if obj.Shape and not obj.Shape.isNull():
|
||||
colors = obj.Proxy.getColors(obj)
|
||||
if colors:
|
||||
obj.ViewObject.DiffuseColor = colors
|
||||
from DraftGui import todo
|
||||
todo.delay(self.recolorize,obj.ViewObject)
|
||||
|
||||
|
||||
def recolorize(self,vobj):
|
||||
|
||||
if hasattr(vobj,"DiffuseColor") and hasattr(vobj,"UpdateColors") and vobj.UpdateColors:
|
||||
vobj.DiffuseColor = vobj.DiffuseColor
|
||||
|
||||
|
||||
def checkChanges(self):
|
||||
|
||||
"checks if the linked file has changed"
|
||||
@@ -455,6 +620,7 @@ class ViewProviderArchReference:
|
||||
self.Object.touch()
|
||||
self.Object.ViewObject.TimeStamp = st_mtime
|
||||
|
||||
|
||||
def onChanged(self,vobj,prop):
|
||||
|
||||
if prop == "ShapeColor":
|
||||
@@ -468,12 +634,14 @@ class ViewProviderArchReference:
|
||||
vobj.Object.Proxy.reload = True
|
||||
vobj.Object.Proxy.execute(vobj.Object)
|
||||
else:
|
||||
if hasattr(vobj.Object,"ReferenceMode") and vobj.Object.ReferenceMode == "Transient":
|
||||
vobj.Object.Proxy.reload = False
|
||||
import Part
|
||||
pl = vobj.Object.Placement
|
||||
vobj.Object.Shape = Part.Shape()
|
||||
vobj.Object.Placement = pl
|
||||
if hasattr(vobj.Object,"ReferenceMode"):
|
||||
if vobj.Object.ReferenceMode == "Transient":
|
||||
vobj.Object.Proxy.reload = False
|
||||
import Part
|
||||
pl = vobj.Object.Placement
|
||||
vobj.Object.Shape = Part.Shape()
|
||||
vobj.Object.Placement = pl
|
||||
|
||||
|
||||
def onDelete(self,obj,doc):
|
||||
|
||||
@@ -482,6 +650,7 @@ class ViewProviderArchReference:
|
||||
del self.timer
|
||||
return True
|
||||
|
||||
|
||||
def setEdit(self, vobj, mode):
|
||||
if mode != 0:
|
||||
return None
|
||||
@@ -490,6 +659,7 @@ class ViewProviderArchReference:
|
||||
FreeCADGui.Control.showDialog(taskd)
|
||||
return True
|
||||
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
if mode != 0:
|
||||
return None
|
||||
@@ -499,6 +669,7 @@ class ViewProviderArchReference:
|
||||
todo.delay(vobj.Proxy.recolorize,vobj)
|
||||
return True
|
||||
|
||||
|
||||
def setupContextMenu(self, vobj, menu):
|
||||
|
||||
actionEdit = QtGui.QAction(translate("Arch", "Edit"),
|
||||
@@ -524,9 +695,12 @@ class ViewProviderArchReference:
|
||||
self.onOpen)
|
||||
menu.addAction(actionOnOpen)
|
||||
|
||||
|
||||
def edit(self):
|
||||
|
||||
FreeCADGui.ActiveDocument.setEdit(self.Object, 0)
|
||||
|
||||
|
||||
def onReload(self):
|
||||
|
||||
"reloads the reference object"
|
||||
@@ -536,6 +710,7 @@ class ViewProviderArchReference:
|
||||
self.Object.touch()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
def onOpen(self):
|
||||
|
||||
"opens the reference file"
|
||||
@@ -544,24 +719,35 @@ class ViewProviderArchReference:
|
||||
if self.Object.File:
|
||||
FreeCAD.openDocument(self.Object.File)
|
||||
|
||||
def loadInventor(self,obj):
|
||||
|
||||
def loadInventor(self, obj):
|
||||
|
||||
"loads an openinventor file and replace the root node of this object"
|
||||
|
||||
filename = obj.Proxy.getFile(obj)
|
||||
if not filename:
|
||||
return None
|
||||
if filename.lower().endswith(".ifc"):
|
||||
self.setIFCNode(obj, filename)
|
||||
return
|
||||
|
||||
# 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")
|
||||
t = translate("Arch","Unable to get lightWeight node for object referenced in")
|
||||
FreeCAD.Console.PrintWarning(t+" "+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")
|
||||
t = translate("Arch","Invalid lightWeight node for object referenced in")
|
||||
FreeCAD.Console.PrintError(t+" "+obj.Label+"\n")
|
||||
return
|
||||
if lwnode.getNumChildren() < 2:
|
||||
FreeCAD.Console.PrintError("Invalid lightWeight node for object referenced in "+obj.Label+"\n")
|
||||
t = translate("Arch","Invalid lightWeight node for object referenced in")
|
||||
FreeCAD.Console.PrintError(t+" "+obj.Label+"\n")
|
||||
return
|
||||
flatlines = lwnode
|
||||
shaded = lwnode.getChild(0)
|
||||
@@ -570,11 +756,11 @@ class ViewProviderArchReference:
|
||||
# check node contents
|
||||
rootnode = obj.ViewObject.RootNode
|
||||
if rootnode.getNumChildren() < 3:
|
||||
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
|
||||
FreeCAD.Console.PrintError(translate("Arch","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")
|
||||
FreeCAD.Console.PrintError(translate("Arch","Invalid root node in")+" "+obj.Label+"\n")
|
||||
return
|
||||
|
||||
# keep a copy of the original nodes
|
||||
@@ -587,6 +773,7 @@ class ViewProviderArchReference:
|
||||
switch.replaceChild(1,shaded)
|
||||
switch.replaceChild(2,wireframe)
|
||||
|
||||
|
||||
def unloadInventor(self,obj):
|
||||
|
||||
"restore original nodes"
|
||||
@@ -601,11 +788,11 @@ class ViewProviderArchReference:
|
||||
# check node contents
|
||||
rootnode = obj.ViewObject.RootNode
|
||||
if rootnode.getNumChildren() < 3:
|
||||
FreeCAD.Console.PrintError("Invalid root node in "+obj.Label+"\n")
|
||||
FreeCAD.Console.PrintError(translate("Arch","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")
|
||||
FreeCAD.Console.PrintError(translate("Arch","Invalid root node in")+" "+obj.Label+"\n")
|
||||
return
|
||||
|
||||
# replace root node of object
|
||||
@@ -618,6 +805,7 @@ class ViewProviderArchReference:
|
||||
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"
|
||||
@@ -659,6 +847,31 @@ class ViewProviderArchReference:
|
||||
return buf
|
||||
|
||||
|
||||
def setIFCNode(self, obj, filename):
|
||||
|
||||
"""Sets the coin node of this object from an IFC file"""
|
||||
|
||||
try:
|
||||
import ifc_tools
|
||||
import ifc_generator
|
||||
except:
|
||||
t = translate("Arch","NativeIFC not available - unable to process IFC files")
|
||||
FreeCAD.Console.PrintError(t+"\n")
|
||||
return
|
||||
ifcfile = obj.Proxy.getIfcFile(filename)
|
||||
elements = obj.Proxy.getIFCElements(obj, ifcfile)
|
||||
node, placement = ifc_generator.generate_coin(ifcfile, elements, cached=True)
|
||||
if node:
|
||||
ifc_generator.set_representation(obj.ViewObject, node)
|
||||
colors = node[0]
|
||||
if colors:
|
||||
ifc_tools.set_colors(obj, colors)
|
||||
else:
|
||||
ifc_generator.set_representation(obj.ViewObject, None)
|
||||
if placement:
|
||||
obj.Placement = placement
|
||||
|
||||
|
||||
class ArchReferenceTaskPanel:
|
||||
|
||||
|
||||
@@ -669,53 +882,64 @@ class ArchReferenceTaskPanel:
|
||||
self.obj = obj
|
||||
self.filename = None
|
||||
self.form = QtGui.QWidget()
|
||||
self.form.setWindowTitle("External reference")
|
||||
self.form.setWindowTitle(translate("Arch","External reference"))
|
||||
layout = QtGui.QVBoxLayout(self.form)
|
||||
label1 = QtGui.QLabel("External file:")
|
||||
label1 = QtGui.QLabel(translate("Arch","External file")+":")
|
||||
layout.addWidget(label1)
|
||||
self.fileButton = QtGui.QPushButton(self.form)
|
||||
self.openButton = QtGui.QPushButton(self.form)
|
||||
self.openButton.setText("Open")
|
||||
self.openButton.setText(translate("Arch","Open"))
|
||||
if not self.obj.File:
|
||||
self.openButton.setEnabled(False)
|
||||
l2 = QtGui.QHBoxLayout()
|
||||
layout.addLayout(l2)
|
||||
l2.addWidget(self.fileButton)
|
||||
l2.addWidget(self.openButton)
|
||||
label2 = QtGui.QLabel("Part to use:")
|
||||
label2 = QtGui.QLabel(translate("Arch","Part to use:"))
|
||||
layout.addWidget(label2)
|
||||
if self.obj.File:
|
||||
self.fileButton.setText(os.path.basename(self.obj.File))
|
||||
else:
|
||||
self.fileButton.setText("Choose file...")
|
||||
self.fileButton.setText(translate("Arch","Choose file..."))
|
||||
self.partCombo = QtGui.QComboBox(self.form)
|
||||
self.partCombo.setEnabled(False)
|
||||
layout.addWidget(self.partCombo)
|
||||
if hasattr(self.obj.Proxy,"parts"):
|
||||
parts = self.obj.Proxy.parts
|
||||
else:
|
||||
parts = self.obj.Proxy.getPartsList(self.obj)
|
||||
sortedkeys = sorted(parts)
|
||||
for k in sortedkeys:
|
||||
self.partCombo.addItem(parts[k][0],k)
|
||||
if self.obj.Part:
|
||||
if self.obj.Part in sortedkeys:
|
||||
self.partCombo.setCurrentIndex(sortedkeys.index(self.obj.Part))
|
||||
if parts:
|
||||
self.partCombo.setEnabled(True)
|
||||
sortedkeys = sorted(parts)
|
||||
self.partCombo.addItem(translate("Arch","None (Use whole object)"),"")
|
||||
for k in sortedkeys:
|
||||
self.partCombo.addItem(parts[k][0],k)
|
||||
if self.obj.Part:
|
||||
if self.obj.Part in sortedkeys:
|
||||
self.partCombo.setCurrentIndex(sortedkeys.index(self.obj.Part))
|
||||
else:
|
||||
self.partCombo.setEnabled(False)
|
||||
QtCore.QObject.connect(self.fileButton, QtCore.SIGNAL("clicked()"), self.chooseFile)
|
||||
QtCore.QObject.connect(self.openButton, QtCore.SIGNAL("clicked()"), self.openFile)
|
||||
|
||||
def accept(self):
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
if self.filename:
|
||||
if self.filename != self.obj.File:
|
||||
self.obj.File = self.filename
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
if self.partCombo.currentText():
|
||||
i = self.partCombo.currentIndex()
|
||||
if self.partCombo.itemData(i) != self.obj.Part:
|
||||
self.obj.Part = self.partCombo.itemData(i)
|
||||
if self.obj.Label == "External Reference":
|
||||
self.obj.Label = self.partCombo.itemText(i)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
if i >= 1:
|
||||
if self.partCombo.itemData(i) != self.obj.Part:
|
||||
self.obj.Part = self.partCombo.itemData(i)
|
||||
else:
|
||||
self.obj.Part = ""
|
||||
QtCore.QTimer.singleShot(0,FreeCAD.ActiveDocument.recompute)
|
||||
if self.filename and self.obj.Label == "External Reference":
|
||||
self.obj.Label = os.path.basename(self.filename)
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
return True
|
||||
|
||||
@@ -730,24 +954,43 @@ class ArchReferenceTaskPanel:
|
||||
loc = QtCore.QDir.homePath()
|
||||
if self.obj.File:
|
||||
loc = os.path.dirname(self.obj.File)
|
||||
f = QtGui.QFileDialog.getOpenFileName(self.form,'Choose reference file',loc,"FreeCAD standard files (*.FCStd)")
|
||||
filters = "*.FCStd *.dxf"
|
||||
# enable IFC support if NativeIFC is present
|
||||
try:
|
||||
import ifc_tools
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
filters += " *.ifc"
|
||||
filters = translate("Arch","Reference files")+" ("+filters+")"
|
||||
f = QtGui.QFileDialog.getOpenFileName(self.form,
|
||||
translate("Arch","Choose reference file"),
|
||||
loc,
|
||||
filters)
|
||||
if f:
|
||||
self.filename = f[0]
|
||||
self.fileButton.setText(os.path.basename(self.filename))
|
||||
parts = self.obj.Proxy.getPartsList(self.obj,self.filename)
|
||||
self.partCombo.clear()
|
||||
if parts:
|
||||
self.partCombo.clear()
|
||||
self.partCombo.setEnabled(True)
|
||||
sortedkeys = sorted(parts)
|
||||
self.partCombo.addItem(translate("Arch","None (Use whole object)"),"")
|
||||
for k in sortedkeys:
|
||||
self.partCombo.addItem(parts[k][0],k)
|
||||
if self.obj.Part:
|
||||
if self.obj.Part in sortedkeys:
|
||||
self.partCombo.setCurrentIndex(sortedkeys.index(self.obj.Part))
|
||||
else:
|
||||
self.partCombo.setEnabled(False)
|
||||
|
||||
def openFile(self):
|
||||
|
||||
if self.obj.File:
|
||||
FreeCAD.openDocument(self.obj.File)
|
||||
if self.obj.File.lower().endswith(".fcstd"):
|
||||
FreeCAD.openDocument(self.obj.File)
|
||||
else:
|
||||
FreeCAD.loadFile(self.obj.File)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
|
||||
@@ -760,9 +1003,9 @@ class ArchReferenceCommand:
|
||||
def GetResources(self):
|
||||
|
||||
return {'Pixmap' : 'Arch_Reference',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Reference","External reference"),
|
||||
'MenuText': QT_TRANSLATE_NOOP("Arch_Reference","External reference"),
|
||||
'Accel': "E, X",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Reference","Creates an external reference object")}
|
||||
'ToolTip': QT_TRANSLATE_NOOP("Arch_Reference","Creates an external reference object")}
|
||||
|
||||
def IsActive(self):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user