230 lines
9.1 KiB
Python
230 lines
9.1 KiB
Python
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2023 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 NativeIFC module handles the retrieval and display of geometry compositions of objects"""
|
|
|
|
|
|
import FreeCAD
|
|
|
|
TAB = 2
|
|
|
|
|
|
def get_geometry_tree(element, prefix=""):
|
|
"""Returns a list of elements representing an object's representation"""
|
|
|
|
result = [prefix + str(element)]
|
|
prefix += TAB * " "
|
|
if getattr(element, "Representation", None):
|
|
reps = element.Representation
|
|
result.append(prefix + str(reps))
|
|
prefix += prefix
|
|
for rep in reps.Representations:
|
|
result.extend(get_geometry_tree(rep, prefix))
|
|
elif getattr(element, "Items", None):
|
|
for it in element.Items:
|
|
result.extend(get_geometry_tree(it, prefix))
|
|
elif element.is_a("IfcPolyline"):
|
|
for p in element.Points:
|
|
result.append(prefix + str(p))
|
|
elif element.is_a("IfcExtrudedAreaSolid"):
|
|
result.append(prefix + str(element.ExtrudedDirection))
|
|
result.extend(get_geometry_tree(element.SweptArea, prefix))
|
|
elif element.is_a("IfcArbitraryClosedProfileDef"):
|
|
result.extend(get_geometry_tree(element.OuterCurve, prefix))
|
|
elif element.is_a("IfcArbitraryProfileDefWithVoids"):
|
|
result.extend(get_geometry_tree(element.OuterCurve, prefix))
|
|
for inn in element.InnerCurves:
|
|
result.extend(get_geometry_tree(inn, prefix))
|
|
elif element.is_a("IfcMappedItem"):
|
|
result.extend(get_geometry_tree(element.MappingSource[1], prefix))
|
|
elif element.is_a("IfcBooleanClippingResult"):
|
|
result.extend(get_geometry_tree(element.FirstOperand, prefix))
|
|
result.extend(get_geometry_tree(element.SecondOperand, prefix))
|
|
elif element.is_a("IfcBooleanResult"):
|
|
result.extend(get_geometry_tree(element.FirstOperand, prefix))
|
|
result.extend(get_geometry_tree(element.SecondOperand, prefix))
|
|
elif element.is_a("IfcHalfSpaceSolid"):
|
|
result.extend(get_geometry_tree(element.BaseSurface, prefix))
|
|
return result
|
|
|
|
|
|
def print_geometry_tree(element):
|
|
"""Same as get_geometry_tree but printed"""
|
|
|
|
for line in get_geometry_tree(element):
|
|
print(line)
|
|
|
|
|
|
def show_geometry_tree(element):
|
|
"""Same as get_geometry_tree but in a Qt dialog"""
|
|
|
|
import Arch_rc
|
|
import FreeCADGui # lazy import
|
|
from nativeifc import ifc_tools
|
|
from PySide import QtGui, QtWidgets
|
|
|
|
if isinstance(element, FreeCAD.DocumentObject):
|
|
element = ifc_tools.get_ifc_element(element)
|
|
if not element:
|
|
return
|
|
dlg = FreeCADGui.PySideUic.loadUi(":/ui/dialogTree.ui")
|
|
tops = {}
|
|
for line in get_geometry_tree(element):
|
|
psize = (len(line) - len(line.lstrip())) / TAB
|
|
wline = QtWidgets.QTreeWidgetItem([line.lstrip()])
|
|
if not psize:
|
|
dlg.geomtree.addTopLevelItem(wline)
|
|
else:
|
|
parent = tops[psize - 1]
|
|
parent.addChild(wline)
|
|
tops[psize] = wline
|
|
dlg.geomtree.expandAll()
|
|
dlg.setWindowTitle(dlg.windowTitle() + " " + element.Name)
|
|
dlg.geomtree.currentItemChanged.connect(show_properties)
|
|
dlg.proptree.itemChanged.connect(edit_property)
|
|
result = dlg.exec_()
|
|
if result:
|
|
props = [r.split("::") for r in dlg.proptree.toolTip().split(";;;;") if r]
|
|
if not props:
|
|
return
|
|
obj = ifc_tools.get_object(element)
|
|
ifcfile = ifc_tools.get_ifcfile(obj)
|
|
modified = False
|
|
for prop in props:
|
|
elt = ifcfile[int(prop[0])]
|
|
attrib = prop[1]
|
|
if attrib not in dir(elt):
|
|
print("DEBUG: Unknown attribute:", attrib)
|
|
continue
|
|
value = prop[3]
|
|
if isfloat(getattr(elt, attrib)):
|
|
try:
|
|
value = float(value)
|
|
except:
|
|
print("DEBUG: wrong value for", attrib, ":", value)
|
|
continue
|
|
ifc_tools.set_attribute(ifcfile, elt, attrib, value)
|
|
modified = True
|
|
if modified:
|
|
obj.touch()
|
|
proj = ifc_tools.get_project(obj)
|
|
proj.Modified = True
|
|
obj.Document.recompute()
|
|
|
|
|
|
def isfloat(s):
|
|
"""Tells if the given string is a number"""
|
|
|
|
try:
|
|
float(s)
|
|
except ValueError:
|
|
return False
|
|
print(s)
|
|
return True
|
|
|
|
|
|
def show_properties(current, previous):
|
|
"""Displays object properties"""
|
|
|
|
import FreeCADGui
|
|
from nativeifc import ifc_tools # lazy loading
|
|
from PySide import QtCore, QtGui, QtWidgets
|
|
|
|
ifcid = int(current.text(0).split("=", 1)[0].strip(" ").strip("#"))
|
|
sel = FreeCADGui.Selection.getSelection()
|
|
if len(sel) != 1:
|
|
return
|
|
obj = sel[0]
|
|
ifcfile = ifc_tools.get_ifcfile(obj)
|
|
if not ifcfile:
|
|
return
|
|
elt = ifcfile[ifcid]
|
|
box = current.treeWidget().parentWidget().widget(1)
|
|
box.setTitle("#" + str(ifcid) + ": " + elt.is_a())
|
|
props = [p for p in dir(elt) if p[0].isupper()]
|
|
# allow only numeric values?
|
|
# props = [p for p in props if isfloat(str(getattr(elt,p)))]
|
|
props = [p for p in props if not str(getattr(elt, p)).startswith("#")]
|
|
props = [p for p in props if not str(getattr(elt, p)).startswith("(")]
|
|
props = [
|
|
p for p in props if p not in ["Position", "LayerAssignments", "StyledByItem"]
|
|
]
|
|
proptree = box.children()[0].itemAt(0).widget()
|
|
proptree.clear()
|
|
proptree.setHorizontalHeaderLabels(["Property", "Value"])
|
|
proptree.setRowCount(len(props))
|
|
for i, prop in enumerate(props):
|
|
value = str(getattr(elt, prop))
|
|
if value == "None":
|
|
value = ""
|
|
r1 = QtWidgets.QTableWidgetItem(prop)
|
|
r1.setFlags(QtCore.Qt.ItemIsSelectable)
|
|
proptree.setItem(i, 0, r1)
|
|
r2 = QtWidgets.QTableWidgetItem(value)
|
|
r2.setToolTip(value)
|
|
if value.startswith("#") or value.startswith("(") or prop == "GlobalId":
|
|
r2.setFlags(QtCore.Qt.ItemIsSelectable)
|
|
proptree.setItem(i, 1, r2)
|
|
if "Position" in dir(elt):
|
|
i = proptree.rowCount()
|
|
proptree.setRowCount(i + 6)
|
|
position = FreeCAD.Vector(elt.Position.Location.Coordinates)
|
|
axis = FreeCAD.Vector(elt.Position.Axis.DirectionRatios)
|
|
xref = FreeCAD.Vector(elt.Position.RefDirection.DirectionRatios)
|
|
rotation = FreeCAD.Rotation(axis, xref, FreeCAD.Vector(), "ZXY").toEulerAngles(
|
|
"XYZ"
|
|
)
|
|
rotation = FreeCAD.Vector(rotation)
|
|
for c in ["x", "y", "z"]:
|
|
r1 = QtWidgets.QTableWidgetItem("Position " + c.upper())
|
|
r1.setFlags(QtCore.Qt.ItemIsSelectable)
|
|
proptree.setItem(i, 0, r1)
|
|
v = str(getattr(position, c))
|
|
r2 = QtWidgets.QTableWidgetItem(v)
|
|
r2.setToolTip(v)
|
|
proptree.setItem(i, 1, r2)
|
|
i += 1
|
|
for c in ["x", "y", "z"]:
|
|
r1 = QtWidgets.QTableWidgetItem("Rotation " + c.upper())
|
|
r1.setFlags(QtCore.Qt.ItemIsSelectable)
|
|
proptree.setItem(i, 0, r1)
|
|
v = str(getattr(rotation, c))
|
|
r2 = QtWidgets.QTableWidgetItem(v)
|
|
r2.setToolTip(v)
|
|
proptree.setItem(i, 1, r2)
|
|
i += 1
|
|
|
|
|
|
def edit_property(item):
|
|
"""Edits the value of a property"""
|
|
|
|
if item.toolTip() and (item.text() != item.toolTip()):
|
|
old = item.toolTip()
|
|
new = item.text()
|
|
table = item.tableWidget()
|
|
prop = table.item(item.row(), 0).text()
|
|
dlg = table.parent().parent().parent()
|
|
line = dlg.geomtree.currentItem().text(0)
|
|
ifcid = line.split("=", 1)[0].strip(" ").strip("#")
|
|
strid = ";;;;" + "::".join([str(i) for i in [ifcid, prop, old, new]])
|
|
dlg.proptree.setToolTip(dlg.proptree.toolTip() + strid)
|