894 lines
37 KiB
Python
894 lines
37 KiB
Python
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net> *
|
|
# * *
|
|
# * This file is part of FreeCAD. *
|
|
# * *
|
|
# * FreeCAD is free software: you can redistribute it and/or modify it *
|
|
# * under the terms of the GNU Lesser General Public License as *
|
|
# * published by the Free Software Foundation, either version 2.1 of the *
|
|
# * License, or (at your option) any later version. *
|
|
# * *
|
|
# * FreeCAD 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 *
|
|
# * Lesser General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Lesser General Public *
|
|
# * License along with FreeCAD. If not, see *
|
|
# * <https://www.gnu.org/licenses/>. *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
"""This module contains FreeCAD commands for the BIM workbench"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
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")
|
|
|
|
|
|
class BIM_IfcProperties:
|
|
|
|
def GetResources(self):
|
|
return {
|
|
"Pixmap": "BIM_IfcProperties",
|
|
"MenuText": QT_TRANSLATE_NOOP(
|
|
"BIM_IfcProperties", "Manage IFC Properties"
|
|
),
|
|
"ToolTip": QT_TRANSLATE_NOOP(
|
|
"BIM_IfcProperties",
|
|
"Manages the different IFC properties of the BIM objects",
|
|
),
|
|
}
|
|
|
|
def IsActive(self):
|
|
v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph")
|
|
return v
|
|
|
|
def Activated(self):
|
|
from PySide import QtGui
|
|
|
|
try:
|
|
import ArchIFC
|
|
|
|
self.ifcroles = ArchIFC.IfcTypes
|
|
except (ImportError, AttributeError):
|
|
import ArchComponent
|
|
|
|
self.ifcroles = ArchComponent.IfcRoles
|
|
|
|
# load the form and set the tree model up
|
|
self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogIfcProperties.ui")
|
|
self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_IfcProperties.svg"))
|
|
self.form.groupMode.setItemIcon(2, QtGui.QIcon(":/icons/Document.svg"))
|
|
self.model = QtGui.QStandardItemModel()
|
|
self.form.tree.setModel(self.model)
|
|
self.form.tree.setUniformRowHeights(True)
|
|
|
|
# restore saved values
|
|
self.form.onlySelected.setChecked(PARAMS.GetInt("IfcPropertiesSelectedState", 0))
|
|
self.form.onlyVisible.setChecked(PARAMS.GetInt("IfcPropertiesVisibleState", 0))
|
|
w = PARAMS.GetInt("BimIfcPropertiesDialogWidth", 1200)
|
|
h = PARAMS.GetInt("BimIfcPropertiesDialogHeight", 608)
|
|
self.form.resize(w, h)
|
|
|
|
# build objects list and fill search terms
|
|
self.objectslist, searchterms = self.rebuildObjectsList()
|
|
self.form.searchField.addItems(searchterms)
|
|
|
|
# set the properties editor
|
|
try:
|
|
import ArchIFCSchema
|
|
|
|
self.ptypes = list(ArchIFCSchema.IfcTypes.keys())
|
|
except (ImportError, AttributeError):
|
|
import ArchComponent
|
|
|
|
self.ptypes = (
|
|
ArchComponent.SimplePropertyTypes + ArchComponent.MeasurePropertyTypes
|
|
)
|
|
self.plabels = [
|
|
"".join(map(lambda x: x if x.islower() else " " + x, t[3:]))[1:]
|
|
for t in self.ptypes
|
|
]
|
|
self.psetdefs = {}
|
|
psetpath = os.path.join(
|
|
FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "pset_definitions.csv"
|
|
)
|
|
custompath = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "CustomPsets.csv")
|
|
self.psetdefs = self.readFromCSV(psetpath)
|
|
self.psetdefs.update(self.readFromCSV(custompath))
|
|
self.psetkeys = [
|
|
"".join(map(lambda x: x if x.islower() else " " + x, t[5:]))[1:]
|
|
for t in self.psetdefs.keys()
|
|
]
|
|
self.psetkeys.sort()
|
|
self.propmodel = QtGui.QStandardItemModel()
|
|
self.form.treeProperties.setModel(self.propmodel)
|
|
# self.ifcEditor.treeProperties.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
|
|
self.form.treeProperties.setUniformRowHeights(True)
|
|
self.form.treeProperties.setItemDelegate(
|
|
propertiesDelegate(container=self, ptypes=self.ptypes, plabels=self.plabels)
|
|
)
|
|
self.form.labelinfo.setText(
|
|
self.form.labelinfo.text()
|
|
+ " "
|
|
+ translate("BIM", "Custom property sets can be defined in")
|
|
+ " "
|
|
+ custompath
|
|
)
|
|
|
|
# set combos
|
|
self.form.comboProperty.addItems(
|
|
[translate("BIM", "Add property")] + self.plabels
|
|
)
|
|
self.form.comboPset.addItems(
|
|
[translate("BIM", "Add property set"), translate("BIM", "New")]
|
|
+ self.psetkeys
|
|
)
|
|
|
|
# connect signals
|
|
self.form.tree.selectionModel().selectionChanged.connect(self.updateProperties)
|
|
self.form.groupMode.currentIndexChanged.connect(self.update)
|
|
if hasattr(self.form.onlyVisible, "checkStateChanged"): # Qt version >= 6.7.0
|
|
self.form.onlyVisible.checkStateChanged.connect(self.update)
|
|
self.form.onlySelected.checkStateChanged.connect(self.onSelected)
|
|
self.form.onlyMatches.checkStateChanged.connect(self.update)
|
|
else: # Qt version < 6.7.0
|
|
self.form.onlyVisible.stateChanged.connect(self.update)
|
|
self.form.onlySelected.stateChanged.connect(self.onSelected)
|
|
self.form.onlyMatches.stateChanged.connect(self.update)
|
|
self.form.buttonBox.accepted.connect(self.accept)
|
|
self.form.searchField.currentIndexChanged.connect(self.update)
|
|
self.form.searchField.editTextChanged.connect(self.update)
|
|
self.form.comboProperty.currentIndexChanged.connect(self.addProperty)
|
|
self.form.comboPset.currentIndexChanged.connect(self.addPset)
|
|
self.form.buttonIFCPropertiesDelete.clicked.connect(self.removeProperty)
|
|
self.form.treeProperties.setSortingEnabled(True)
|
|
|
|
# center the dialog over FreeCAD window
|
|
mw = FreeCADGui.getMainWindow()
|
|
self.form.move(
|
|
mw.frameGeometry().topLeft()
|
|
+ mw.rect().center()
|
|
- self.form.rect().center()
|
|
)
|
|
|
|
self.update()
|
|
self.form.show()
|
|
|
|
def rebuildObjectsList(self):
|
|
|
|
# build objects list and fill search terms
|
|
objectslist = {}
|
|
searchterms = [""]
|
|
if self.form.onlySelected.isChecked():
|
|
objects = FreeCADGui.Selection.getSelection()
|
|
else:
|
|
objects = FreeCAD.ActiveDocument.Objects
|
|
for obj in objects:
|
|
role = self.getRole(obj)
|
|
if role:
|
|
if hasattr(obj, "IfcProperties") and isinstance(
|
|
obj.IfcProperties, dict
|
|
):
|
|
props = obj.IfcProperties
|
|
elif hasattr(obj, "IfcClass"):
|
|
props = self.getNativeIfcProperties(obj)
|
|
else:
|
|
props = {}
|
|
objectslist[obj.Name] = [role, props]
|
|
for key, val in props.items():
|
|
val = val.split(";;")
|
|
if ";;" in key:
|
|
# 0.19 format
|
|
# pset;;pname = ptype;;pvalue
|
|
key = key.split(";;")
|
|
val = [key[1]] + val
|
|
key = key[0]
|
|
if not key in searchterms:
|
|
searchterms.append(key)
|
|
if len(val) == 3:
|
|
if not val[0] in searchterms:
|
|
searchterms.append(val[0])
|
|
return objectslist, searchterms
|
|
|
|
def update(self, index=None):
|
|
"updates the tree widgets in all tabs"
|
|
|
|
self.model.clear()
|
|
self.model.setHorizontalHeaderLabels(
|
|
[
|
|
translate("BIM", "Label"),
|
|
translate("BIM", "IFC type"),
|
|
translate("BIM", "Search results"),
|
|
]
|
|
)
|
|
# self.form.tree.header().setResizeMode(QtGui.QHeaderView.Stretch)
|
|
# self.form.tree.resizeColumnsToContents()
|
|
if self.form.groupMode.currentIndex() == 1:
|
|
# group by type
|
|
self.updateByType()
|
|
elif self.form.groupMode.currentIndex() == 2:
|
|
# group by model structure
|
|
self.updateByTree()
|
|
else:
|
|
# group alphabetically
|
|
self.updateDefault()
|
|
self.model.sort(0)
|
|
self.form.tree.setColumnWidth(0, 300)
|
|
|
|
def getRole(self, obj):
|
|
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 readFromCSV(self, csvfile):
|
|
"""reads a csv file and returns a dict"""
|
|
|
|
import csv
|
|
|
|
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
|
|
|
|
def updateByType(self):
|
|
from PySide import QtGui
|
|
|
|
groups = {}
|
|
for name, role in self.objectslist.items():
|
|
role = role[0]
|
|
obj = FreeCAD.ActiveDocument.getObject(name)
|
|
if obj:
|
|
if (
|
|
not self.form.onlyVisible.isChecked()
|
|
) or obj.ViewObject.isVisible():
|
|
groups.setdefault(role, []).append(name)
|
|
|
|
for group in groups.keys():
|
|
s1 = group + " (" + str(len(groups[group])) + ")"
|
|
top = QtGui.QStandardItem(s1)
|
|
self.model.appendRow([top, QtGui.QStandardItem(), QtGui.QStandardItem()])
|
|
for name in groups[group]:
|
|
obj = FreeCAD.ActiveDocument.getObject(name)
|
|
if obj:
|
|
it1 = QtGui.QStandardItem(obj.Label)
|
|
icon = obj.ViewObject.Icon
|
|
it1.setIcon(icon)
|
|
it1.setToolTip(obj.Name)
|
|
it2 = QtGui.QStandardItem(group)
|
|
if group != self.getRole(obj):
|
|
it2.setIcon(QtGui.QIcon(":/icons/edit-edit.svg"))
|
|
it3 = self.getSearchResults(obj)
|
|
if it3:
|
|
top.appendRow([it1, it2, it3])
|
|
top.sortChildren(0)
|
|
self.form.tree.expandAll()
|
|
self.spanTopLevels()
|
|
|
|
def updateByTree(self):
|
|
from PySide import QtGui
|
|
|
|
# order by hierarchy
|
|
def istop(obj):
|
|
for parent in obj.InListRecursive:
|
|
if parent.Name in self.objectslist.keys():
|
|
return False
|
|
return True
|
|
|
|
rel = []
|
|
deps = []
|
|
for name in self.objectslist.keys():
|
|
obj = FreeCAD.ActiveDocument.getObject(name)
|
|
if obj:
|
|
if istop(obj):
|
|
rel.append(obj)
|
|
else:
|
|
deps.append(obj)
|
|
pa = 1
|
|
while deps:
|
|
for obj in rel:
|
|
for child in obj.OutList:
|
|
if child in deps:
|
|
rel.append(child)
|
|
deps.remove(child)
|
|
pa += 1
|
|
if pa == 10: # max 10 hierarchy levels, okay? Let's keep civilised
|
|
rel.extend(deps)
|
|
break
|
|
|
|
done = {}
|
|
for obj in rel:
|
|
role = self.objectslist[obj.Name][0]
|
|
if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible():
|
|
it1 = QtGui.QStandardItem(obj.Label)
|
|
icon = obj.ViewObject.Icon
|
|
it1.setIcon(icon)
|
|
it1.setToolTip(obj.Name)
|
|
it2 = QtGui.QStandardItem(role)
|
|
if role != self.getRole(obj):
|
|
it2.setIcon(QtGui.QIcon(":/icons/edit-edit.svg"))
|
|
it3 = self.getSearchResults(obj)
|
|
ok = False
|
|
for par in obj.InListRecursive:
|
|
if par.Name in done:
|
|
if (not hasattr(par, "Hosts")) or (obj not in par.Hosts):
|
|
if it3:
|
|
done[par.Name].appendRow([it1, it2, it3])
|
|
done[obj.Name] = it1
|
|
ok = True
|
|
break
|
|
if not ok:
|
|
if it3:
|
|
self.model.appendRow([it1, it2, it3])
|
|
done[obj.Name] = it1
|
|
self.form.tree.expandAll()
|
|
|
|
def updateDefault(self):
|
|
from PySide import QtGui
|
|
|
|
for name, role in self.objectslist.items():
|
|
role = role[0]
|
|
obj = FreeCAD.ActiveDocument.getObject(name)
|
|
if obj:
|
|
if (
|
|
not self.form.onlyVisible.isChecked()
|
|
) or obj.ViewObject.isVisible():
|
|
it1 = QtGui.QStandardItem(obj.Label)
|
|
icon = obj.ViewObject.Icon
|
|
it1.setIcon(icon)
|
|
it1.setToolTip(obj.Name)
|
|
it2 = QtGui.QStandardItem(role)
|
|
if role != self.getRole(obj):
|
|
it2.setIcon(QtGui.QIcon(":/icons/edit-edit.svg"))
|
|
it3 = self.getSearchResults(obj)
|
|
if it3:
|
|
self.model.appendRow([it1, it2, it3])
|
|
|
|
def spanTopLevels(self):
|
|
if self.form.groupMode.currentIndex() in [1, 2]:
|
|
idx = self.model.invisibleRootItem().index()
|
|
for i in range(self.model.rowCount()):
|
|
if self.model.item(i, 0).hasChildren():
|
|
self.form.tree.setFirstColumnSpanned(i, idx, True)
|
|
|
|
def accept(self):
|
|
PARAMS.SetInt("BimIfcPropertiesDialogWidth", self.form.width())
|
|
PARAMS.SetInt("BimIfcPropertiesDialogHeight", self.form.height())
|
|
self.form.hide()
|
|
|
|
# print(self.objectslist)
|
|
changed = False
|
|
for key, values in self.objectslist.items():
|
|
obj = FreeCAD.ActiveDocument.getObject(key)
|
|
if obj:
|
|
if hasattr(obj, "IfcProperties"):
|
|
if not isinstance(obj.IfcProperties, dict):
|
|
FreeCAD.Console.PrintWarning(
|
|
translate(
|
|
"BIM",
|
|
"Warning: object %1 has old-styled IfcProperties and cannot be updated",
|
|
).replace("%1", obj.Label)
|
|
+ "\n"
|
|
)
|
|
continue
|
|
props = obj.IfcProperties
|
|
elif hasattr(obj, "IfcClass"):
|
|
props = self.getNativeIfcProperties(obj)
|
|
else:
|
|
props = {}
|
|
if values[1] != props:
|
|
if not changed:
|
|
FreeCAD.ActiveDocument.openTransaction("Change properties")
|
|
changed = True
|
|
if hasattr(obj,"IfcClass"):
|
|
print("props:",props)
|
|
for key,value in values[1].items():
|
|
if ";;" in key and ";;" in value:
|
|
pname, pset = key.split(";;")
|
|
ptype, pvalue = value.split(";;")
|
|
from nativeifc import ifc_psets # lazy loading
|
|
fctype = ifc_psets.get_freecad_type(ptype)
|
|
if not pname in obj.PropertiesList:
|
|
obj.addProperty(fctype, pname, pset, ptype+":"+pname)
|
|
ifc_psets.edit_pset(obj, pname, force=True)
|
|
if pvalue:
|
|
setattr(obj, pname, pvalue)
|
|
elif not hasattr(obj, "IfcProperties"):
|
|
obj.addProperty(
|
|
"App::PropertyMap",
|
|
"IfcPRoperties",
|
|
"IFC",
|
|
QT_TRANSLATE_NOOP(
|
|
"App::Property", "IFC properties of this object"
|
|
),
|
|
locked=True,
|
|
)
|
|
if hasattr(obj, "IfcProperties"):
|
|
obj.IfcProperties = values[1]
|
|
if changed:
|
|
FreeCAD.ActiveDocument.commitTransaction()
|
|
FreeCAD.ActiveDocument.recompute()
|
|
|
|
def getNativeIfcProperties(self, obj):
|
|
props = {}
|
|
for p in obj.PropertiesList:
|
|
pset = obj.getGroupOfProperty(p)
|
|
ttip = obj.getDocumentationOfProperty(p)
|
|
if ":" in ttip:
|
|
ptype, pname = ttip.split(":")
|
|
if pset not in ["Base", "IFC", "Geometry"]:
|
|
props[pname+";;"+pset] = ptype+";;"+str(getattr(obj,p))
|
|
return props
|
|
|
|
def getSearchResults(self, obj):
|
|
from PySide import QtGui
|
|
|
|
text = self.form.searchField.currentText()
|
|
if not text:
|
|
return QtGui.QStandardItem()
|
|
else:
|
|
if obj.Name in self.objectslist:
|
|
result = []
|
|
for key, value in self.objectslist[obj.Name][1].items():
|
|
if ";;" in key:
|
|
# 0.19 format
|
|
key = key.split(";;")
|
|
pset = key[1]
|
|
key = key[0]
|
|
else:
|
|
pset = value.split(";;")[0]
|
|
if text.lower() in key.lower():
|
|
if not key in result:
|
|
result.append(key)
|
|
if text.lower() in pset.lower():
|
|
if not pset in result:
|
|
result.append(pset)
|
|
if result:
|
|
return QtGui.QStandardItem(",".join(result))
|
|
else:
|
|
if self.form.onlyMatches.isChecked():
|
|
return None
|
|
else:
|
|
return QtGui.QStandardItem()
|
|
else:
|
|
return QtGui.QStandardItem()
|
|
|
|
def updateProperties(self, sel1=None, sel2=None):
|
|
from PySide import QtGui
|
|
|
|
self.propmodel.clear()
|
|
self.propmodel.setHorizontalHeaderLabels(
|
|
[
|
|
translate("Arch", "Property", None),
|
|
translate("Arch", "Type", None),
|
|
translate("Arch", "Value", None),
|
|
]
|
|
)
|
|
|
|
self.form.treeProperties.setColumnWidth(0, 300)
|
|
self.form.treeProperties.setColumnWidth(1, 200)
|
|
|
|
# gather common properties
|
|
allprops = []
|
|
sel = self.form.tree.selectedIndexes()
|
|
for index in sel:
|
|
if index.column() == 0:
|
|
name = self.model.itemFromIndex(index).toolTip()
|
|
if name in self.objectslist:
|
|
allprops.append(self.objectslist[name][1])
|
|
psets = {}
|
|
if allprops:
|
|
for key in allprops[0].keys():
|
|
value = allprops[0][key].split(";;")
|
|
iscommon = True
|
|
if len(value) == 3:
|
|
pset = value[0]
|
|
ptype = value[1]
|
|
pvalue = value[2]
|
|
elif (len(value) == 2) and (";;" in key): # 0.19 format
|
|
pset = key.split(";;")[1]
|
|
ptype = value[0]
|
|
pvalue = value[1]
|
|
elif len(value) == 2: # old system
|
|
pset = "Default property set"
|
|
ptype = value[0]
|
|
pvalue = value[1]
|
|
else:
|
|
print("Error: Unparsable property:", value)
|
|
return
|
|
for other in allprops[1:]:
|
|
if key in other.keys():
|
|
othervalue = other[key].split(";;")
|
|
if len(value) == 3:
|
|
otherpset = othervalue[0]
|
|
otherptype = othervalue[1]
|
|
otherpvalue = othervalue[2]
|
|
elif (len(value) == 2) and (";;" in key): # 0.19 format
|
|
otherpset = key.split(";;")[1]
|
|
otherptype = othervalue[0]
|
|
otherpvalue = othervalue[1]
|
|
elif len(value) == 2: # old system
|
|
otherpset = "Default property set"
|
|
otherptype = othervalue[0]
|
|
otherpvalue = othervalue[1]
|
|
else:
|
|
print("Error: Unparsable property:", othervalue)
|
|
return
|
|
if otherpset != pset:
|
|
iscommon = False
|
|
if otherptype != ptype:
|
|
iscommon = False
|
|
if otherpvalue != pvalue:
|
|
pvalue = "*VARIES*"
|
|
else:
|
|
iscommon = False
|
|
if iscommon:
|
|
plabel = ptype
|
|
if ptype in self.ptypes:
|
|
plabel = self.plabels[self.ptypes.index(ptype)]
|
|
psets.setdefault(pset, []).append([key, plabel, pvalue])
|
|
|
|
# fill the tree
|
|
for pset, plists in psets.items():
|
|
top = QtGui.QStandardItem(pset)
|
|
top.setDragEnabled(False)
|
|
top.setToolTip("PropertySet")
|
|
self.propmodel.appendRow(
|
|
[top, QtGui.QStandardItem(), QtGui.QStandardItem()]
|
|
)
|
|
for plist in plists:
|
|
pname = plist[0]
|
|
if ";;" in pname:
|
|
pname = pname.split(";;")[0]
|
|
it1 = QtGui.QStandardItem(pname)
|
|
it1.setDropEnabled(False)
|
|
it2 = QtGui.QStandardItem(plist[1])
|
|
it2.setDropEnabled(False)
|
|
it3 = QtGui.QStandardItem(plist[2])
|
|
it3.setDropEnabled(False)
|
|
top.appendRow([it1, it2, it3])
|
|
top.sortChildren(0)
|
|
# span top levels
|
|
idx = self.propmodel.invisibleRootItem().index()
|
|
for i in range(self.propmodel.rowCount()):
|
|
if self.propmodel.item(i, 0).hasChildren():
|
|
self.form.treeProperties.setFirstColumnSpanned(i, idx, True)
|
|
self.form.treeProperties.expandAll()
|
|
|
|
def updateDicts(self, remove=None):
|
|
# update the stored dicts to reflect the editor
|
|
|
|
sel = self.form.tree.selectedIndexes()
|
|
|
|
for row in range(self.propmodel.rowCount()):
|
|
pset = self.propmodel.item(row, 0).text()
|
|
if self.propmodel.item(row, 0).hasChildren():
|
|
for childrow in range(self.propmodel.item(row, 0).rowCount()):
|
|
prop = self.propmodel.item(row, 0).child(childrow, 0).text()
|
|
if ";;" in prop:
|
|
prop = prop.split(";;")[0]
|
|
ptype = self.propmodel.item(row, 0).child(childrow, 1).text()
|
|
if not ptype.startswith("Ifc"):
|
|
ptype = self.ptypes[self.plabels.index(ptype)]
|
|
pvalue = self.propmodel.item(row, 0).child(childrow, 2).text()
|
|
if (sys.version_info.major < 3) and isinstance(prop, unicode):
|
|
prop = prop.encode("utf8")
|
|
|
|
# update objects
|
|
for index in sel:
|
|
if index.column() == 0:
|
|
name = self.model.itemFromIndex(index).toolTip()
|
|
if name in self.objectslist:
|
|
# print("object",name,self.objectslist[name][1])
|
|
if pvalue == "*VARIES*":
|
|
if not (
|
|
prop + ";;" + pset in self.objectslist[name][1]
|
|
):
|
|
# print("adding",prop)
|
|
self.objectslist[name][1][
|
|
prop + ";;" + pset
|
|
] = (ptype + ";;")
|
|
else:
|
|
pval = ptype + ";;" + pvalue
|
|
if prop in self.objectslist[name][1]:
|
|
if (
|
|
self.objectslist[name][1][
|
|
prop + ";;" + pset
|
|
]
|
|
!= pval
|
|
):
|
|
# print("modifying",prop)
|
|
self.objectslist[name][1][
|
|
prop + ";;" + pset
|
|
] = pval
|
|
else:
|
|
# print("adding",prop)
|
|
self.objectslist[name][1][
|
|
prop + ";;" + pset
|
|
] = pval
|
|
if remove:
|
|
for index in sel:
|
|
if index.column() == 0:
|
|
name = self.model.itemFromIndex(index).toolTip()
|
|
if name in self.objectslist:
|
|
for prop in remove:
|
|
if prop in self.objectslist[name][1]:
|
|
# print("deleting",prop)
|
|
del self.objectslist[name][1][prop]
|
|
|
|
def addProperty(self, idx=0, pset=None, prop=None, ptype=None):
|
|
from PySide import QtGui
|
|
|
|
if not self.form.tree.selectedIndexes():
|
|
return
|
|
if not pset:
|
|
sel = self.form.treeProperties.selectedIndexes()
|
|
if sel:
|
|
item = self.propmodel.itemFromIndex(sel[0])
|
|
if item.toolTip() == "PropertySet":
|
|
pset = item
|
|
if pset:
|
|
if not prop:
|
|
basename = translate("Arch", "New property", None)
|
|
# check for duplicate name
|
|
names = []
|
|
for i in range(self.propmodel.rowCount()):
|
|
topitem = self.propmodel.item(i, 0)
|
|
names.append(topitem.text())
|
|
if topitem.hasChildren():
|
|
for j in range(topitem.rowCount()):
|
|
childitem = topitem.child(j, 0)
|
|
names.append(childitem.text())
|
|
suffix = 1
|
|
newname = basename
|
|
while newname in names:
|
|
newname = basename + str(suffix).zfill(3)
|
|
suffix += 1
|
|
prop = newname
|
|
if not ptype:
|
|
if idx > 0:
|
|
ptype = self.plabels[idx - 1]
|
|
if prop and ptype:
|
|
if ptype in self.ptypes:
|
|
ptype = self.plabels[self.ptypes.index(ptype)]
|
|
it1 = QtGui.QStandardItem(prop)
|
|
it1.setDropEnabled(False)
|
|
it2 = QtGui.QStandardItem(ptype)
|
|
it2.setDropEnabled(False)
|
|
it3 = QtGui.QStandardItem()
|
|
it3.setDropEnabled(False)
|
|
pset.appendRow([it1, it2, it3])
|
|
self.updateDicts()
|
|
else:
|
|
if idx != 0:
|
|
QtGui.QMessageBox.critical(
|
|
None,
|
|
"Error",
|
|
translate(
|
|
"BIM",
|
|
"Please select or create a property set first in which the new property should be placed.",
|
|
),
|
|
QtGui.QMessageBox.Ok,
|
|
)
|
|
if idx != 0:
|
|
self.form.comboProperty.setCurrentIndex(0)
|
|
|
|
def addPset(self, idx):
|
|
from PySide import QtGui
|
|
|
|
if not self.form.tree.selectedIndexes():
|
|
return
|
|
if idx == 1:
|
|
name = translate("Arch", "New property set", None)
|
|
res = QtGui.QInputDialog.getText(
|
|
None,
|
|
translate("BIM", "New property set"),
|
|
translate("BIM", "Property set name:"),
|
|
QtGui.QLineEdit.Normal,
|
|
name,
|
|
)
|
|
if res[1]:
|
|
name = res[0]
|
|
top = QtGui.QStandardItem(name)
|
|
top.setDragEnabled(False)
|
|
top.setToolTip("PropertySet")
|
|
self.propmodel.appendRow(
|
|
[top, QtGui.QStandardItem(), QtGui.QStandardItem()]
|
|
)
|
|
elif idx > 1:
|
|
psetlabel = self.psetkeys[idx - 2]
|
|
psetdef = "Pset_" + psetlabel.replace(" ", "")
|
|
if psetdef in self.psetdefs:
|
|
top = QtGui.QStandardItem(psetdef)
|
|
top.setDragEnabled(False)
|
|
top.setToolTip("PropertySet")
|
|
self.propmodel.appendRow(
|
|
[top, QtGui.QStandardItem(), QtGui.QStandardItem()]
|
|
)
|
|
for i in range(0, len(self.psetdefs[psetdef]), 2):
|
|
self.addProperty(
|
|
pset=top,
|
|
prop=self.psetdefs[psetdef][i],
|
|
ptype=self.psetdefs[psetdef][i + 1],
|
|
)
|
|
if idx != 0:
|
|
# span top levels
|
|
idx = self.propmodel.invisibleRootItem().index()
|
|
for i in range(self.propmodel.rowCount()):
|
|
if self.propmodel.item(i, 0).hasChildren():
|
|
self.form.treeProperties.setFirstColumnSpanned(i, idx, True)
|
|
self.form.treeProperties.expandAll()
|
|
self.form.comboPset.setCurrentIndex(0)
|
|
|
|
def removeProperty(self):
|
|
from PySide import QtGui
|
|
|
|
sel = self.form.treeProperties.selectedIndexes()
|
|
remove = []
|
|
if sel:
|
|
item = self.propmodel.itemFromIndex(sel[0])
|
|
if item.toolTip() == "PropertySet":
|
|
for i in range(item.rowCount()):
|
|
remove.append(item.child(i, 0).text() + ";;" + item.text())
|
|
self.propmodel.takeRow(sel[0].row())
|
|
else:
|
|
pset = self.propmodel.itemFromIndex(sel[0].parent())
|
|
itemtext = item.text()
|
|
if isinstance(pset, QtGui.QStandardItem):
|
|
itemtext += ";;" + pset.text()
|
|
remove.append(itemtext)
|
|
pset.takeRow(sel[0].row())
|
|
self.updateDicts(remove=remove)
|
|
|
|
def onSelected(self, index):
|
|
PARAMS.SetInt("IfcPropertiesSelectedState", getattr(index, "value", index))
|
|
self.objectslist, searchterms = self.rebuildObjectsList()
|
|
self.form.searchField.clear()
|
|
self.form.searchField.addItems(searchterms)
|
|
self.update()
|
|
|
|
def onVisible(self, index):
|
|
PARAMS.SetInt("IfcPropertiesVisibleState", getattr(index, "value", index))
|
|
self.update()
|
|
|
|
|
|
if FreeCAD.GuiUp:
|
|
from PySide import QtGui
|
|
|
|
class propertiesDelegate(QtGui.QStyledItemDelegate):
|
|
def __init__(self, parent=None, container=None, ptypes=[], plabels=[], *args):
|
|
self.container = container
|
|
QtGui.QStyledItemDelegate.__init__(self, parent, *args)
|
|
self.ptypes = ptypes
|
|
self.plabels = plabels
|
|
|
|
def createEditor(self, parent, option, index):
|
|
import FreeCADGui
|
|
|
|
if index.column() == 0: # property name
|
|
editor = QtGui.QLineEdit(parent)
|
|
elif index.column() == 1: # property type
|
|
editor = QtGui.QComboBox(parent)
|
|
else: # property value
|
|
ptype = index.sibling(index.row(), 1).data()
|
|
if "Integer" in ptype:
|
|
editor = QtGui.QSpinBox(parent)
|
|
elif "Real" in ptype:
|
|
editor = QtGui.QDoubleSpinBox(parent)
|
|
editor.setDecimals(
|
|
FreeCAD.ParamGet(
|
|
"User parameter:BaseApp/Preferences/Units"
|
|
).GetInt("Decimals", 2)
|
|
)
|
|
elif ("Boolean" in ptype) or ("Logical" in ptype):
|
|
editor = QtGui.QComboBox(parent)
|
|
editor.addItems(["True", "False"])
|
|
elif "Measure" in ptype:
|
|
editor = FreeCADGui.UiLoader().createWidget("Gui::InputField")
|
|
editor.setParent(parent)
|
|
else:
|
|
editor = QtGui.QLineEdit(parent)
|
|
editor.setObjectName("editor_" + ptype)
|
|
return editor
|
|
|
|
def setEditorData(self, editor, index):
|
|
if index.column() == 0:
|
|
editor.setText(index.data())
|
|
elif index.column() == 1:
|
|
editor.addItems(self.plabels)
|
|
if index.data() in self.plabels:
|
|
idx = self.plabels.index(index.data())
|
|
editor.setCurrentIndex(idx)
|
|
else:
|
|
if "Integer" in editor.objectName():
|
|
try:
|
|
editor.setValue(int(index.data()))
|
|
except (TypeError, ValueError, AttributeError):
|
|
editor.setValue(0)
|
|
elif "Real" in editor.objectName():
|
|
try:
|
|
editor.setValue(float(index.data()))
|
|
except (TypeError, ValueError, AttributeError):
|
|
editor.setValue(0)
|
|
elif ("Boolean" in editor.objectName()) or (
|
|
"Logical" in editor.objectName()
|
|
):
|
|
try:
|
|
editor.setCurrentIndex(
|
|
["true", "false"].index(index.data().lower())
|
|
)
|
|
except (ValueError, AttributeError):
|
|
editor.setCurrentIndex(1)
|
|
elif "Measure" in editor.objectName():
|
|
try:
|
|
editor.setText(index.data())
|
|
except (ValueError, AttributeError):
|
|
editor.setValue(0)
|
|
else:
|
|
editor.setText(index.data())
|
|
|
|
def setModelData(self, editor, model, index):
|
|
remove = []
|
|
if index.column() == 0:
|
|
oldtext = index.data()
|
|
if oldtext != editor.text():
|
|
pset = index.parent().data()
|
|
remove.append(oldtext + ";;" + pset)
|
|
basename = editor.text()
|
|
# check for duplicate name
|
|
names = []
|
|
for i in range(model.rowCount()):
|
|
topitem = model.item(i, 0)
|
|
names.append(topitem.text())
|
|
if topitem.hasChildren():
|
|
for j in range(topitem.rowCount()):
|
|
childitem = topitem.child(j, 0)
|
|
names.append(childitem.text())
|
|
suffix = 1
|
|
newname = basename
|
|
while newname in names:
|
|
newname = basename + str(suffix).zfill(3)
|
|
suffix += 1
|
|
model.setData(index, newname)
|
|
elif index.column() == 1:
|
|
if editor.currentIndex() > -1:
|
|
idx = editor.currentIndex()
|
|
data = self.plabels[idx]
|
|
model.setData(index, data)
|
|
else:
|
|
if ("Integer" in editor.objectName()) or (
|
|
"Real" in editor.objectName()
|
|
):
|
|
model.setData(index, str(editor.value()))
|
|
elif ("Boolean" in editor.objectName()) or (
|
|
"Logical" in editor.objectName()
|
|
):
|
|
model.setData(index, editor.currentText())
|
|
elif "Measure" in editor.objectName():
|
|
model.setData(index, editor.property("text"))
|
|
else:
|
|
model.setData(index, editor.text())
|
|
self.container.updateDicts(remove)
|
|
|
|
|
|
FreeCADGui.addCommand("BIM_IfcProperties", BIM_IfcProperties())
|