BIM: Quantities support for nativeIFC objects (#18689)
* BIM: Quantities support for nativeIFC objects * BIM: Added nativeIFC support for schedules
This commit is contained in:
@@ -696,6 +696,20 @@ def makeRoof(baseobj=None,
|
||||
return obj
|
||||
|
||||
|
||||
def makeSchedule():
|
||||
"""makeSchedule(): Creates a schedule object in the active document"""
|
||||
|
||||
import ArchSchedule
|
||||
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Schedule")
|
||||
obj.Label = translate("Arch","Schedule")
|
||||
ArchSchedule._ArchSchedule(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
ArchSchedule._ViewProviderArchSchedule(obj.ViewObject)
|
||||
if hasattr(obj,"CreateSpreadsheet") and obj.CreateSpreadsheet:
|
||||
obj.Proxy.getSpreadSheet(obj, force=True)
|
||||
return obj
|
||||
|
||||
|
||||
def makeSectionPlane(objectslist=None,name=None):
|
||||
|
||||
"""makeSectionPlane([objectslist],[name]) : Creates a Section plane objects including the
|
||||
|
||||
@@ -746,7 +746,7 @@ def getHost(obj,strict=True):
|
||||
return par
|
||||
return None
|
||||
|
||||
def pruneIncluded(objectslist,strict=False):
|
||||
def pruneIncluded(objectslist,strict=False,silent=False):
|
||||
"""pruneIncluded(objectslist,[strict]): removes from a list of Arch objects, those that are subcomponents of
|
||||
another shape-based object, leaving only the top-level shapes. If strict is True, the object
|
||||
is removed only if the parent is also part of the selection."""
|
||||
@@ -793,7 +793,7 @@ def pruneIncluded(objectslist,strict=False):
|
||||
toplevel = True
|
||||
if toplevel:
|
||||
newlist.append(obj)
|
||||
else:
|
||||
elif not silent:
|
||||
FreeCAD.Console.PrintWarning("pruning "+obj.Label+"\n")
|
||||
return newlist
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ __author__ = "Yorik van Havre"
|
||||
__url__ = "https://www.freecad.org"
|
||||
|
||||
|
||||
verbose = True # change this for silent recomputes
|
||||
PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM")
|
||||
VERBOSE = True # change this for silent recomputes
|
||||
|
||||
|
||||
|
||||
@@ -94,28 +95,33 @@ class _ArchSchedule:
|
||||
self.setSchedulePropertySpreadsheet(sp, obj)
|
||||
obj.removeProperty("Result")
|
||||
from draftutils.messages import _wrn
|
||||
if "Description" in obj.PropertiesList:
|
||||
if obj.getTypeOfProperty("Description") == "App::PropertyStringList":
|
||||
obj.Operation = obj.Description
|
||||
obj.removeProperty("Description")
|
||||
_wrn("v0.21, " + sp.Label + ", " + translate("Arch", "renamed property 'Description' to 'Operation'"))
|
||||
_wrn("v0.21, " + obj.Label + ", " + translate("Arch", "removed property 'Result', and added property 'AutoUpdate'"))
|
||||
if sp is not None:
|
||||
_wrn("v0.21, " + sp.Label + ", " + translate("Arch", "added property 'Schedule'"))
|
||||
|
||||
def setProperties(self,obj):
|
||||
|
||||
if not "Description" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Description", "Arch",QT_TRANSLATE_NOOP("App::Property","The description column"))
|
||||
if not "Operation" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Operation", "Schedule",QT_TRANSLATE_NOOP("App::Property","The operation column"))
|
||||
if not "Value" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Value", "Arch",QT_TRANSLATE_NOOP("App::Property","The values column"))
|
||||
obj.addProperty("App::PropertyStringList","Value", "Schedule",QT_TRANSLATE_NOOP("App::Property","The values column"))
|
||||
if not "Unit" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Unit", "Arch",QT_TRANSLATE_NOOP("App::Property","The units column"))
|
||||
obj.addProperty("App::PropertyStringList","Unit", "Schedule",QT_TRANSLATE_NOOP("App::Property","The units column"))
|
||||
if not "Objects" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Objects", "Arch",QT_TRANSLATE_NOOP("App::Property","The objects column"))
|
||||
obj.addProperty("App::PropertyStringList","Objects", "Schedule",QT_TRANSLATE_NOOP("App::Property","The objects column"))
|
||||
if not "Filter" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyStringList","Filter", "Arch",QT_TRANSLATE_NOOP("App::Property","The filter column"))
|
||||
obj.addProperty("App::PropertyStringList","Filter", "Schedule",QT_TRANSLATE_NOOP("App::Property","The filter column"))
|
||||
if not "CreateSpreadsheet" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyBool", "CreateSpreadsheet", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, a spreadsheet containing the results is recreated when needed"))
|
||||
obj.addProperty("App::PropertyBool", "CreateSpreadsheet", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, a spreadsheet containing the results is recreated when needed"))
|
||||
if not "DetailedResults" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyBool", "DetailedResults", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results"))
|
||||
obj.addProperty("App::PropertyBool", "DetailedResults", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results"))
|
||||
if not "AutoUpdate" in obj.PropertiesList:
|
||||
obj.addProperty("App::PropertyBool", "AutoUpdate", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed"))
|
||||
obj.addProperty("App::PropertyBool", "AutoUpdate", "Schedule",QT_TRANSLATE_NOOP("App::Property","If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed"))
|
||||
obj.AutoUpdate = True
|
||||
|
||||
# To add the doc observer:
|
||||
@@ -191,7 +197,7 @@ class _ArchSchedule:
|
||||
# clearAll removes the custom property, we need to re-add it:
|
||||
self.setSchedulePropertySpreadsheet(sp, obj)
|
||||
# set headers
|
||||
sp.set("A1","Description")
|
||||
sp.set("A1","Operation")
|
||||
sp.set("B1","Value")
|
||||
sp.set("C1","Unit")
|
||||
sp.setStyle('A1:C1', 'bold', 'add')
|
||||
@@ -209,26 +215,26 @@ class _ArchSchedule:
|
||||
|
||||
# verify the data
|
||||
|
||||
if not obj.Description:
|
||||
if not obj.Operation:
|
||||
# empty description column
|
||||
return
|
||||
for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
|
||||
# different number of items in each column
|
||||
if len(obj.Description) != len(p):
|
||||
if len(obj.Operation) != len(p):
|
||||
return
|
||||
|
||||
self.data = {} # store all results in self.data, so it lives even without spreadsheet
|
||||
li = 1 # row index - starts at 2 to leave 2 blank rows for the title
|
||||
self.li = 1 # row index - starts at 2 to leave 2 blank rows for the title
|
||||
|
||||
for i in range(len(obj.Description)):
|
||||
li += 1
|
||||
if not obj.Description[i]:
|
||||
for i in range(len(obj.Operation)):
|
||||
self.li += 1
|
||||
if not obj.Operation[i]:
|
||||
# blank line
|
||||
continue
|
||||
# write description
|
||||
self.data["A"+str(li)] = obj.Description[i]
|
||||
if verbose:
|
||||
l= "OPERATION: "+obj.Description[i]
|
||||
self.data["A"+str(self.li)] = obj.Operation[i]
|
||||
if VERBOSE:
|
||||
l= "OPERATION: "+obj.Operation[i]
|
||||
print("")
|
||||
print (l)
|
||||
print (len(l)*"=")
|
||||
@@ -237,6 +243,10 @@ class _ArchSchedule:
|
||||
|
||||
objs = obj.Objects[i]
|
||||
val = obj.Value[i]
|
||||
unit = obj.Unit[i]
|
||||
details = obj.DetailedResults
|
||||
ifcfile = None
|
||||
elts = None
|
||||
if val:
|
||||
import Draft,Arch
|
||||
if objs:
|
||||
@@ -244,143 +254,316 @@ class _ArchSchedule:
|
||||
objs = [FreeCAD.ActiveDocument.getObject(o) for o in objs]
|
||||
objs = [o for o in objs if o is not None]
|
||||
else:
|
||||
if hasattr(getattr(FreeCAD.ActiveDocument, "Proxy", None), "ifcfile"):
|
||||
ifcfile = FreeCAD.ActiveDocument.Proxy.ifcfile
|
||||
objs = FreeCAD.ActiveDocument.Objects
|
||||
if len(objs) == 1:
|
||||
if hasattr(objs[0], "StepId"):
|
||||
from nativeifc import ifc_tools
|
||||
ifcfile = ifc_tools.get_ifcfile(objs[0])
|
||||
# remove object itself if the object is a group
|
||||
if objs[0].isDerivedFrom("App::DocumentObjectGroup"):
|
||||
objs = objs[0].Group
|
||||
objs = Draft.get_group_contents(objs)
|
||||
objs = Arch.pruneIncluded(objs,strict=True)
|
||||
objs = Arch.pruneIncluded(objs,strict=True,silent=True)
|
||||
# Remove all schedules and spreadsheets:
|
||||
objs = [o for o in objs if Draft.get_type(o) not in ["Schedule", "Spreadsheet::Sheet"]]
|
||||
|
||||
# filter elements
|
||||
|
||||
if obj.Filter[i]:
|
||||
# apply filters
|
||||
nobjs = []
|
||||
for o in objs:
|
||||
props = [p.upper() for p in o.PropertiesList]
|
||||
ok = True
|
||||
for f in obj.Filter[i].split(";"):
|
||||
args = [a.strip() for a in f.strip().split(":")]
|
||||
if args[0][0] == "!":
|
||||
inv = True
|
||||
prop = args[0][1:].upper()
|
||||
else:
|
||||
inv = False
|
||||
prop = args[0].upper()
|
||||
fval = args[1].upper()
|
||||
if prop == "TYPE":
|
||||
prop = "IFCTYPE"
|
||||
if inv:
|
||||
if prop in props:
|
||||
csprop = o.PropertiesList[props.index(prop)]
|
||||
if fval in getattr(o,csprop).upper():
|
||||
ok = False
|
||||
else:
|
||||
if not (prop in props):
|
||||
ok = False
|
||||
else:
|
||||
csprop = o.PropertiesList[props.index(prop)]
|
||||
if not (fval in getattr(o,csprop).upper()):
|
||||
ok = False
|
||||
if ok:
|
||||
nobjs.append(o)
|
||||
objs = nobjs
|
||||
if ifcfile:
|
||||
elts = self.get_ifc_elements(ifcfile, obj.Filter[i])
|
||||
else:
|
||||
objs = self.apply_filter(objs, obj.Filter[i])
|
||||
|
||||
# perform operation: count or retrieve property
|
||||
|
||||
if val.upper() == "COUNT":
|
||||
val = len(objs)
|
||||
if verbose:
|
||||
print (val, ",".join([o.Label for o in objs]))
|
||||
self.data["B"+str(li)] = str(val)
|
||||
if obj.DetailedResults:
|
||||
# additional blank line...
|
||||
li += 1
|
||||
self.data["A"+str(li)] = " "
|
||||
else:
|
||||
vals = val.split(".")
|
||||
if vals[0][0].islower():
|
||||
# old-style: first member is not a property
|
||||
vals = vals[1:]
|
||||
sumval = 0
|
||||
if ifcfile:
|
||||
if elts:
|
||||
self.update_from_elts(elts, val, unit, details)
|
||||
elif objs:
|
||||
self.update_from_objs(objs, val, unit, details)
|
||||
|
||||
# get unit
|
||||
tp = None
|
||||
unit = None
|
||||
q = None
|
||||
if obj.Unit[i]:
|
||||
unit = obj.Unit[i]
|
||||
unit = unit.replace("^","") # get rid of existing power symbol
|
||||
unit = unit.replace("2","^2")
|
||||
unit = unit.replace("3","^3")
|
||||
unit = unit.replace("²","^2")
|
||||
unit = unit.replace("³","^3")
|
||||
if "2" in unit:
|
||||
tp = FreeCAD.Units.Area
|
||||
elif "3" in unit:
|
||||
tp = FreeCAD.Units.Volume
|
||||
elif "deg" in unit:
|
||||
tp = FreeCAD.Units.Angle
|
||||
else:
|
||||
tp = FreeCAD.Units.Length
|
||||
|
||||
# format value
|
||||
dv = params.get_param("Decimals",path="Units")
|
||||
fs = "{:."+str(dv)+"f}" # format string
|
||||
for o in objs:
|
||||
if verbose:
|
||||
l = o.Name+" ("+o.Label+"):"
|
||||
print (l+(40-len(l))*" ",end="")
|
||||
try:
|
||||
d = o
|
||||
for v in vals:
|
||||
d = getattr(d,v)
|
||||
if hasattr(d,"Value"):
|
||||
d = d.Value
|
||||
except Exception:
|
||||
FreeCAD.Console.PrintWarning(translate("Arch","Unable to retrieve value from object")+": "+o.Name+"."+".".join(vals)+"\n")
|
||||
else:
|
||||
if verbose:
|
||||
if tp and unit:
|
||||
v = fs.format(FreeCAD.Units.Quantity(d,tp).getValueAs(unit).Value)
|
||||
print(v,unit)
|
||||
else:
|
||||
print(fs.format(d))
|
||||
if obj.DetailedResults:
|
||||
li += 1
|
||||
self.data["A"+str(li)] = o.Name+" ("+o.Label+")"
|
||||
if tp and unit:
|
||||
q = FreeCAD.Units.Quantity(d,tp)
|
||||
self.data["B"+str(li)] = str(q.getValueAs(unit).Value)
|
||||
self.data["C"+str(li)] = unit
|
||||
else:
|
||||
self.data["B"+str(li)] = str(d)
|
||||
|
||||
if not sumval:
|
||||
sumval = d
|
||||
else:
|
||||
sumval += d
|
||||
val = sumval
|
||||
if tp:
|
||||
q = FreeCAD.Units.Quantity(val,tp)
|
||||
|
||||
# write data
|
||||
if obj.DetailedResults:
|
||||
li += 1
|
||||
self.data["A"+str(li)] = "TOTAL"
|
||||
if q and unit:
|
||||
self.data["B"+str(li)] = str(q.getValueAs(unit).Value)
|
||||
self.data["C"+str(li)] = unit
|
||||
else:
|
||||
self.data["B"+str(li)] = str(val)
|
||||
if verbose:
|
||||
if tp and unit:
|
||||
v = fs.format(FreeCAD.Units.Quantity(val,tp).getValueAs(unit).Value)
|
||||
print("TOTAL:"+34*" "+v+" "+unit)
|
||||
else:
|
||||
v = fs.format(val)
|
||||
print("TOTAL:"+34*" "+v)
|
||||
self.setSpreadsheetData(obj)
|
||||
self.save_ifc_props(obj)
|
||||
|
||||
def apply_filter(self, objs, filters):
|
||||
"""Applies the given filters to the given list of objects"""
|
||||
|
||||
nobjs = []
|
||||
for o in objs:
|
||||
props = [p.upper() for p in o.PropertiesList]
|
||||
ok = True
|
||||
for f in filters.split(";"):
|
||||
args = [a.strip() for a in f.strip().split(":")]
|
||||
if args[0][0] == "!":
|
||||
inv = True
|
||||
prop = args[0][1:].upper()
|
||||
else:
|
||||
inv = False
|
||||
prop = args[0].upper()
|
||||
fval = args[1].upper()
|
||||
if prop == "TYPE":
|
||||
prop = "IFCTYPE"
|
||||
if inv:
|
||||
if prop in props:
|
||||
csprop = o.PropertiesList[props.index(prop)]
|
||||
if fval in getattr(o,csprop).upper():
|
||||
ok = False
|
||||
else:
|
||||
if not (prop in props):
|
||||
ok = False
|
||||
else:
|
||||
csprop = o.PropertiesList[props.index(prop)]
|
||||
if not (fval in getattr(o,csprop).upper()):
|
||||
ok = False
|
||||
if ok:
|
||||
nobjs.append(o)
|
||||
return nobjs
|
||||
|
||||
def get_ifc_elements(self, ifcfile, filters):
|
||||
"""Retrieves IFC elements corresponding to the given filters"""
|
||||
|
||||
elts = []
|
||||
for el in ifcfile.by_type("IfcProduct"):
|
||||
ok = True
|
||||
for f in filters.split(";"):
|
||||
args = [a.strip() for a in f.strip().split(":")]
|
||||
if args[0][0] == "!":
|
||||
inv = True
|
||||
prop = args[0][1:]
|
||||
else:
|
||||
inv = False
|
||||
prop = args[0]
|
||||
fval = args[1]
|
||||
if prop.upper() in ["CLASS", "IFCCLASS", "IFCTYPE"]:
|
||||
prop = "is_a"
|
||||
if inv:
|
||||
if prop == "is_a":
|
||||
if not fval.upper().startswith("IFC"):
|
||||
fval = "Ifc" + fval
|
||||
fval = fval.replace(" ","")
|
||||
if el.is_a(fval):
|
||||
ok = False
|
||||
else:
|
||||
if prop in dir(el):
|
||||
rval = getattr(el, prop)
|
||||
if hasattr(rval, "id"):
|
||||
if fval.startswith("#"):
|
||||
fval = int(fval[1:])
|
||||
if rval == fval:
|
||||
ok = False
|
||||
else:
|
||||
if prop == "is_a":
|
||||
if not fval.upper().startswith("IFC"):
|
||||
fval = "Ifc" + fval
|
||||
fval = fval.replace(" ","")
|
||||
if not el.is_a(fval):
|
||||
ok = False
|
||||
else:
|
||||
if prop in dir(el):
|
||||
rval = getattr(el, prop)
|
||||
if hasattr(rval, "id"):
|
||||
if fval.startswith("#"):
|
||||
fval = int(fval[1:])
|
||||
if rval != fval:
|
||||
ok = False
|
||||
else:
|
||||
ok = False
|
||||
if ok:
|
||||
elts.append(el)
|
||||
return elts
|
||||
|
||||
def update_from_objs(self, objs, val, unit, details):
|
||||
"""Updates the spreadsheet data from FreeCAD objects"""
|
||||
|
||||
if val.upper() == "COUNT":
|
||||
val = len(objs)
|
||||
if VERBOSE:
|
||||
print (val, ",".join([o.Label for o in objs]))
|
||||
self.data["B"+str(self.li)] = str(val)
|
||||
if details:
|
||||
# additional blank line...
|
||||
self.li += 1
|
||||
self.data["A"+str(self.li)] = " "
|
||||
else:
|
||||
vals = val.split(".")
|
||||
if vals[0][0].islower():
|
||||
# old-style: first member is not a property
|
||||
vals = vals[1:]
|
||||
sumval = 0
|
||||
|
||||
# get unit
|
||||
tp = None
|
||||
unit = None
|
||||
q = None
|
||||
if unit:
|
||||
unit = unit.replace("^","") # get rid of existing power symbol
|
||||
unit = unit.replace("2","^2")
|
||||
unit = unit.replace("3","^3")
|
||||
unit = unit.replace("²","^2")
|
||||
unit = unit.replace("³","^3")
|
||||
if "2" in unit:
|
||||
tp = FreeCAD.Units.Area
|
||||
elif "3" in unit:
|
||||
tp = FreeCAD.Units.Volume
|
||||
elif "deg" in unit:
|
||||
tp = FreeCAD.Units.Angle
|
||||
else:
|
||||
tp = FreeCAD.Units.Length
|
||||
|
||||
# format value
|
||||
dv = params.get_param("Decimals",path="Units")
|
||||
fs = "{:."+str(dv)+"f}" # format string
|
||||
for o in objs:
|
||||
if VERBOSE:
|
||||
l = o.Name+" ("+o.Label+"):"
|
||||
print (l+(40-len(l))*" ",end="")
|
||||
try:
|
||||
d = o
|
||||
for v in vals:
|
||||
d = getattr(d,v)
|
||||
if hasattr(d,"Value"):
|
||||
d = d.Value
|
||||
except Exception:
|
||||
t = translate("Arch","Unable to retrieve value from object")
|
||||
FreeCAD.Console.PrintWarning(t+": "+o.Name+"."+".".join(vals)+"\n")
|
||||
else:
|
||||
if VERBOSE:
|
||||
if tp and unit:
|
||||
v = fs.format(FreeCAD.Units.Quantity(d,tp).getValueAs(unit).Value)
|
||||
print(v,unit)
|
||||
elif isinstance(d, str):
|
||||
if d.replace('.', '', 1).isdigit():
|
||||
print(fs.format(d))
|
||||
else:
|
||||
print(d)
|
||||
else:
|
||||
print(fs.format(d))
|
||||
if details:
|
||||
self.li += 1
|
||||
self.data["A"+str(self.li)] = o.Name+" ("+o.Label+")"
|
||||
if tp and unit:
|
||||
q = FreeCAD.Units.Quantity(d,tp)
|
||||
self.data["B"+str(self.li)] = str(q.getValueAs(unit).Value)
|
||||
self.data["C"+str(self.li)] = unit
|
||||
else:
|
||||
self.data["B"+str(self.li)] = str(d)
|
||||
|
||||
if sumval:
|
||||
sumval += d
|
||||
else:
|
||||
sumval = d
|
||||
val = sumval
|
||||
if tp:
|
||||
q = FreeCAD.Units.Quantity(val,tp)
|
||||
|
||||
# write data
|
||||
if details:
|
||||
self.li += 1
|
||||
self.data["A"+str(self.li)] = "TOTAL"
|
||||
if q and unit:
|
||||
self.data["B"+str(self.li)] = str(q.getValueAs(unit).Value)
|
||||
self.data["C"+str(self.li)] = unit
|
||||
else:
|
||||
self.data["B"+str(self.li)] = str(val)
|
||||
if VERBOSE:
|
||||
if tp and unit:
|
||||
v = fs.format(FreeCAD.Units.Quantity(val,tp).getValueAs(unit).Value)
|
||||
print("TOTAL:"+34*" "+v+" "+unit)
|
||||
elif isinstance(val, str):
|
||||
if val.replace('.', '', 1).isdigit():
|
||||
v = fs.format(val)
|
||||
print("TOTAL:"+34*" "+v)
|
||||
else:
|
||||
print("TOTAL:"+34*" "+val)
|
||||
else:
|
||||
v = fs.format(val)
|
||||
print("TOTAL:"+34*" "+v)
|
||||
|
||||
def update_from_elts(self, elts, val, unit, details):
|
||||
"""Updates the spreadsheet data from IFC elements"""
|
||||
|
||||
if val.upper() == "COUNT":
|
||||
val = len(elts)
|
||||
if VERBOSE:
|
||||
print ("COUNT:", val, "(", ",".join(["#"+str(e.id()) for e in elts]), ")")
|
||||
self.data["B"+str(self.li)] = str(val)
|
||||
if details:
|
||||
# additional blank line...
|
||||
self.li += 1
|
||||
self.data["A"+str(self.li)] = " "
|
||||
else:
|
||||
total = 0
|
||||
for el in elts:
|
||||
if val in dir(el):
|
||||
elval = getattr(el, val, "")
|
||||
if isinstance(elval, tuple):
|
||||
if len(elval) == 1:
|
||||
elval = elval[0]
|
||||
elif len(elval) == 0:
|
||||
elval = ""
|
||||
if hasattr(elval, "is_a") and elval.is_a("IfcRelationship"):
|
||||
for att in dir(elval):
|
||||
if att.startswith("Relating"):
|
||||
targ = getattr(elval, att)
|
||||
if targ != el:
|
||||
elval = targ
|
||||
break
|
||||
elif att.startswith("Related"):
|
||||
if not elval in getattr(elval, att):
|
||||
elval = str(getattr(elval, att))
|
||||
break
|
||||
if details:
|
||||
self.li += 1
|
||||
name = el.Name if el.Name else ""
|
||||
self.data["A"+str(self.li)] = "#" + str(el.id()) + name
|
||||
self.data["B"+str(self.li)] = str(elval)
|
||||
if VERBOSE:
|
||||
print("#"+str(el.id())+"."+val+" = "+str(elval))
|
||||
if isinstance(elval, str) and elval.replace('.', '', 1).isdigit():
|
||||
total += float(elval)
|
||||
elif isinstance(elval, (int, float)):
|
||||
total += elval
|
||||
if total:
|
||||
if details:
|
||||
self.li += 1
|
||||
self.data["A"+str(self.li)] = "TOTAL"
|
||||
self.data["B"+str(self.li)] = str(total)
|
||||
if VERBOSE:
|
||||
print("TOTAL:",str(total))
|
||||
|
||||
def create_ifc(self, obj, ifcfile, export=False):
|
||||
"""Creates an IFC element for this object"""
|
||||
|
||||
from nativeifc import ifc_tools # lazy loading
|
||||
|
||||
proj = ifcfile.by_type("IfcProject")[0]
|
||||
elt = ifc_tools.api_run("root.create_entity", ifcfile, ifc_class="IfcControl")
|
||||
ifc_tools.set_attribute(ifcfile, elt, "Name", obj.Label)
|
||||
ifc_tools.api_run("project.assign_declaration", ifcfile, definitions=[elt], relating_context=proj)
|
||||
if not export:
|
||||
ifc_tools.add_properties(obj, ifcfile, elt)
|
||||
return elt
|
||||
|
||||
def save_ifc_props(self, obj, ifcfile=None, elt=None):
|
||||
"""Saves the object data to IFC"""
|
||||
|
||||
from nativeifc import ifc_psets # lazy loading
|
||||
|
||||
ifc_psets.edit_pset(obj, "Operation", "::".join(obj.Operation), ifcfile=ifcfile, element=elt)
|
||||
ifc_psets.edit_pset(obj, "Value", "::".join(obj.Value), ifcfile=ifcfile, element=elt)
|
||||
ifc_psets.edit_pset(obj, "Unit", "::".join(obj.Unit), ifcfile=ifcfile, element=elt)
|
||||
ifc_psets.edit_pset(obj, "Objects", "::".join(obj.Objects), ifcfile=ifcfile, element=elt)
|
||||
ifc_psets.edit_pset(obj, "Filter", "::".join(obj.Filter), ifcfile=ifcfile, element=elt)
|
||||
|
||||
def export_ifc(self, obj, ifcfile):
|
||||
"""Exports the object to IFC (does not modify the FreeCAD object)."""
|
||||
|
||||
elt = self.create_ifc(obj, ifcfile, export=True)
|
||||
self.save_ifc_props(obj, ifcfile, elt)
|
||||
return elt
|
||||
|
||||
def dumps(self):
|
||||
|
||||
@@ -417,8 +600,16 @@ class _ViewProviderArchSchedule:
|
||||
return None
|
||||
|
||||
self.taskd = ArchScheduleTaskPanel(vobj.Object)
|
||||
if not self.taskd.form.isVisible():
|
||||
from PySide import QtCore
|
||||
QtCore.QTimer.singleShot(100, self.showEditor)
|
||||
return True
|
||||
|
||||
def showEditor(self):
|
||||
|
||||
if hasattr(self, "taskd"):
|
||||
self.taskd.form.show()
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
if mode != 0:
|
||||
return None
|
||||
@@ -507,30 +698,37 @@ class ArchScheduleTaskPanel:
|
||||
h = params.get_param_arch("ScheduleDialogHeight")
|
||||
self.form.resize(w,h)
|
||||
|
||||
# restore default states
|
||||
self.form.checkAutoUpdate.setChecked(PARAMS.GetBool("ScheduleAutoUpdate", False))
|
||||
|
||||
# set delegate - Not using custom delegates for now...
|
||||
#self.form.list.setItemDelegate(ScheduleDelegate())
|
||||
#self.form.list.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked)
|
||||
|
||||
# connect slots
|
||||
QtCore.QObject.connect(self.form.buttonAdd, QtCore.SIGNAL("clicked()"), self.add)
|
||||
QtCore.QObject.connect(self.form.buttonDel, QtCore.SIGNAL("clicked()"), self.remove)
|
||||
QtCore.QObject.connect(self.form.buttonClear, QtCore.SIGNAL("clicked()"), self.clear)
|
||||
QtCore.QObject.connect(self.form.buttonImport, QtCore.SIGNAL("clicked()"), self.importCSV)
|
||||
QtCore.QObject.connect(self.form.buttonExport, QtCore.SIGNAL("clicked()"), self.export)
|
||||
QtCore.QObject.connect(self.form.buttonSelect, QtCore.SIGNAL("clicked()"), self.select)
|
||||
QtCore.QObject.connect(self.form.buttonBox, QtCore.SIGNAL("accepted()"), self.accept)
|
||||
QtCore.QObject.connect(self.form.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
|
||||
QtCore.QObject.connect(self.form, QtCore.SIGNAL("rejected()"), self.reject)
|
||||
self.form.buttonAdd.clicked.connect(self.add)
|
||||
self.form.buttonDel.clicked.connect(self.remove)
|
||||
self.form.buttonClear.clicked.connect(self.clear)
|
||||
self.form.buttonImport.clicked.connect(self.importCSV)
|
||||
self.form.buttonExport.clicked.connect(self.export)
|
||||
self.form.buttonSelect.clicked.connect(self.select)
|
||||
self.form.buttonBox.accepted.connect(self.accept)
|
||||
self.form.buttonBox.rejected.connect(self.reject)
|
||||
self.form.rejected.connect(self.reject)
|
||||
self.form.list.clearContents()
|
||||
|
||||
if self.obj:
|
||||
for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
|
||||
if len(obj.Description) != len(p):
|
||||
return
|
||||
self.form.list.setRowCount(len(obj.Description))
|
||||
#for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
|
||||
# if len(obj.Operation) != len(p):
|
||||
# return
|
||||
self.form.list.setRowCount(len(obj.Operation))
|
||||
for i in range(5):
|
||||
for j in range(len(obj.Description)):
|
||||
item = QtGui.QTableWidgetItem([obj.Description,obj.Value,obj.Unit,obj.Objects,obj.Filter][i][j])
|
||||
for j in range(len(obj.Operation)):
|
||||
try:
|
||||
text = [obj.Operation,obj.Value,obj.Unit,obj.Objects,obj.Filter][i][j]
|
||||
except:
|
||||
text = ""
|
||||
item = QtGui.QTableWidgetItem(text)
|
||||
self.form.list.setItem(j,i,item)
|
||||
self.form.lineEditName.setText(self.obj.Label)
|
||||
self.form.checkSpreadsheet.setChecked(self.obj.CreateSpreadsheet)
|
||||
@@ -646,7 +844,7 @@ class ArchScheduleTaskPanel:
|
||||
import csv
|
||||
with open(filename, 'w') as csvfile:
|
||||
csvfile = csv.writer(csvfile,delimiter=delimiter)
|
||||
csvfile.writerow([translate("Arch","Description"),translate("Arch","Value"),translate("Arch","Unit")])
|
||||
csvfile.writerow([translate("Arch","Operation"),translate("Arch","Value"),translate("Arch","Unit")])
|
||||
if self.obj.DetailedResults:
|
||||
csvfile.writerow(["","",""])
|
||||
for i in self.getRows():
|
||||
@@ -664,7 +862,7 @@ class ArchScheduleTaskPanel:
|
||||
"""Exports the results as a Markdown file"""
|
||||
|
||||
with open(filename, 'w') as mdfile:
|
||||
mdfile.write("| "+translate("Arch","Description")+" | "+translate("Arch","Value")+" | "+translate("Arch","Unit")+" |\n")
|
||||
mdfile.write("| "+translate("Arch","Operation")+" | "+translate("Arch","Value")+" | "+translate("Arch","Unit")+" |\n")
|
||||
mdfile.write("| --- | --- | --- |\n")
|
||||
if self.obj.DetailedResults:
|
||||
mdfile.write("| | | |\n")
|
||||
@@ -704,6 +902,9 @@ class ArchScheduleTaskPanel:
|
||||
params.set_param_arch("ScheduleDialogWidth",self.form.width())
|
||||
params.set_param_arch("ScheduleDialogHeight",self.form.height())
|
||||
|
||||
# store default states
|
||||
PARAMS.SetBool("ScheduleAutoUpdate", self.form.checkAutoUpdate.isChecked())
|
||||
|
||||
# commit values
|
||||
self.writeValues()
|
||||
self.form.hide()
|
||||
@@ -723,13 +924,8 @@ class ArchScheduleTaskPanel:
|
||||
"""commits values and recalculate"""
|
||||
|
||||
if not self.obj:
|
||||
self.obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Schedule")
|
||||
self.obj.Label = translate("Arch","Schedule")
|
||||
_ArchSchedule(self.obj)
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderArchSchedule(self.obj.ViewObject)
|
||||
if hasattr(self.obj,"CreateSpreadsheet") and self.obj.CreateSpreadsheet:
|
||||
self.obj.Proxy.getSpreadSheet(self.obj, force=True)
|
||||
import Arch
|
||||
self.obj = Arch.makeSchedule()
|
||||
lists = [ [], [], [], [], [] ]
|
||||
for i in range(self.form.list.rowCount()):
|
||||
for j in range(5):
|
||||
@@ -739,7 +935,7 @@ class ArchScheduleTaskPanel:
|
||||
else:
|
||||
lists[j].append("")
|
||||
FreeCAD.ActiveDocument.openTransaction("Edited Schedule")
|
||||
self.obj.Description = lists[0]
|
||||
self.obj.Operation = lists[0]
|
||||
self.obj.Value = lists[1]
|
||||
self.obj.Unit = lists[2]
|
||||
self.obj.Objects = lists[3]
|
||||
|
||||
@@ -78,6 +78,7 @@ SET(Dice3DS_SRCS
|
||||
SET(Arch_presets
|
||||
Presets/profiles.csv
|
||||
Presets/pset_definitions.csv
|
||||
Presets/qto_definitions.csv
|
||||
Presets/ifc_products_IFC2X3.json
|
||||
Presets/ifc_products_IFC4.json
|
||||
Presets/ifc_types_IFC2X3.json
|
||||
|
||||
File diff suppressed because one or more lines are too long
115
src/Mod/BIM/Presets/qto_definitions.csv
Normal file
115
src/Mod/BIM/Presets/qto_definitions.csv
Normal file
@@ -0,0 +1,115 @@
|
||||
Qto_ActuatorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_AirTerminalBaseQuantities;GrossWeight;IfcQuantityWeight;Perimeter;IfcQuantityLength;TotalSurfaceArea;IfcQuantityArea
|
||||
Qto_AirTerminalBoxTypeBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_AirToAirHeatRecoveryBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_AlarmBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ArealStratumBaseQuantities;Area;IfcQuantityArea;Length;IfcQuantityLength;PlanLength;IfcQuantityLength
|
||||
Qto_AudioVisualApplianceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_BeamBaseQuantities;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossSurfaceArea;IfcQuantityArea;NetSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_BodyGeometryValidation;GrossSurfaceArea;IfcQuantityArea;NetSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;SurfaceGenusBeforeFeatures;IfcQuantityCount;SurfaceGenusAfterFeatures;IfcQuantityCount
|
||||
Qto_BoilerBaseQuantities;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight;TotalSurfaceArea;IfcQuantityArea
|
||||
Qto_BuildingBaseQuantities;Height;IfcQuantityLength;EavesHeight;IfcQuantityLength;FootPrintArea;IfcQuantityArea;GrossFloorArea;IfcQuantityArea;NetFloorArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_BuildingElementProxyQuantities;NetSurfaceArea;IfcQuantityArea;NetVolume;IfcQuantityVolume
|
||||
Qto_BuildingStoreyBaseQuantities;GrossHeight;IfcQuantityLength;NetHeight;IfcQuantityLength;GrossPerimeter;IfcQuantityLength;GrossFloorArea;IfcQuantityArea;NetFloorArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_BurnerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CableCarrierFittingBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CableCarrierSegmentBaseQuantities;GrossWeight;IfcQuantityWeight;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea
|
||||
Qto_CableFittingBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CableSegmentBaseQuantities;GrossWeight;IfcQuantityWeight;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea
|
||||
Qto_ChillerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ChimneyBaseQuantities;Length;IfcQuantityLength
|
||||
Qto_CoilBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ColumnBaseQuantities;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossSurfaceArea;IfcQuantityArea;NetSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_CommunicationsApplianceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CompressorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CondenserBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ConduitSegmentBaseQuantities;InnerDiameter;IfcQuantityLength;OuterDiameter;IfcQuantityLength
|
||||
Qto_ConstructionEquipmentResourceBaseQuantities;UsageTime;IfcQuantityTime;OperatingTime;IfcQuantityTime
|
||||
Qto_ConstructionMaterialResourceBaseQuantities;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_ControllerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CooledBeamBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CoolingTowerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_CourseBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Thickness;IfcQuantityLength;Volume;IfcQuantityVolume;GrossVolume;IfcQuantityVolume;Weight;IfcQuantityWeight
|
||||
Qto_CoveringBaseQuantities;Width;IfcQuantityLength;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea
|
||||
Qto_CurtainWallQuantities;Length;IfcQuantityLength;Height;IfcQuantityLength;Width;IfcQuantityLength;GrossSideArea;IfcQuantityArea;NetSideArea;IfcQuantityArea
|
||||
Qto_DamperBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_DistributionBoardBaseQuantities;GrossWeight;IfcQuantityWeight;NumberOfCircuits;IfcQuantityCount
|
||||
Qto_DistributionChamberElementBaseQuantities;GrossSurfaceArea;IfcQuantityArea;NetSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;Depth;IfcQuantityLength
|
||||
Qto_DoorBaseQuantities;Width;IfcQuantityLength;Height;IfcQuantityLength;Perimeter;IfcQuantityLength;Area;IfcQuantityArea
|
||||
Qto_DuctFittingBaseQuantities;Length;IfcQuantityLength;GrossCrossSectionArea;IfcQuantityArea;NetCrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossWeight;IfcQuantityWeight
|
||||
Qto_DuctSegmentBaseQuantities;Length;IfcQuantityLength;GrossCrossSectionArea;IfcQuantityArea;NetCrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossWeight;IfcQuantityWeight
|
||||
Qto_DuctSilencerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_EarthworksCutBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Depth;IfcQuantityLength;UndisturbedVolume;IfcQuantityVolume;LooseVolume;IfcQuantityVolume;Weight;IfcQuantityWeight
|
||||
Qto_EarthworksFillBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Depth;IfcQuantityLength;CompactedVolume;IfcQuantityVolume;LooseVolume;IfcQuantityVolume
|
||||
Qto_ElectricApplianceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ElectricFlowStorageDeviceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ElectricGeneratorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ElectricMotorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ElectricTimeControlBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_EvaporativeCoolerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_EvaporatorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FacilityPartBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength;Area;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_FanBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FilterBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FireSuppressionTerminalBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FlowInstrumentBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FlowMeterBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_FootingBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_HeatExchangerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_HumidifierBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ImpactProtectionDeviceBaseQuantities;Weight;IfcQuantityWeight
|
||||
Qto_InterceptorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_JunctionBoxBaseQuantities;GrossWeight;IfcQuantityWeight;NumberOfGangs;IfcQuantityCount;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength
|
||||
Qto_KerbBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength;Depth;IfcQuantityLength;Volume;IfcQuantityVolume;Weight;IfcQuantityWeight
|
||||
Qto_LaborResourceBaseQuantities;StandardWork;IfcQuantityTime;OvertimeWork;IfcQuantityTime
|
||||
Qto_LampBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_LightFixtureBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_LinearStratumBaseQuantities;Diameter;IfcQuantityLength;Length;IfcQuantityLength
|
||||
Qto_MarineFacilityBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength;Area;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_MemberBaseQuantities;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossSurfaceArea;IfcQuantityArea;NetSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_MotorConnectionBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_OpeningElementBaseQuantities;Width;IfcQuantityLength;Height;IfcQuantityLength;Depth;IfcQuantityLength;Area;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_OutletBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_PavementBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Depth;IfcQuantityLength;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_PictorialSignQuantities;Area;IfcQuantityArea;SignArea;IfcQuantityArea
|
||||
Qto_PileBaseQuantities;Length;IfcQuantityLength;CrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossSurfaceArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_PipeFittingBaseQuantities;Length;IfcQuantityLength;GrossCrossSectionArea;IfcQuantityArea;NetCrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_PipeSegmentBaseQuantities;Length;IfcQuantityLength;GrossCrossSectionArea;IfcQuantityArea;NetCrossSectionArea;IfcQuantityArea;OuterSurfaceArea;IfcQuantityArea;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight;FootPrintArea;IfcQuantityArea
|
||||
Qto_PlateBaseQuantities;Width;IfcQuantityLength;Perimeter;IfcQuantityLength;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_ProjectionElementBaseQuantities;Area;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_ProtectiveDeviceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ProtectiveDeviceTrippingUnitBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_PumpBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_RailBaseQuantities;Length;IfcQuantityLength;Volume;IfcQuantityVolume;Weight;IfcQuantityWeight
|
||||
Qto_RailingBaseQuantities;Length;IfcQuantityLength
|
||||
Qto_RampFlightBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_ReinforcedSoilBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Depth;IfcQuantityLength;Area;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_ReinforcingElementBaseQuantities;Count;IfcQuantityCount;Length;IfcQuantityLength;Weight;IfcQuantityWeight
|
||||
Qto_RoofBaseQuantities;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea;ProjectedArea;IfcQuantityArea
|
||||
Qto_SanitaryTerminalBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_SensorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_SignBaseQuantities;Height;IfcQuantityLength;Width;IfcQuantityLength;Thickness;IfcQuantityLength;Weight;IfcQuantityWeight
|
||||
Qto_SignalBaseQuantities;Weight;IfcQuantityWeight
|
||||
Qto_SiteBaseQuantities;GrossPerimeter;IfcQuantityLength;GrossArea;IfcQuantityArea
|
||||
Qto_SlabBaseQuantities;Width;IfcQuantityLength;Length;IfcQuantityLength;Depth;IfcQuantityLength;Perimeter;IfcQuantityLength;GrossArea;IfcQuantityArea;NetArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_SleeperBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength
|
||||
Qto_SolarDeviceBaseQuantities;GrossWeight;IfcQuantityWeight;GrossArea;IfcQuantityArea
|
||||
Qto_SpaceBaseQuantities;Height;IfcQuantityLength;FinishCeilingHeight;IfcQuantityLength;FinishFloorHeight;IfcQuantityLength;GrossPerimeter;IfcQuantityLength;NetPerimeter;IfcQuantityLength;GrossFloorArea;IfcQuantityArea;NetFloorArea;IfcQuantityArea;GrossWallArea;IfcQuantityArea;NetWallArea;IfcQuantityArea;GrossCeilingArea;IfcQuantityArea;NetCeilingArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_SpaceHeaterBaseQuantities;Length;IfcQuantityLength;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_SpatialZoneBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength
|
||||
Qto_StackTerminalBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_StairFlightBaseQuantities;Length;IfcQuantityLength;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume
|
||||
Qto_SurfaceFeatureBaseQuantities;Area;IfcQuantityArea;Length;IfcQuantityLength
|
||||
Qto_SwitchingDeviceBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_TankBaseQuantities;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight;TotalSurfaceArea;IfcQuantityArea
|
||||
Qto_TransformerBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_TubeBundleBaseQuantities;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_UnitaryControlElementBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_UnitaryEquipmentBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_ValveBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_VehicleBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength
|
||||
Qto_VibrationIsolatorBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_VolumetricStratumBaseQuantities;Area;IfcQuantityArea;Mass;IfcQuantityWeight;PlanArea;IfcQuantityArea;Volume;IfcQuantityVolume
|
||||
Qto_WallBaseQuantities;Length;IfcQuantityLength;Width;IfcQuantityLength;Height;IfcQuantityLength;GrossFootPrintArea;IfcQuantityArea;NetFootPrintArea;IfcQuantityArea;GrossSideArea;IfcQuantityArea;NetSideArea;IfcQuantityArea;GrossVolume;IfcQuantityVolume;NetVolume;IfcQuantityVolume;GrossWeight;IfcQuantityWeight;NetWeight;IfcQuantityWeight
|
||||
Qto_WasteTerminalBaseQuantities;GrossWeight;IfcQuantityWeight
|
||||
Qto_WindowBaseQuantities;Width;IfcQuantityLength;Height;IfcQuantityLength;Perimeter;IfcQuantityLength;Area;IfcQuantityArea
|
||||
|
File diff suppressed because one or more lines are too long
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>852</width>
|
||||
<height>471</height>
|
||||
<width>680</width>
|
||||
<height>512</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -40,6 +40,16 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboQto"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonApply">
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -53,11 +63,26 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonRefresh">
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="reload">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSelectAll">
|
||||
<property name="text">
|
||||
<string>Select all</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="edit-select-all">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -25,13 +25,15 @@
|
||||
"""This module contains FreeCAD commands for the BIM workbench"""
|
||||
|
||||
import os
|
||||
import csv
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
|
||||
translate = FreeCAD.Qt.translate
|
||||
PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM")
|
||||
|
||||
qprops = [
|
||||
QPROPS = [
|
||||
"Length",
|
||||
"Width",
|
||||
"Height",
|
||||
@@ -39,8 +41,8 @@ qprops = [
|
||||
"HorizontalArea",
|
||||
"VerticalArea",
|
||||
"Volume",
|
||||
] # quantities columns
|
||||
trqprops = [
|
||||
]
|
||||
TR_QPROPS = [
|
||||
translate("BIM", "Length"),
|
||||
translate("BIM", "Width"),
|
||||
translate("BIM", "Height"),
|
||||
@@ -49,6 +51,15 @@ trqprops = [
|
||||
translate("BIM", "Vertical Area"),
|
||||
translate("BIM", "Volume"),
|
||||
]
|
||||
QTO_TYPES = {
|
||||
"IfcQuantityArea": "App::PropertyArea",
|
||||
"IfcQuantityCount": "App::PropertyInteger",
|
||||
"IfcQuantityLength": "App::PropertyLength",
|
||||
"IfcQuantityNumber": "App::PropertyInteger",
|
||||
"IfcQuantityTime": "App::PropertyTime",
|
||||
"IfcQuantityVolume": "App::PropertyVolume",
|
||||
"IfcQuantityWeight": "App::PropertyWeight",
|
||||
}
|
||||
|
||||
|
||||
class BIM_IfcQuantities:
|
||||
@@ -74,6 +85,7 @@ class BIM_IfcQuantities:
|
||||
|
||||
# build objects list
|
||||
self.objectslist = {}
|
||||
self.ifcqtolist = {}
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
role = self.getRole(obj)
|
||||
if role:
|
||||
@@ -95,17 +107,22 @@ class BIM_IfcQuantities:
|
||||
# load the form and set the tree model up
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogIfcQuantities.ui")
|
||||
self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_IfcQuantities.svg"))
|
||||
w = PARAMS.GetInt("BimIfcQuantitiesDialogWidth", 680)
|
||||
h = PARAMS.GetInt("BimIfcQuantitiesDialogHeight", 512)
|
||||
self.form.resize(w, h)
|
||||
self.get_qtos()
|
||||
|
||||
# quantities tab
|
||||
self.qmodel = QtGui.QStandardItemModel()
|
||||
self.form.quantities.setModel(self.qmodel)
|
||||
self.form.quantities.setUniformRowHeights(True)
|
||||
self.form.quantities.setItemDelegate(QtGui.QStyledItemDelegate())
|
||||
self.quantitiesDrawn = False
|
||||
self.qmodel.dataChanged.connect(self.setChecked)
|
||||
self.form.buttonBox.accepted.connect(self.accept)
|
||||
self.form.quantities.clicked.connect(self.onClickTree)
|
||||
self.form.onlyVisible.stateChanged.connect(self.update)
|
||||
self.form.buttonRefresh.clicked.connect(self.update)
|
||||
self.form.buttonApply.clicked.connect(self.add_qto)
|
||||
|
||||
# center the dialog over FreeCAD window
|
||||
mw = FreeCADGui.getMainWindow()
|
||||
@@ -132,137 +149,256 @@ class BIM_IfcQuantities:
|
||||
def decamelize(self, s):
|
||||
return "".join([" " + c if c.isupper() else c for c in s]).strip(" ")
|
||||
|
||||
def get_qtos(self):
|
||||
"populates the qtos combo box"
|
||||
|
||||
def read_csv(csvfile):
|
||||
result = {}
|
||||
if os.path.exists(csvfile):
|
||||
with open(csvfile, "r") as f:
|
||||
reader = csv.reader(f, delimiter=";")
|
||||
for row in reader:
|
||||
result[row[0]] = row[1:]
|
||||
return result
|
||||
|
||||
self.qtodefs = {}
|
||||
qtopath = os.path.join(
|
||||
FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "qto_definitions.csv"
|
||||
)
|
||||
custompath = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "CustomQtos.csv")
|
||||
self.qtodefs = read_csv(qtopath)
|
||||
self.qtodefs.update(read_csv(custompath))
|
||||
self.qtokeys = [
|
||||
"".join(map(lambda x: x if x.islower() else " " + x, t[4:]))[1:]
|
||||
for t in self.qtodefs.keys()
|
||||
]
|
||||
self.qtokeys.sort()
|
||||
self.form.comboQto.addItems(
|
||||
[translate("BIM", "Add quantity set..."),]
|
||||
+ self.qtokeys
|
||||
)
|
||||
|
||||
def add_qto(self):
|
||||
"Adds a standard qto set to the todo list"
|
||||
|
||||
index = self.form.comboQto.currentIndex()
|
||||
if index <= 0:
|
||||
return
|
||||
if len(FreeCADGui.Selection.getSelection()) != 1:
|
||||
return
|
||||
obj = FreeCADGui.Selection.getSelection()[0]
|
||||
qto = list(self.qtodefs.keys())[index-1]
|
||||
self.ifcqtolist.setdefault(obj.Name, []).append(qto)
|
||||
self.update_line(obj.Name, qto)
|
||||
FreeCAD.Console.PrintMessage(translate("BIM", "Adding quantity set")+": "+qto+"\n")
|
||||
|
||||
def apply_qto(self, obj, qto):
|
||||
"Adds a standard qto set to the object"
|
||||
|
||||
val = self.qtodefs[qto]
|
||||
qset = None
|
||||
if hasattr(obj, "StepId"):
|
||||
from nativeifc import ifc_tools
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not ifcfile or not element:
|
||||
return
|
||||
qset = ifc_tools.api_run("pset.add_qto", ifcfile, product=element, name=qto)
|
||||
for i in range(0, len(val), 2):
|
||||
qname = val[i]
|
||||
qtype = QTO_TYPES[val[i+1]]
|
||||
if not qname in obj.PropertiesList:
|
||||
obj.addProperty(qtype, qname, "Quantities", val[i+1])
|
||||
qval = 0
|
||||
i = self.get_row(obj.Name)
|
||||
if i > -1:
|
||||
for j, p in enumerate(QPROPS):
|
||||
it = self.qmodel.item(i, j+1)
|
||||
t = it.text()
|
||||
if t:
|
||||
t = t.replace("²","^2").replace("³","^3")
|
||||
qval = FreeCAD.Units.Quantity(t).Value
|
||||
if qval:
|
||||
setattr(obj, qname, qval)
|
||||
if hasattr(obj, "StepId") and qset:
|
||||
ifc_tools.api_run("pset.edit_qto", ifcfile, qto=qset, properties={qname: qval})
|
||||
|
||||
def update(self, index=None):
|
||||
"updates the tree widgets in all tabs"
|
||||
"""updates the tree widgets in all tabs. Index is not used,
|
||||
it is just there to match a qt slot requirement"""
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import Draft
|
||||
|
||||
# quantities tab - only fill once
|
||||
# quantities tab
|
||||
|
||||
if not self.quantitiesDrawn:
|
||||
self.qmodel.setHorizontalHeaderLabels(
|
||||
[translate("BIM", "Label")] + trqprops
|
||||
)
|
||||
quantheaders = self.form.quantities.header() # QHeaderView instance
|
||||
if hasattr(quantheaders, "setClickable"): # qt4
|
||||
quantheaders.setClickable(True)
|
||||
else: # qt5
|
||||
quantheaders.setSectionsClickable(True)
|
||||
quantheaders.sectionClicked.connect(self.quantHeaderClicked)
|
||||
self.qmodel.clear()
|
||||
self.qmodel.setHorizontalHeaderLabels(
|
||||
[translate("BIM", "Label")] + TR_QPROPS
|
||||
)
|
||||
self.form.quantities.setColumnWidth(0, 200) # TODO remember width
|
||||
quantheaders = self.form.quantities.header() # QHeaderView instance
|
||||
quantheaders.setSectionsClickable(True)
|
||||
quantheaders.sectionClicked.connect(self.quantHeaderClicked)
|
||||
|
||||
# sort by type
|
||||
# sort by type
|
||||
|
||||
groups = {}
|
||||
for name, role in self.objectslist.items():
|
||||
groups.setdefault(role, []).append(name)
|
||||
for names in groups.values():
|
||||
suffix = ""
|
||||
for name in names:
|
||||
if "+array" in name:
|
||||
name = name.split("+array")[0]
|
||||
suffix = " (duplicate)"
|
||||
obj = FreeCAD.ActiveDocument.getObject(name)
|
||||
if obj:
|
||||
if (
|
||||
not self.form.onlyVisible.isChecked()
|
||||
) or obj.ViewObject.isVisible():
|
||||
if obj.isDerivedFrom("Part::Feature") and not (
|
||||
Draft.getType(obj) == "Site"
|
||||
):
|
||||
it1 = QtGui.QStandardItem(obj.Label + suffix)
|
||||
it1.setToolTip(name + suffix)
|
||||
it1.setEditable(False)
|
||||
if QtCore.QFileInfo(
|
||||
":/icons/Arch_" + obj.Proxy.Type + "_Tree.svg"
|
||||
).exists():
|
||||
icon = QtGui.QIcon(
|
||||
":/icons/Arch_" + obj.Proxy.Type + "_Tree.svg"
|
||||
)
|
||||
else:
|
||||
icon = QtGui.QIcon(":/icons/Arch_Component.svg")
|
||||
it1.setIcon(icon)
|
||||
props = []
|
||||
for prop in qprops:
|
||||
it = QtGui.QStandardItem()
|
||||
val = None
|
||||
if prop == "Volume":
|
||||
if obj.Shape and hasattr(obj.Shape, "Volume"):
|
||||
val = FreeCAD.Units.Quantity(
|
||||
obj.Shape.Volume, FreeCAD.Units.Volume
|
||||
)
|
||||
it.setText(
|
||||
val.getUserPreferred()[0].replace(
|
||||
"^3", "³"
|
||||
)
|
||||
)
|
||||
it.setCheckable(True)
|
||||
else:
|
||||
if hasattr(obj, prop) and (
|
||||
not "Hidden" in obj.getEditorMode(prop)
|
||||
groups = {}
|
||||
for name, role in self.objectslist.items():
|
||||
groups.setdefault(role, []).append(name)
|
||||
for names in groups.values():
|
||||
suffix = ""
|
||||
for name in names:
|
||||
if "+array" in name:
|
||||
name = name.split("+array")[0]
|
||||
suffix = " (duplicate)"
|
||||
obj = FreeCAD.ActiveDocument.getObject(name)
|
||||
if obj:
|
||||
if (
|
||||
not self.form.onlyVisible.isChecked()
|
||||
) or obj.ViewObject.isVisible():
|
||||
if obj.isDerivedFrom("Part::Feature") and not (
|
||||
Draft.getType(obj) == "Site"
|
||||
):
|
||||
it1 = QtGui.QStandardItem(obj.Label + suffix)
|
||||
it1.setToolTip(name + suffix)
|
||||
it1.setEditable(False)
|
||||
it1.setIcon(obj.ViewObject.Icon)
|
||||
props = []
|
||||
for prop in QPROPS:
|
||||
it = QtGui.QStandardItem()
|
||||
val = None
|
||||
if hasattr(obj, prop) and (
|
||||
"Hidden" not in obj.getEditorMode(prop)
|
||||
):
|
||||
val = self.get_text(obj, prop)
|
||||
it.setText(val)
|
||||
it.setCheckable(True)
|
||||
if val != None:
|
||||
d = None
|
||||
if hasattr(obj, "IfcAttributes"):
|
||||
d = obj.IfcAttributes
|
||||
elif hasattr(obj, "IfcData"):
|
||||
d = obj.IfcData
|
||||
if d:
|
||||
if ("Export" + prop in d) and (
|
||||
d["Export" + prop] == "True"
|
||||
):
|
||||
val = getattr(obj, prop)
|
||||
it.setText(
|
||||
val.getUserPreferred()[0].replace(
|
||||
"^2", "²"
|
||||
)
|
||||
)
|
||||
it.setCheckable(True)
|
||||
if val != None:
|
||||
d = None
|
||||
if hasattr(obj, "IfcAttributes"):
|
||||
d = obj.IfcAttributes
|
||||
elif hasattr(obj, "IfcData"):
|
||||
d = obj.IfcData
|
||||
if d:
|
||||
if ("Export" + prop in d) and (
|
||||
d["Export" + prop] == "True"
|
||||
):
|
||||
it.setCheckState(QtCore.Qt.Checked)
|
||||
if val == 0:
|
||||
it.setIcon(
|
||||
QtGui.QIcon(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"icons",
|
||||
"warning.svg",
|
||||
)
|
||||
)
|
||||
)
|
||||
if prop in [
|
||||
"Area",
|
||||
"HorizontalArea",
|
||||
"VerticalArea",
|
||||
"Volume",
|
||||
]:
|
||||
it.setEditable(False)
|
||||
props.append(it)
|
||||
self.qmodel.appendRow([it1] + props)
|
||||
self.quantitiesDrawn = True
|
||||
it.setCheckState(QtCore.Qt.Checked)
|
||||
elif self.has_qto(obj, prop):
|
||||
it.setCheckState(QtCore.Qt.Checked)
|
||||
if val == 0:
|
||||
it.setIcon(QtGui.QIcon(":/icons/warning.svg"))
|
||||
self.set_editable(it, prop)
|
||||
props.append(it)
|
||||
self.qmodel.appendRow([it1] + props)
|
||||
|
||||
def has_qto(self, obj, prop):
|
||||
"""Says if the given object has the given prop in a qto set"""
|
||||
|
||||
if not "StepId" in obj.PropertiesList:
|
||||
return False
|
||||
from nativeifc import ifc_tools
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not element:
|
||||
return False
|
||||
for rel in getattr(element, "IsDefinedBy", []):
|
||||
pset = rel.RelatingPropertyDefinition
|
||||
if pset.is_a("IfcElementQuantity"):
|
||||
if pset.Name in self.qtodefs:
|
||||
if prop in self.qtodefs[pset.Name]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_text(self, obj, prop):
|
||||
"""Gets the text from a property"""
|
||||
|
||||
val = getattr(obj, prop, "0")
|
||||
txt = val.getUserPreferred()[0].replace("^2", "²").replace("^3", "³")
|
||||
return txt
|
||||
|
||||
def get_row(self, name):
|
||||
"""Returns the row number correspinding to the given object name"""
|
||||
|
||||
for i in range(self.qmodel.rowCount()):
|
||||
if self.qmodel.item(i).toolTip().split(" ")[0] == name:
|
||||
return i
|
||||
return -1
|
||||
|
||||
def update_line(self, name, qto):
|
||||
"""Updates a single line of the table, without updating
|
||||
the actual object"""
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
i = self.get_row(name)
|
||||
if i == -1:
|
||||
return
|
||||
obj = FreeCAD.ActiveDocument.getObject(name)
|
||||
qto_val = self.qtodefs[qto]
|
||||
for j, p in enumerate(QPROPS):
|
||||
it = self.qmodel.item(i, j+1)
|
||||
if p in obj.PropertiesList:
|
||||
val = self.get_text(obj, p)
|
||||
it.setText(val)
|
||||
self.set_editable(it, p)
|
||||
it.setCheckable(True)
|
||||
elif p in qto_val:
|
||||
it.setText("0")
|
||||
it.setCheckable(True)
|
||||
it.setCheckState(QtCore.Qt.Checked)
|
||||
self.set_editable(it, p)
|
||||
|
||||
def set_editable(self, it, prop):
|
||||
"""Checks if the given prop should be editable, and sets it"""
|
||||
|
||||
if prop in ["Area", "HorizontalArea", "VerticalArea", "Volume"]:
|
||||
it.setEditable(False)
|
||||
else:
|
||||
it.setEditable(True)
|
||||
|
||||
def getRole(self, obj):
|
||||
"""gets the IFC class of this object"""
|
||||
|
||||
if hasattr(obj, "IfcType"):
|
||||
return obj.IfcType
|
||||
elif hasattr(obj, "IfcRole"):
|
||||
return obj.IfcRole
|
||||
elif hasattr(obj, "IfcClass"):
|
||||
return obj.IfcClass
|
||||
else:
|
||||
return None
|
||||
|
||||
def accept(self):
|
||||
"""OK pressed"""
|
||||
|
||||
PARAMS.SetInt("BimIfcQuantitiesDialogWidth", self.form.width())
|
||||
PARAMS.SetInt("BimIfcQuantitiesDialogHeight", self.form.height())
|
||||
self.form.hide()
|
||||
changed = False
|
||||
if self.ifcqtolist:
|
||||
if not changed:
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
"Change quantities"
|
||||
)
|
||||
changed = True
|
||||
for key, val in self.ifcqtolist.items():
|
||||
obj = FreeCAD.ActiveDocument.getObject(key)
|
||||
if obj:
|
||||
for qto in val:
|
||||
self.apply_qto(obj, qto)
|
||||
for row in range(self.qmodel.rowCount()):
|
||||
name = self.qmodel.item(row, 0).toolTip()
|
||||
obj = FreeCAD.ActiveDocument.getObject(name)
|
||||
if obj:
|
||||
for i in range(len(qprops)):
|
||||
for i in range(len(QPROPS)):
|
||||
item = self.qmodel.item(row, i + 1)
|
||||
val = item.text()
|
||||
sav = bool(item.checkState())
|
||||
if i < 3: # Length, Width, Height, value can be changed
|
||||
if hasattr(obj, qprops[i]):
|
||||
if getattr(obj, qprops[i]).getUserPreferred()[0] != val:
|
||||
setattr(obj, qprops[i], val)
|
||||
if hasattr(obj, QPROPS[i]):
|
||||
if getattr(obj, QPROPS[i]).getUserPreferred()[0] != val:
|
||||
setattr(obj, QPROPS[i], val)
|
||||
if not changed:
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
"Change quantities"
|
||||
@@ -277,10 +413,10 @@ class BIM_IfcQuantities:
|
||||
att = "IfcData"
|
||||
if d:
|
||||
if sav:
|
||||
if (not "Export" + qprops[i] in d) or (
|
||||
d["Export" + qprops[i]] == "False"
|
||||
if (not "Export" + QPROPS[i] in d) or (
|
||||
d["Export" + QPROPS[i]] == "False"
|
||||
):
|
||||
d["Export" + qprops[i]] = "True"
|
||||
d["Export" + QPROPS[i]] = "True"
|
||||
setattr(obj, att, d)
|
||||
if not changed:
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
@@ -288,16 +424,16 @@ class BIM_IfcQuantities:
|
||||
)
|
||||
changed = True
|
||||
else:
|
||||
if "Export" + qprops[i] in d:
|
||||
if d["Export" + qprops[i]] == "True":
|
||||
d["Export" + qprops[i]] = "False"
|
||||
if "Export" + QPROPS[i] in d:
|
||||
if d["Export" + QPROPS[i]] == "True":
|
||||
d["Export" + QPROPS[i]] = "False"
|
||||
setattr(obj, att, d)
|
||||
if not changed:
|
||||
FreeCAD.ActiveDocument.openTransaction(
|
||||
"Change quantities"
|
||||
)
|
||||
changed = True
|
||||
else:
|
||||
elif "StepId" not in obj.PropertiesList:
|
||||
FreeCAD.Console.PrintError(
|
||||
translate(
|
||||
"BIM", "Cannot save quantities settings for object %1"
|
||||
|
||||
@@ -272,14 +272,17 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
objectslist = Draft.get_group_contents(exportList, walls=True,
|
||||
addgroups=True)
|
||||
|
||||
# separate 2D objects
|
||||
# separate 2D and special objects. Special objects provide their own IFC export method
|
||||
|
||||
annotations = []
|
||||
specials = []
|
||||
for obj in objectslist:
|
||||
if obj.isDerivedFrom("Part::Part2DObject"):
|
||||
annotations.append(obj)
|
||||
elif obj.isDerivedFrom("App::Annotation") or (Draft.getType(obj) in ["DraftText","Text","Dimension","LinearDimension","AngularDimension"]):
|
||||
annotations.append(obj)
|
||||
elif hasattr(obj, "Proxy") and hasattr(obj.Proxy, "export_ifc"):
|
||||
specials.append(obj)
|
||||
elif obj.isDerivedFrom("Part::Feature"):
|
||||
if obj.Shape and (not obj.Shape.Solids) and obj.Shape.Edges:
|
||||
if not obj.Shape.Faces:
|
||||
@@ -290,6 +293,7 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
# clean objects list of unwanted types
|
||||
|
||||
objectslist = [obj for obj in objectslist if obj not in annotations]
|
||||
objectslist = [obj for obj in objectslist if obj not in specials]
|
||||
objectslist = Arch.pruneIncluded(objectslist,strict=True)
|
||||
objectslist = [obj for obj in objectslist if Draft.getType(obj) not in ["Dimension","Material","MaterialContainer","WorkingPlaneProxy"]]
|
||||
if preferences['FULL_PARAMETRIC']:
|
||||
@@ -1296,6 +1300,14 @@ def export(exportList, filename, colors=None, preferences=None):
|
||||
ann = create_annotation(anno, ifcfile, context, history, preferences)
|
||||
annos[anno.Name] = ann
|
||||
|
||||
# specials. Specials should take care of register themselves where needed under the project
|
||||
|
||||
specs = {}
|
||||
for spec in specials:
|
||||
if preferences['DEBUG']: print("exporting special object:",spec.Label)
|
||||
elt = spec.Proxy.export_ifc(spec, ifcfile)
|
||||
specs[spec.Name] = elt
|
||||
|
||||
# groups
|
||||
|
||||
sortedgroups = []
|
||||
|
||||
@@ -187,6 +187,8 @@ def get_object_type(ifcentity, objecttype=None):
|
||||
objecttype = "text"
|
||||
elif ifcentity.is_a("IfcGridAxis"):
|
||||
objecttype = "axis"
|
||||
elif ifcentity.is_a("IfcControl"):
|
||||
objecttype = "schedule"
|
||||
return objecttype
|
||||
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ class ifc_object:
|
||||
obj.ViewObject.signalChangeIcon()
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Geometry":
|
||||
self.edit_geometry(obj, prop)
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Quantities":
|
||||
self.edit_quantity(obj, prop)
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) not in NON_PSETS:
|
||||
# Treat all property groups outside the default ones as Psets
|
||||
# print("DEBUG: editinog pset prop",prop)
|
||||
@@ -353,6 +355,11 @@ class ifc_object:
|
||||
# Not doing anything right now because an unset Type property could screw the ifc file
|
||||
pass
|
||||
|
||||
|
||||
def edit_quantity(self, obj, prop):
|
||||
"""Edits the given quantity"""
|
||||
pass # TODO implement
|
||||
|
||||
def get_section_data(self, obj):
|
||||
"""Returns two things: a list of objects and a cut plane"""
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
|
||||
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
|
||||
@@ -197,8 +196,10 @@ class ifc_observer:
|
||||
return
|
||||
del self.docname
|
||||
del self.objname
|
||||
if obj.isDerivedFrom("Part::Feature") or "IfcType" in obj.PropertiesList:
|
||||
FreeCAD.Console.PrintLog("Converting" + obj.Label + "to IFC\n")
|
||||
if obj.isDerivedFrom("Part::Feature") \
|
||||
or "IfcType" in obj.PropertiesList \
|
||||
or "CreateSpreadsheet" in obj.PropertiesList:
|
||||
FreeCAD.Console.PrintLog("Converting " + obj.Label + " to IFC\n")
|
||||
from nativeifc import ifc_geometry # lazy loading
|
||||
from nativeifc import ifc_tools # lazy loading
|
||||
|
||||
|
||||
@@ -105,34 +105,35 @@ def show_psets(obj):
|
||||
ttip = (
|
||||
ptype + ":" + oname
|
||||
) # setting IfcType:PropName as a tooltip to desambiguate
|
||||
while pname in obj.PropertiesList:
|
||||
#while pname in obj.PropertiesList:
|
||||
# print("DEBUG: property", pname, "(", value, ") already exists in", obj.Label)
|
||||
pname += "_"
|
||||
# pname += "_"
|
||||
ftype = None
|
||||
if ptype in [
|
||||
"IfcPositiveLengthMeasure",
|
||||
"IfcLengthMeasure",
|
||||
"IfcNonNegativeLengthMeasure",
|
||||
]:
|
||||
obj.addProperty("App::PropertyDistance", pname, gname, ttip)
|
||||
ftype = "App::PropertyDistance"
|
||||
elif ptype in ["IfcVolumeMeasure"]:
|
||||
obj.addProperty("App::PropertyVolume", pname, gname, ttip)
|
||||
ftype = "App::PropertyVolume"
|
||||
elif ptype in ["IfcPositivePlaneAngleMeasure", "IfcPlaneAngleMeasure"]:
|
||||
obj.addProperty("App::PropertyAngle", pname, gname, ttip)
|
||||
ftype = "App::PropertyAngle"
|
||||
value = float(value)
|
||||
while value > 360:
|
||||
value = value - 360
|
||||
elif ptype in ["IfcMassMeasure"]:
|
||||
obj.addProperty("App::PropertyMass", pname, gname, ttip)
|
||||
ftype = "App::PropertyMass"
|
||||
elif ptype in ["IfcAreaMeasure"]:
|
||||
obj.addProperty("App::PropertyArea", pname, gname, ttip)
|
||||
ftype = "App::PropertyArea"
|
||||
elif ptype in ["IfcCountMeasure", "IfcInteger"]:
|
||||
obj.addProperty("App::PropertyInteger", pname, gname, ttip)
|
||||
ftype = "App::PropertyInteger"
|
||||
value = int(value.strip("."))
|
||||
elif ptype in ["IfcReal"]:
|
||||
obj.addProperty("App::PropertyFloat", pname, gname, ttip)
|
||||
ftype = "App::PropertyFloat"
|
||||
value = float(value)
|
||||
elif ptype in ["IfcBoolean", "IfcLogical"]:
|
||||
obj.addProperty("App::PropertyBool", pname, gname, ttip)
|
||||
ftype = "App::PropertyBool"
|
||||
if value in [".T."]:
|
||||
value = True
|
||||
else:
|
||||
@@ -144,14 +145,30 @@ def show_psets(obj):
|
||||
"IfcDuration",
|
||||
"IfcTimeStamp",
|
||||
]:
|
||||
obj.addProperty("App::PropertyTime", pname, gname, ttip)
|
||||
ftype = "App::PropertyTime"
|
||||
elif isinstance(value, str) and "::" in value:
|
||||
# FreeCAD-specific: split strings by :: delimiter
|
||||
ftype = "App::PropertyStringList"
|
||||
value = value.split("::")
|
||||
else:
|
||||
obj.addProperty("App::PropertyString", pname, gname, ttip)
|
||||
ftype = "App::PropertyString"
|
||||
# print("DEBUG: setting",pname, ptype, value)
|
||||
setattr(obj, pname, value)
|
||||
if ftype:
|
||||
if pname in obj.PropertiesList \
|
||||
and obj.getGroupOfProperty(pname) == gname:
|
||||
if obj.getTypeOfProperty(pname) == ftype:
|
||||
pass
|
||||
if ftype == "App::PropertyString" \
|
||||
and obj.getTypeOfProperty(pname) == "App::PropertyStringList":
|
||||
value = [value]
|
||||
else:
|
||||
print(pname, gname, obj.PropertiesList)
|
||||
obj.addProperty(ftype, pname, gname, ttip)
|
||||
if pname in obj.PropertiesList:
|
||||
setattr(obj, pname, value)
|
||||
|
||||
|
||||
def edit_pset(obj, prop, value=None, force=False):
|
||||
def edit_pset(obj, prop, value=None, force=False, ifcfile=None, element=None):
|
||||
"""Edits the corresponding property. If force is True,
|
||||
the property is created even if it has no value"""
|
||||
|
||||
@@ -159,8 +176,14 @@ def edit_pset(obj, prop, value=None, force=False):
|
||||
ptype = obj.getDocumentationOfProperty(prop)
|
||||
if value is None:
|
||||
value = getattr(obj, prop)
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not ifcfile:
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
if not ifcfile:
|
||||
return
|
||||
if not element:
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not element:
|
||||
return
|
||||
pset_exist = get_psets(element)
|
||||
target_prop = None
|
||||
value_exist = None
|
||||
@@ -242,7 +265,7 @@ def edit_pset(obj, prop, value=None, force=False):
|
||||
"IFC: property changed for "
|
||||
+ obj.Label
|
||||
+ " ("
|
||||
+ str(obj.StepId)
|
||||
+ str(element.id())
|
||||
+ "): "
|
||||
+ str(target_prop)
|
||||
+ ": "
|
||||
@@ -356,3 +379,7 @@ def remove_property(obj, prop):
|
||||
# delete the pset too
|
||||
FreeCAD.Console.PrintMessage(translate("BIM","Removing property set")+": "+psetname)
|
||||
ifc_tools.api_run("pset.remove_pset", ifcfile, product=element, pset=pset)
|
||||
|
||||
|
||||
# Quantity types
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/ifcsharedbldgelements/content.html#6.1.5-Quantity-Sets
|
||||
|
||||
@@ -46,6 +46,7 @@ from nativeifc import ifc_import
|
||||
from nativeifc import ifc_layers
|
||||
from nativeifc import ifc_status
|
||||
from nativeifc import ifc_export
|
||||
from nativeifc import ifc_psets
|
||||
|
||||
from draftviewproviders import view_layer
|
||||
from PySide import QtCore
|
||||
@@ -443,7 +444,7 @@ def get_ifcfile(obj):
|
||||
if getattr(project, "Proxy", None):
|
||||
if hasattr(project.Proxy, "ifcfile"):
|
||||
return project.Proxy.ifcfile
|
||||
if project.IfcFilePath:
|
||||
if getattr(project, "IfcFilePath", None):
|
||||
ifcfile = ifcopenshell.open(project.IfcFilePath)
|
||||
if hasattr(project, "Proxy"):
|
||||
if project.Proxy is None:
|
||||
@@ -453,7 +454,7 @@ def get_ifcfile(obj):
|
||||
project.Proxy.ifcfile = ifcfile
|
||||
return ifcfile
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Error: No IFC file attached to this project")
|
||||
FreeCAD.Console.PrintError("Error: No IFC file attached to this project: "+project.Label)
|
||||
return None
|
||||
|
||||
|
||||
@@ -508,11 +509,14 @@ def add_object(document, otype=None, oname="IfcObject"):
|
||||
'dimension',
|
||||
'sectionplane',
|
||||
'axis',
|
||||
'schedule'
|
||||
or anything else for a standard IFC object"""
|
||||
|
||||
if not document:
|
||||
return None
|
||||
if otype == "sectionplane":
|
||||
if otype == "schedule":
|
||||
obj = Arch.makeSchedule()
|
||||
elif otype == "sectionplane":
|
||||
obj = Arch.makeSectionPlane()
|
||||
obj.Proxy = ifc_objects.ifc_object(otype)
|
||||
elif otype == "axis":
|
||||
@@ -746,6 +750,8 @@ def add_properties(
|
||||
obj.addProperty("App::PropertyStringList", "Text", "Base")
|
||||
obj.Text = [text.Literal]
|
||||
obj.Placement = ifc_export.get_placement(ifcentity.ObjectPlacement, ifcfile)
|
||||
elif ifcentity.is_a("IfcControl"):
|
||||
ifc_psets.show_psets(obj)
|
||||
|
||||
# link Label2 and Description
|
||||
if "Description" in obj.PropertiesList and hasattr(obj, "setExpression"):
|
||||
@@ -1150,6 +1156,7 @@ def aggregate(obj, parent, mode=None):
|
||||
if not ifcfile:
|
||||
return
|
||||
product = None
|
||||
new = False
|
||||
stepid = getattr(obj, "StepId", None)
|
||||
if stepid:
|
||||
# obj might be dragging at this point and has no project anymore
|
||||
@@ -1163,7 +1170,6 @@ def aggregate(obj, parent, mode=None):
|
||||
# this object already has an associated IFC product
|
||||
print("DEBUG:", obj.Label, "is already part of the IFC document")
|
||||
newobj = obj
|
||||
new = False
|
||||
else:
|
||||
ifcclass = None
|
||||
if mode == "opening":
|
||||
@@ -1173,12 +1179,16 @@ def aggregate(obj, parent, mode=None):
|
||||
product = ifc_export.create_annotation(obj, ifcfile)
|
||||
if Draft.get_type(obj) in ["DraftText","Text"]:
|
||||
objecttype = "text"
|
||||
elif "CreateSpreadsheet" in obj.PropertiesList:
|
||||
obj.Proxy.create_ifc(obj, ifcfile)
|
||||
newobj = obj
|
||||
else:
|
||||
product = ifc_export.create_product(obj, parent, ifcfile, ifcclass)
|
||||
if product:
|
||||
shapemode = getattr(parent, "ShapeMode", DEFAULT_SHAPEMODE)
|
||||
newobj = create_object(product, obj.Document, ifcfile, shapemode, objecttype)
|
||||
new = True
|
||||
create_relationship(obj, newobj, parent, product, ifcfile, mode)
|
||||
create_relationship(obj, newobj, parent, product, ifcfile, mode)
|
||||
base = getattr(obj, "Base", None)
|
||||
if base:
|
||||
# make sure the base is used only by this object before deleting
|
||||
@@ -1524,6 +1534,12 @@ def get_orphan_elements(ifcfile):
|
||||
products = [
|
||||
p for p in products if not hasattr(p, "VoidsElements") or not p.VoidsElements
|
||||
]
|
||||
# add control elements
|
||||
proj = ifcfile.by_type("IfcProject")[0]
|
||||
for rel in proj.Declares:
|
||||
for ctrl in getattr(rel,"RelatedDefinitions", []):
|
||||
if ctrl.is_a("IfcControl"):
|
||||
products.append(ctrl)
|
||||
groups = []
|
||||
for o in products:
|
||||
for rel in getattr(o, "HasAssignments", []):
|
||||
|
||||
@@ -20,56 +20,119 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""This script retrieves a list of standard property sets from the IFC4 official documentation website
|
||||
and stores them into a pset_dfinitions.xml files in the current directory. Warning, this can take
|
||||
a certain time (there are more than 400 definitions to retrieve)"""
|
||||
"""This script retrieves a list of standard property sets from the IFC4 official
|
||||
documentation website and stores them into 1) a pset_definitions.csv and 2)
|
||||
a qto_definitions.csv files in the directory ../Presets."""
|
||||
|
||||
import codecs, os, re
|
||||
import os
|
||||
from zipfile import ZipFile
|
||||
from urllib.request import urlopen
|
||||
import xml.sax
|
||||
|
||||
MAXTRIES = 3
|
||||
IFC_DOCS_ROOT_URL = "https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/"
|
||||
|
||||
# read the pset list
|
||||
print("Getting psets list...")
|
||||
u = urlopen(
|
||||
IFC_DOCS_ROOT_URL + "annex/annex-b/alphabeticalorder_psets.htm"
|
||||
)
|
||||
p = u.read().decode('utf-8')
|
||||
u.close()
|
||||
psets = re.findall(r">Pset_(.*?)</a>", p)
|
||||
URL = "https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/annex-a-psd.zip"
|
||||
|
||||
# retrieve xml data from each Pset type
|
||||
psetdefs = ""
|
||||
failed = []
|
||||
for i, pset in enumerate(psets):
|
||||
print(i + 1, "/", len(psets), ": Retrieving Pset", pset)
|
||||
for j in range(MAXTRIES):
|
||||
try:
|
||||
u = urlopen(
|
||||
IFC_DOCS_ROOT_URL + "psd/Pset_"
|
||||
+ pset
|
||||
+ ".xml"
|
||||
)
|
||||
p = u.read().decode('utf-8')
|
||||
u.close()
|
||||
except:
|
||||
print(" Connection failed. trying one more time...")
|
||||
QTO_TYPES = {
|
||||
"Q_AREA": "IfcQuantityArea",
|
||||
"Q_COUNT": "IfcQuantityCount",
|
||||
"Q_LENGTH": "IfcQuantityLength",
|
||||
"Q_NUMBER": "IfcQuantityNumber",
|
||||
"Q_TIME": "IfcQuantityTime",
|
||||
"Q_VOLUME": "IfcQuantityVolume",
|
||||
"Q_WEIGHT": "IfcQuantityWeight",
|
||||
}
|
||||
|
||||
class PropertyDefHandler(xml.sax.ContentHandler):
|
||||
"A XML handler to process pset definitions"
|
||||
|
||||
# this creates a dictionary where each key is a Pset name,
|
||||
# and each value is a list of [property,type] lists
|
||||
|
||||
def __init__(self, pset):
|
||||
super().__init__()
|
||||
self.line = pset.strip(".xml") + ";"
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
self.charbuffer = []
|
||||
self.writing = False
|
||||
self.prop = False
|
||||
self.qtotype = False
|
||||
|
||||
# Call when raw text is read (the property name)
|
||||
|
||||
def characters(self, data):
|
||||
if self.writing:
|
||||
self.charbuffer.append(data)
|
||||
|
||||
# Call when an element starts
|
||||
|
||||
def startElement(self, tag, attributes):
|
||||
if tag in ["PropertyDef", "QtoDef"]:
|
||||
self.prop = True
|
||||
elif tag == "Name":
|
||||
self.writing = True
|
||||
elif tag == "DataType":
|
||||
self.currenttype = attributes["type"]
|
||||
elif tag == "QtoType":
|
||||
self.qtotype = True
|
||||
self.writing = True
|
||||
|
||||
# Call when an elements ends
|
||||
|
||||
def endElement(self, tag):
|
||||
if tag in ["Name", "QtoType"]:
|
||||
if self.prop:
|
||||
self.currentprop = "".join(self.charbuffer)
|
||||
elif self.qtotype:
|
||||
self.currenttype = "".join(self.charbuffer)
|
||||
self.writing = False
|
||||
self.prop = False
|
||||
self.qtotype = False
|
||||
self.charbuffer = []
|
||||
elif tag in ["PropertyDef", "QtoDef"]:
|
||||
if self.currentprop and self.currenttype:
|
||||
if self.currenttype in QTO_TYPES:
|
||||
self.currenttype = QTO_TYPES[self.currenttype]
|
||||
self.line += self.currentprop + ";" + self.currenttype + ";"
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
|
||||
|
||||
|
||||
# MAIN
|
||||
|
||||
|
||||
print("Getting psets xml definitions...")
|
||||
|
||||
with open("psd.zip","wb") as f:
|
||||
u = urlopen(URL)
|
||||
p = u.read()
|
||||
f.write(p)
|
||||
|
||||
print("Reading xml definitions...")
|
||||
|
||||
psets = []
|
||||
qtos = []
|
||||
|
||||
with ZipFile("psd.zip", 'r') as z:
|
||||
for entry in z.namelist():
|
||||
print("Parsing",entry)
|
||||
xml_data = z.read(entry).decode(encoding="utf-8")
|
||||
handler = PropertyDefHandler(entry)
|
||||
xml.sax.parseString(xml_data, handler)
|
||||
if entry.startswith("Pset"):
|
||||
psets.append(handler.line)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
print(" Unable to retrieve ", pset, ". Skipping...")
|
||||
failed.append(pset)
|
||||
psetdefs += p
|
||||
psetdefs = psetdefs.replace('<?xml version="1.0" encoding="utf-8"?>', "")
|
||||
psetdefs = '<?xml version="1.0" encoding="utf-8"?>\n<Root>\n' + psetdefs + "</Root>"
|
||||
qtos.append(handler.line)
|
||||
|
||||
f = codecs.open("pset_definitions.xml", "wb", "utf-8")
|
||||
f.write(psetdefs)
|
||||
f.close()
|
||||
print(
|
||||
"All done! writing "
|
||||
+ os.path.join(os.path.abspath(os.curdir), "pset_definitions.xml")
|
||||
)
|
||||
if failed:
|
||||
print("The following psets failed and were not retrieved:", failed)
|
||||
print("Saving files...")
|
||||
|
||||
with open("../Presets/pset_definitions.csv", "w") as f:
|
||||
for l in psets:
|
||||
f.write(l.strip(";") + "\n")
|
||||
|
||||
with open("../Presets/qto_definitions.csv", "w") as f:
|
||||
for l in qtos:
|
||||
f.write(l.strip(";") + "\n")
|
||||
|
||||
os.remove("psd.zip")
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * 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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""This script converts a xml file containing pset definitions to a csv file.
|
||||
Python3 only!! (py2 csv doesn't support utf8"""
|
||||
|
||||
import xml.sax, os
|
||||
|
||||
|
||||
class PropertyDefHandler(xml.sax.ContentHandler):
|
||||
"A XML handler to process pset definitions"
|
||||
|
||||
# this creates a dictionary where each key is a Pset name,
|
||||
# and each value is a list of [property,type] lists
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.psets = {}
|
||||
self.currentpset = None
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
self.currentlist = []
|
||||
self.charbuffer = []
|
||||
self.writing = False
|
||||
|
||||
# Call when raw text is read
|
||||
|
||||
def characters(self, data):
|
||||
if self.writing:
|
||||
self.charbuffer.append(data)
|
||||
|
||||
# Call when an element starts
|
||||
|
||||
def startElement(self, tag, attributes):
|
||||
if tag == "Name":
|
||||
self.writing = True
|
||||
if tag == "DataType":
|
||||
self.currenttype = attributes["type"]
|
||||
|
||||
# Call when an elements ends
|
||||
|
||||
def endElement(self, tag):
|
||||
if tag == "Name":
|
||||
if not self.currentpset:
|
||||
self.currentpset = "".join(self.charbuffer)
|
||||
else:
|
||||
if not self.currentprop:
|
||||
self.currentprop = "".join(self.charbuffer)
|
||||
self.writing = False
|
||||
self.charbuffer = []
|
||||
elif tag == "PropertyDef":
|
||||
if self.currentprop and self.currenttype:
|
||||
self.currentlist.append([self.currentprop, self.currenttype])
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
elif tag == "PropertySetDef":
|
||||
if self.currentpset and self.currentlist:
|
||||
self.psets[self.currentpset] = self.currentlist
|
||||
self.currentpset = None
|
||||
self.currentlist = []
|
||||
|
||||
|
||||
defpath = "pset_definitions.xml"
|
||||
outpath = "pset_definitions.csv"
|
||||
|
||||
if os.path.exists(defpath):
|
||||
handler = PropertyDefHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
# parser.setFeature(xml.sax.handler.feature_namespaces, 0)
|
||||
parser.setContentHandler(handler)
|
||||
parser.parse(defpath)
|
||||
psets = handler.psets
|
||||
|
||||
import csv
|
||||
|
||||
with open(outpath, "w", encoding="utf-8") as csvfile:
|
||||
csvfile = csv.writer(csvfile, delimiter=";")
|
||||
for key, values in psets.items():
|
||||
r = [key]
|
||||
for value in values:
|
||||
r.extend(value)
|
||||
csvfile.writerow(r)
|
||||
print("successfully exported ", outpath)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user