BIM: Support for property sets in Native IFC (#18067)

* BIM: UI to add properties and psets to IFC objects

* BIM: Support native IFC objects in BimProperties

* BIM: Support removing IFC properties

* BIM: Fixed lint issues
This commit is contained in:
Yorik van Havre
2024-12-03 16:08:27 +01:00
committed by GitHub
parent 6a92b77632
commit 41ca58bf7c
11 changed files with 869 additions and 91 deletions

View File

@@ -116,6 +116,8 @@ def setStatusIcons(show=True):
if show:
if statuswidget:
statuswidget.show()
if hasattr(statuswidget, "propertybuttons"):
statuswidget.propertybuttons.show()
else:
statuswidget = FreeCADGui.UiLoader().createWidget("Gui::ToolBar")
statuswidget.setObjectName("BIMStatusWidget")
@@ -221,3 +223,5 @@ def setStatusIcons(show=True):
if statuswidget:
statuswidget.hide()
statuswidget.toggleViewAction().setVisible(False)
if hasattr(statuswidget, "propertybuttons"):
statuswidget.propertybuttons.hide()

View File

@@ -82,6 +82,7 @@ SET(Arch_presets
Presets/ifc_types_IFC4.json
Presets/ifc_contexts_IFC2X3.json
Presets/ifc_contexts_IFC4.json
Presets/properties_conversion.csv
)
SET(bimcommands_SRCS

View File

@@ -0,0 +1,230 @@
FreeCAD,IFC
App::PropertyBool,IfcBoolean:IfcLogical
App::PropertyBoolList,
App::PropertyFloat,IfcReal
App::PropertyFloatList,
App::PropertyFloatConstraint,
App::PropertyPrecision,
App::PropertyQuantity,
App::PropertyQuantityConstraint,
App::PropertyInteger,IfcInteger
App::PropertyIntegerConstraint,
App::PropertyPercent,
App::PropertyEnumeration,
App::PropertyIntegerList,
App::PropertyIntegerSet,
App::PropertyMap,
App::PropertyString,IfcLabel:IfcIdentifier:IfcText
App::PropertyPersistentObject,
App::PropertyUUID,IfcGloballyUniqueId
App::PropertyFont,IfcTextFontName
App::PropertyStringList,
App::PropertyLink,
App::PropertyLinkChild,
App::PropertyLinkGlobal,
App::PropertyLinkHidden,
App::PropertyLinkSub,
App::PropertyLinkSubChild,
App::PropertyLinkSubGlobal,
App::PropertyLinkSubHidden,
App::PropertyLinkList,
App::PropertyLinkListChild,
App::PropertyLinkListGlobal,
App::PropertyLinkListHidden,
App::PropertyLinkSubList,
App::PropertyLinkSubListChild,
App::PropertyLinkSubListGlobal,
App::PropertyLinkSubListHidden,
App::PropertyXLink,
App::PropertyXLinkSub,
App::PropertyXLinkSubHidden,
App::PropertyXLinkSubList,
App::PropertyXLinkList,
App::PropertyMatrix,
App::PropertyVector,
App::PropertyVectorDistance,
App::PropertyPosition,
App::PropertyDirection,
App::PropertyVectorList,
App::PropertyPlacement,
App::PropertyPlacementList,
App::PropertyPlacementLink,
App::PropertyRotation,
App::PropertyColor,
App::PropertyColorList,
App::PropertyMaterial,
App::PropertyMaterialList,
App::PropertyPath,
App::PropertyFile,
App::PropertyFileIncluded,
App::PropertyPythonObject,
App::PropertyExpressionEngine,
App::PropertyAcceleration,
App::PropertyAmountOfSubstance,
App::PropertyAngle,IfcPlaneAngleMeasure
App::PropertyArea,IfcAreaMeasure
App::PropertyCompressiveStrength,
App::PropertyCurrentDensity,
App::PropertyDensity,
App::PropertyDissipationRate,
App::PropertyDistance,IfcLengthMeasure
App::PropertyDynamicViscosity,
App::PropertyElectricalCapacitance,
App::PropertyElectricalConductance,
App::PropertyElectricalConductivity,
App::PropertyElectricalInductance,
App::PropertyElectricalResistance,
App::PropertyElectricCharge,
App::PropertyElectricCurrent,
App::PropertyElectricPotential,
App::PropertyFrequency,IfcFrequencyMeasure
App::PropertyForce,IfcForceMeasure
App::PropertyHeatFlux,IfcHeatFluxDensityMeasure
App::PropertyInverseArea,
App::PropertyInverseLength,
App::PropertyInverseVolume,
App::PropertyKinematicViscosity,
App::PropertyLength,IfcPositiveLengthMeasure
App::PropertyLuminousIntensity,
App::PropertyMagneticFieldStrength,
App::PropertyMagneticFlux,
App::PropertyMagneticFluxDensity,
App::PropertyMagnetization,
App::PropertyMass,IfcMassMeasure
App::PropertyMoment,IfcMomentOfInertiaMeasure
App::PropertyPressure,IfcPressureMeasure
App::PropertyPower,IfcPowerMeasure
App::PropertyShearModulus,IfcShearModulusMeasure
App::PropertySpecificEnergy,
App::PropertySpecificHeat,
App::PropertySpeed,
App::PropertyStiffness,
App::PropertyStiffnessDensity,
App::PropertyStress,
App::PropertyTemperature,IfcThermodynamicTemperatureMeasure
App::PropertyThermalConductivity,IfcThermalConductivityMeasure
App::PropertyThermalExpansionCoefficient,IfcThermalExpansionCoefficientMeasure
App::PropertyThermalTransferCoefficient,IfcThermalTransmittanceMeasure
App::PropertyTime,IfcDateTime:IfcDate:IfcTime:IfcTimeMeasure:IfcTimeStamp
App::PropertyUltimateTensileStrength,
App::PropertyVacuumPermittivity,
App::PropertyVelocity,
App::PropertyVolume,IfcVolumeMeasure
App::PropertyVolumeFlowRate,
App::PropertyVolumetricThermalExpansionCoefficient,
App::PropertyWork,
App::PropertyYieldStrength,
App::PropertyYoungsModulus,
Materials::PropertyMaterial,
Part::PropertyPartShape,
Part::PropertyGeometryList,
Part::PropertyShapeHistory,
Part::PropertyFilletEdges,
Part::PropertyShapeCache,
Part::PropertyTopoShapeList,
Sketcher::PropertyConstraintList,
,IfcBSplineCurveForm
,IfcBSplineSurfaceForm
,IfcBooleanOperator
,IfcKnotType
,IfcNullStyle
,IfcPreferredSurfaceCurveRepresentation
,IfcSIPrefix
,IfcSIUnitName
,IfcSurfaceSide
,IfcTextPath
,IfcTransitionCode
,IfcTrimmingPreference
,IfcAbsorbedDoseMeasure
,IfcAccelerationMeasure
,IfcAmountOfSubstanceMeasure
,IfcAngularVelocityMeasure
,IfcAreaDensityMeasure
,IfcBoxAlignment
,IfcCardinalPointReference
,IfcContextDependentMeasure
,IfcCountMeasure
,IfcCurvatureMeasure
,IfcDayInMonthNumber
,IfcDayInWeekNumber
,IfcDescriptiveMeasure
,IfcDimensionCount
,IfcDoseEquivalentMeasure
,IfcDuration
,IfcDynamicViscosityMeasure
,IfcElectricCapacitanceMeasure
,IfcElectricChargeMeasure
,IfcElectricConductanceMeasure
,IfcElectricCurrentMeasure
,IfcElectricResistanceMeasure
,IfcElectricVoltageMeasure
,IfcEnergyMeasure
,IfcFontStyle
,IfcFontVariant
,IfcFontWeight
,IfcHeatingValueMeasure
,IfcIlluminanceMeasure
,IfcInductanceMeasure
,IfcIntegerCountRateMeasure
,IfcIonConcentrationMeasure
,IfcIsothermalMoistureCapacityMeasure
,IfcKinematicViscosityMeasure
,IfcLanguageId
,IfcLinearForceMeasure
,IfcLinearMomentMeasure
,IfcLinearStiffnessMeasure
,IfcLinearVelocityMeasure
,IfcLuminousFluxMeasure
,IfcLuminousIntensityDistributionMeasure
,IfcLuminousIntensityMeasure
,IfcMagneticFluxDensityMeasure
,IfcMagneticFluxMeasure
,IfcMassDensityMeasure
,IfcMassFlowRateMeasure
,IfcMassPerLengthMeasure
,IfcModulusOfElasticityMeasure
,IfcModulusOfLinearSubgradeReactionMeasure
,IfcModulusOfRotationalSubgradeReactionMeasure
,IfcModulusOfSubgradeReactionMeasure
,IfcMoistureDiffusivityMeasure
,IfcMolecularWeightMeasure
,IfcMonetaryMeasure
,IfcMonthInYearNumber
,IfcNonNegativeLengthMeasure
,IfcNormalisedRatioMeasure
,IfcNumericMeasure
,IfcPHMeasure
,IfcParameterValue
,IfcPlanarForceMeasure
,IfcPositiveInteger
,IfcPositivePlaneAngleMeasure
,IfcPositiveRatioMeasure
,IfcPresentableText
,IfcRadioActivityMeasure
,IfcRatioMeasure
,IfcRotationalFrequencyMeasure
,IfcRotationalMassMeasure
,IfcRotationalStiffnessMeasure
,IfcSectionModulusMeasure
,IfcSectionalAreaIntegralMeasure
,IfcSolidAngleMeasure
,IfcSoundPowerLevelMeasure
,IfcSoundPowerMeasure
,IfcSoundPressureLevelMeasure
,IfcSoundPressureMeasure
,IfcSpecificHeatCapacityMeasure
,IfcSpecularExponent
,IfcSpecularRoughness
,IfcTemperatureGradientMeasure
,IfcTemperatureRateOfChangeMeasure
,IfcTextAlignment
,IfcTextDecoration
,IfcTextTransformation
,IfcThermalAdmittanceMeasure
,IfcThermalResistanceMeasure
,IfcTorqueMeasure
,IfcURIReference
,IfcVaporPermeabilityMeasure
,IfcVolumetricFlowRateMeasure
,IfcWarpingConstantMeasure
,IfcWarpingMomentMeasure
1 FreeCAD IFC
2 App::PropertyBool IfcBoolean:IfcLogical
3 App::PropertyBoolList
4 App::PropertyFloat IfcReal
5 App::PropertyFloatList
6 App::PropertyFloatConstraint
7 App::PropertyPrecision
8 App::PropertyQuantity
9 App::PropertyQuantityConstraint
10 App::PropertyInteger IfcInteger
11 App::PropertyIntegerConstraint
12 App::PropertyPercent
13 App::PropertyEnumeration
14 App::PropertyIntegerList
15 App::PropertyIntegerSet
16 App::PropertyMap
17 App::PropertyString IfcLabel:IfcIdentifier:IfcText
18 App::PropertyPersistentObject
19 App::PropertyUUID IfcGloballyUniqueId
20 App::PropertyFont IfcTextFontName
21 App::PropertyStringList
22 App::PropertyLink
23 App::PropertyLinkChild
24 App::PropertyLinkGlobal
25 App::PropertyLinkHidden
26 App::PropertyLinkSub
27 App::PropertyLinkSubChild
28 App::PropertyLinkSubGlobal
29 App::PropertyLinkSubHidden
30 App::PropertyLinkList
31 App::PropertyLinkListChild
32 App::PropertyLinkListGlobal
33 App::PropertyLinkListHidden
34 App::PropertyLinkSubList
35 App::PropertyLinkSubListChild
36 App::PropertyLinkSubListGlobal
37 App::PropertyLinkSubListHidden
38 App::PropertyXLink
39 App::PropertyXLinkSub
40 App::PropertyXLinkSubHidden
41 App::PropertyXLinkSubList
42 App::PropertyXLinkList
43 App::PropertyMatrix
44 App::PropertyVector
45 App::PropertyVectorDistance
46 App::PropertyPosition
47 App::PropertyDirection
48 App::PropertyVectorList
49 App::PropertyPlacement
50 App::PropertyPlacementList
51 App::PropertyPlacementLink
52 App::PropertyRotation
53 App::PropertyColor
54 App::PropertyColorList
55 App::PropertyMaterial
56 App::PropertyMaterialList
57 App::PropertyPath
58 App::PropertyFile
59 App::PropertyFileIncluded
60 App::PropertyPythonObject
61 App::PropertyExpressionEngine
62 App::PropertyAcceleration
63 App::PropertyAmountOfSubstance
64 App::PropertyAngle IfcPlaneAngleMeasure
65 App::PropertyArea IfcAreaMeasure
66 App::PropertyCompressiveStrength
67 App::PropertyCurrentDensity
68 App::PropertyDensity
69 App::PropertyDissipationRate
70 App::PropertyDistance IfcLengthMeasure
71 App::PropertyDynamicViscosity
72 App::PropertyElectricalCapacitance
73 App::PropertyElectricalConductance
74 App::PropertyElectricalConductivity
75 App::PropertyElectricalInductance
76 App::PropertyElectricalResistance
77 App::PropertyElectricCharge
78 App::PropertyElectricCurrent
79 App::PropertyElectricPotential
80 App::PropertyFrequency IfcFrequencyMeasure
81 App::PropertyForce IfcForceMeasure
82 App::PropertyHeatFlux IfcHeatFluxDensityMeasure
83 App::PropertyInverseArea
84 App::PropertyInverseLength
85 App::PropertyInverseVolume
86 App::PropertyKinematicViscosity
87 App::PropertyLength IfcPositiveLengthMeasure
88 App::PropertyLuminousIntensity
89 App::PropertyMagneticFieldStrength
90 App::PropertyMagneticFlux
91 App::PropertyMagneticFluxDensity
92 App::PropertyMagnetization
93 App::PropertyMass IfcMassMeasure
94 App::PropertyMoment IfcMomentOfInertiaMeasure
95 App::PropertyPressure IfcPressureMeasure
96 App::PropertyPower IfcPowerMeasure
97 App::PropertyShearModulus IfcShearModulusMeasure
98 App::PropertySpecificEnergy
99 App::PropertySpecificHeat
100 App::PropertySpeed
101 App::PropertyStiffness
102 App::PropertyStiffnessDensity
103 App::PropertyStress
104 App::PropertyTemperature IfcThermodynamicTemperatureMeasure
105 App::PropertyThermalConductivity IfcThermalConductivityMeasure
106 App::PropertyThermalExpansionCoefficient IfcThermalExpansionCoefficientMeasure
107 App::PropertyThermalTransferCoefficient IfcThermalTransmittanceMeasure
108 App::PropertyTime IfcDateTime:IfcDate:IfcTime:IfcTimeMeasure:IfcTimeStamp
109 App::PropertyUltimateTensileStrength
110 App::PropertyVacuumPermittivity
111 App::PropertyVelocity
112 App::PropertyVolume IfcVolumeMeasure
113 App::PropertyVolumeFlowRate
114 App::PropertyVolumetricThermalExpansionCoefficient
115 App::PropertyWork
116 App::PropertyYieldStrength
117 App::PropertyYoungsModulus
118 Materials::PropertyMaterial
119 Part::PropertyPartShape
120 Part::PropertyGeometryList
121 Part::PropertyShapeHistory
122 Part::PropertyFilletEdges
123 Part::PropertyShapeCache
124 Part::PropertyTopoShapeList
125 Sketcher::PropertyConstraintList
126 IfcBSplineCurveForm
127 IfcBSplineSurfaceForm
128 IfcBooleanOperator
129 IfcKnotType
130 IfcNullStyle
131 IfcPreferredSurfaceCurveRepresentation
132 IfcSIPrefix
133 IfcSIUnitName
134 IfcSurfaceSide
135 IfcTextPath
136 IfcTransitionCode
137 IfcTrimmingPreference
138 IfcAbsorbedDoseMeasure
139 IfcAccelerationMeasure
140 IfcAmountOfSubstanceMeasure
141 IfcAngularVelocityMeasure
142 IfcAreaDensityMeasure
143 IfcBoxAlignment
144 IfcCardinalPointReference
145 IfcContextDependentMeasure
146 IfcCountMeasure
147 IfcCurvatureMeasure
148 IfcDayInMonthNumber
149 IfcDayInWeekNumber
150 IfcDescriptiveMeasure
151 IfcDimensionCount
152 IfcDoseEquivalentMeasure
153 IfcDuration
154 IfcDynamicViscosityMeasure
155 IfcElectricCapacitanceMeasure
156 IfcElectricChargeMeasure
157 IfcElectricConductanceMeasure
158 IfcElectricCurrentMeasure
159 IfcElectricResistanceMeasure
160 IfcElectricVoltageMeasure
161 IfcEnergyMeasure
162 IfcFontStyle
163 IfcFontVariant
164 IfcFontWeight
165 IfcHeatingValueMeasure
166 IfcIlluminanceMeasure
167 IfcInductanceMeasure
168 IfcIntegerCountRateMeasure
169 IfcIonConcentrationMeasure
170 IfcIsothermalMoistureCapacityMeasure
171 IfcKinematicViscosityMeasure
172 IfcLanguageId
173 IfcLinearForceMeasure
174 IfcLinearMomentMeasure
175 IfcLinearStiffnessMeasure
176 IfcLinearVelocityMeasure
177 IfcLuminousFluxMeasure
178 IfcLuminousIntensityDistributionMeasure
179 IfcLuminousIntensityMeasure
180 IfcMagneticFluxDensityMeasure
181 IfcMagneticFluxMeasure
182 IfcMassDensityMeasure
183 IfcMassFlowRateMeasure
184 IfcMassPerLengthMeasure
185 IfcModulusOfElasticityMeasure
186 IfcModulusOfLinearSubgradeReactionMeasure
187 IfcModulusOfRotationalSubgradeReactionMeasure
188 IfcModulusOfSubgradeReactionMeasure
189 IfcMoistureDiffusivityMeasure
190 IfcMolecularWeightMeasure
191 IfcMonetaryMeasure
192 IfcMonthInYearNumber
193 IfcNonNegativeLengthMeasure
194 IfcNormalisedRatioMeasure
195 IfcNumericMeasure
196 IfcPHMeasure
197 IfcParameterValue
198 IfcPlanarForceMeasure
199 IfcPositiveInteger
200 IfcPositivePlaneAngleMeasure
201 IfcPositiveRatioMeasure
202 IfcPresentableText
203 IfcRadioActivityMeasure
204 IfcRatioMeasure
205 IfcRotationalFrequencyMeasure
206 IfcRotationalMassMeasure
207 IfcRotationalStiffnessMeasure
208 IfcSectionModulusMeasure
209 IfcSectionalAreaIntegralMeasure
210 IfcSolidAngleMeasure
211 IfcSoundPowerLevelMeasure
212 IfcSoundPowerMeasure
213 IfcSoundPressureLevelMeasure
214 IfcSoundPressureMeasure
215 IfcSpecificHeatCapacityMeasure
216 IfcSpecularExponent
217 IfcSpecularRoughness
218 IfcTemperatureGradientMeasure
219 IfcTemperatureRateOfChangeMeasure
220 IfcTextAlignment
221 IfcTextDecoration
222 IfcTextTransformation
223 IfcThermalAdmittanceMeasure
224 IfcThermalResistanceMeasure
225 IfcTorqueMeasure
226 IfcURIReference
227 IfcVaporPermeabilityMeasure
228 IfcVolumetricFlowRateMeasure
229 IfcWarpingConstantMeasure
230 IfcWarpingMomentMeasure

View File

@@ -185,6 +185,8 @@
<file>ui/ParametersWindowFixed.svg</file>
<file>ui/ParametersWindowSimple.svg</file>
<file>ui/ParametersWindowStash.svg</file>
<file>ui/dialogAddPSet.ui</file>
<file>ui/dialogAddProperty.ui</file>
<file>ui/dialogClasses.ui</file>
<file>ui/dialogClassification.ui</file>
<file>ui/dialogConvertDocument.ui</file>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>310</width>
<height>122</height>
</rect>
</property>
<property name="windowTitle">
<string>Add standard IFC PSet</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="field_pset">
<property name="enabled">
<bool>true</bool>
</property>
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>338</width>
<height>217</height>
</rect>
</property>
<property name="windowTitle">
<string>Add IFC property</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="field_type">
<item>
<property name="text">
<string>IfcLabel</string>
</property>
</item>
<item>
<property name="text">
<string>IfcBoolean</string>
</property>
</item>
<item>
<property name="text">
<string>IfcInteger</string>
</property>
</item>
<item>
<property name="text">
<string>IfcReal</string>
</property>
</item>
<item>
<property name="text">
<string>IfcLengthMeasure</string>
</property>
</item>
<item>
<property name="text">
<string>IfcAreaMeasure</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="field_name"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>PSet</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="field_pset">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -175,6 +175,8 @@ class BIM_IfcProperties:
obj.IfcProperties, dict
):
props = obj.IfcProperties
elif hasattr(obj, "IfcClass"):
props = self.getNativeIfcProperties(obj)
else:
props = {}
objectslist[obj.Name] = [role, props]
@@ -182,6 +184,7 @@ class BIM_IfcProperties:
val = val.split(";;")
if ";;" in key:
# 0.19 format
# pset;;pname = ptype;;pvalue
key = key.split(";;")
val = [key[1]] + val
key = key[0]
@@ -222,6 +225,8 @@ class BIM_IfcProperties:
return obj.IfcType
elif hasattr(obj, "IfcRole"):
return obj.IfcRole
elif hasattr(obj, "IfcClass"):
return obj.IfcClass
else:
return None
@@ -259,14 +264,7 @@ class BIM_IfcProperties:
obj = FreeCAD.ActiveDocument.getObject(name)
if obj:
it1 = QtGui.QStandardItem(obj.Label)
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")
icon = obj.ViewObject.Icon
it1.setIcon(icon)
it1.setToolTip(obj.Name)
it2 = QtGui.QStandardItem(group)
@@ -315,12 +313,7 @@ class BIM_IfcProperties:
role = self.objectslist[obj.Name][0]
if (not self.form.onlyVisible.isChecked()) or obj.ViewObject.isVisible():
it1 = QtGui.QStandardItem(obj.Label)
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")
icon = obj.ViewObject.Icon
it1.setIcon(icon)
it1.setToolTip(obj.Name)
it2 = QtGui.QStandardItem(role)
@@ -353,14 +346,7 @@ class BIM_IfcProperties:
not self.form.onlyVisible.isChecked()
) or obj.ViewObject.isVisible():
it1 = QtGui.QStandardItem(obj.Label)
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")
icon = obj.ViewObject.Icon
it1.setIcon(icon)
it1.setToolTip(obj.Name)
it2 = QtGui.QStandardItem(role)
@@ -398,13 +384,28 @@ class BIM_IfcProperties:
)
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 not hasattr(obj, "IfcProperties"):
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",
@@ -413,11 +414,23 @@ class BIM_IfcProperties:
"App::Property", "IFC properties of this object"
),
)
obj.IfcProperties = values[1]
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 QtCore, QtGui

View File

@@ -31,7 +31,7 @@ params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
def add_observer():
"""Adds an observer to the running FreeCAD instance"""
"""Adds this observer to the running FreeCAD instance"""
FreeCAD.BIMobserver = ifc_observer()
FreeCAD.addDocumentObserver(FreeCAD.BIMobserver)
@@ -123,6 +123,11 @@ class ifc_observer:
ifc_status.on_activate()
def slotRemoveDynamicProperty(self, obj, prop):
from nativeifc import ifc_psets
ifc_psets.remove_property(obj, prop)
# implementation methods
def fit_all(self):

View File

@@ -23,10 +23,13 @@
"""This NativeIFC module deals with properties and property sets"""
import os
import re
import FreeCAD
from nativeifc import ifc_tools
translate = FreeCAD.Qt.translate
def has_psets(obj):
"""Returns True if an object has attached psets"""
@@ -148,75 +151,117 @@ def show_psets(obj):
setattr(obj, pname, value)
def edit_pset(obj, prop, value=None):
"""Edits the corresponding property"""
def edit_pset(obj, prop, value=None, force=False):
"""Edits the corresponding property. If force is True,
the property is created even if it has no value"""
pset = obj.getGroupOfProperty(prop)
ttip = obj.getDocumentationOfProperty(prop)
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)
pset_exist = get_psets(element)
if ttip.startswith("Ifc") and ":" in ttip:
target_prop = ttip.split(":", 1)[-1]
target_prop = None
value_exist = None
# build prop name and type
if ptype.startswith("Ifc"):
if ":" in ptype:
target_prop = ptype.split(":", 1)[-1]
ptype = ptype.split(":", 1)[0]
else:
# no tooltip set - try to build a name
ptype = obj.getTypeIdOfProperty(prop)
if ifcprop == "App::PropertyDistance":
ptype = "IfcLengthMeasure"
elif ifcprop == "App::PropertyLength":
ptype = "IfcPositiveLengthMeasure"
elif ifcprop == "App::PropertyBool":
ptype = "IfcBoolean"
elif ifcprop == "App::PropertyInteger":
ptype = "IfcInteger"
elif ifcprop == "App::PropertyFloat":
ptype = "IfcReal"
elif ifcprop == "App::PropertyArea":
ptype = "IfcAreaMeasure"
else:
# default
ptype = "IfcLabel"
if not target_prop:
# test if the prop exists under different forms (uncameled, unslashed...)
prop = prop.rstrip("_")
prop_uncamel = re.sub(r"(\w)([A-Z])", r"\1 \2", prop)
prop_unslash = re.sub(r"(\w)([A-Z])", r"\1\/\2", prop)
target_prop = None
if pset in pset_exist:
if not target_prop:
if pset in pset_exist:
if prop in pset_exist[pset]:
target_prop = prop
elif prop_uncamel in pset_exist[pset]:
target_prop = prop_uncamel
elif prop_unslash in pset_exist[pset]:
target_prop = prop_unslash
if target_prop:
value_exist = pset_exist[pset][target_prop].split("(", 1)[1][:-1].strip("'")
if value_exist in [".F.", ".U."]:
value_exist = False
elif value_exist in [".T."]:
value_exist = True
elif isinstance(value, int):
value_exist = int(value_exist.strip("."))
elif isinstance(value, float):
value_exist = float(value_exist)
elif isinstance(value, FreeCAD.Units.Quantity):
if value.Unit.Type == "Angle":
value_exist = float(value_exist)
while value_exist > 360:
value_exist = value_exist - 360
value_exist = FreeCAD.Units.Quantity(float(value_exist), value.Unit)
if value == value_exist:
return False
else:
FreeCAD.Console.PrintLog(
"IFC: property changed for "
+ obj.Label
+ " ("
+ str(obj.StepId)
+ ") : "
+ str(target_prop)
+ " : "
+ str(value)
+ " ("
+ str(type(value))
+ ") -> "
+ str(value_exist)
+ " ("
+ str(type(value_exist))
+ ")\n"
)
pset = get_pset(pset, element)
else:
pset = ifc_tools.api_run("pset.add_pset", ifcfile, product=element, name=pset)
if not target_prop:
target_prop = prop
# create pset if needed
if pset in pset_exist:
ifcpset = get_pset(pset, element)
if target_prop in pset_exist[pset]:
value_exist = pset_exist[pset][target_prop].split("(", 1)[1][:-1].strip("'")
else:
ifcpset = ifc_tools.api_run("pset.add_pset", ifcfile, product=element, name=pset)
# value conversions
if value_exist in [".F.", ".U."]:
value_exist = False
elif value_exist in [".T."]:
value_exist = True
elif isinstance(value, int):
if value_exist:
value_exist = int(value_exist.strip("."))
elif isinstance(value, float):
if value_exist:
value_exist = float(value_exist)
elif isinstance(value, FreeCAD.Units.Quantity):
if value_exist:
value_exist = float(value_exist)
if value.Unit.Type == "Angle":
if value_exist:
while value_exist > 360:
value_exist = value_exist - 360
value = value.getValueAs("deg")
elif value.Unit.Type == "Length":
value = value.getValueAs("mm").Value * ifc_tools.get_scale(ifcfile)
else:
print("DEBUG: unhandled quantity type:",value, value.Unit.Type)
return False
if value == value_exist:
return False
if not force and not value and not value_exist:
return False
FreeCAD.Console.PrintLog(
"IFC: property changed for "
+ obj.Label
+ " ("
+ str(obj.StepId)
+ "): "
+ str(target_prop)
+ ": "
+ str(value_exist)
+ " ("
+ type(value_exist).__name__
+ ") -> "
+ str(value)
+ " ("
+ type(value).__name__
+ ")\n"
)
# run the change
# TODO the property type is automatically determined by ifcopenhell
# https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/pset/edit_pset/index.html
# and is therefore wrong for Quantity types. Research a way to overcome that
ifc_tools.api_run(
"pset.edit_pset", ifcfile, pset=pset, properties={target_prop: value}
"pset.edit_pset", ifcfile, pset=ifcpset, properties={target_prop: value}
)
# TODO manage quantities
return True
@@ -252,3 +297,62 @@ def add_property(ifcfile, pset, name, value=""):
To force a certain type, value can also be an IFC element such as IfcLabel"""
ifc_tools.api_run("pset.edit_pset", ifcfile, pset=pset, properties={name: value})
def get_freecad_type(ptype):
"""Returns a FreeCAD property type correspinding to an IFC property type"""
conv = read_properties_conversion()
for key, values in conv.items():
if ptype.lower() in [v.lower() for v in values.split(":")]:
return key
return "App::PropertyString"
def get_ifc_type(fctype):
"""Returns an IFC property type correspinding to a FreeCAD property type"""
conv = read_properties_conversion()
for key, values in conv.items():
if fctype.lower() == key.lower():
return values.split(":")[0]
return "IfcLabel"
def read_properties_conversion():
"""Reads the properties conversion table"""
import csv
csvfile = os.path.join(
FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "properties_conversion.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 remove_property(obj, prop):
"""Removes a custom property"""
from nativeifc import ifc_tools
ifcfile = ifc_tools.get_ifcfile(obj)
if not ifcfile:
return
element = ifc_tools.get_ifc_element(obj, ifcfile)
if not element:
return
psets = get_psets(element)
for psetname, props in psets.items():
if prop in props:
pset = get_pset(psetname, element)
if pset:
FreeCAD.Console.PrintMessage(translate("BIM","Removing property")+": "+prop)
ifc_tools.api_run("pset.edit_pset", ifcfile, pset=pset, properties={prop: None})
if len(props) == 1:
# 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)

View File

@@ -24,6 +24,8 @@
"""This contains nativeifc status widgets and functionality"""
import os
import csv
import FreeCAD
import FreeCADGui
@@ -56,6 +58,169 @@ def set_status_widget(statuswidget):
lock_button.setChecked(checked)
on_toggle_lock(checked, noconvert=True)
lock_button.triggered.connect(on_toggle_lock)
set_properties_editor(statuswidget)
def set_properties_editor(statuswidget):
"""Adds additional buttons to the properties editor"""
if hasattr(statuswidget, "propertybuttons"):
statuswidget.propertybuttons.show()
else:
from PySide import QtCore, QtGui # lazy loading
mw = FreeCADGui.getMainWindow()
editor = mw.findChild(QtGui.QTabWidget,"propertyTab")
if editor:
pTabCornerWidget = QtGui.QWidget()
pButton1 = QtGui.QToolButton(pTabCornerWidget)
pButton1.setText("")
pButton1.setToolTip(translate("BIM","Add IFC property..."))
pButton1.setIcon(QtGui.QIcon(":/icons/IFC.svg"))
pButton1.clicked.connect(on_add_property)
pButton2 = QtGui.QToolButton(pTabCornerWidget)
pButton2.setText("")
pButton2.setToolTip(translate("BIM","Add standard IFC Property Set..."))
pButton2.setIcon(QtGui.QIcon(":/icons/BIM_IfcProperties.svg"))
pButton2.clicked.connect(on_add_pset)
pHLayout = QtGui.QHBoxLayout(pTabCornerWidget)
pHLayout.addWidget(pButton1)
pHLayout.addWidget(pButton2)
pHLayout.setSpacing(2)
pHLayout.setContentsMargins(2, 2, 0, 0)
pHLayout.insertStretch(0)
editor.setCornerWidget(pTabCornerWidget, QtCore.Qt.BottomRightCorner)
statuswidget.propertybuttons = pTabCornerWidget
QtCore.QTimer.singleShot(0,pTabCornerWidget.show)
def on_add_property():
"""When the 'add property' button is clicked"""
sel = FreeCADGui.Selection.getSelection()
if not sel:
return
from PySide import QtCore, QtGui # lazy loading
from nativeifc import ifc_psets
obj = sel[0]
psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList]))
psets = [p for p in psets if p]
psets = [p for p in psets if p not in ["Base", "IFC", "Geometry"]]
mw = FreeCADGui.getMainWindow()
editor = mw.findChild(QtGui.QTabWidget,"propertyTab")
pset = None
if editor:
wid = editor.currentWidget()
if wid and wid.objectName() == "propertyEditorData":
if wid.currentIndex().parent():
pset = wid.currentIndex().parent().data()
else:
pset = wid.currentIndex().data()
form = FreeCADGui.PySideUic.loadUi(":/ui/dialogAddProperty.ui")
# center the dialog over FreeCAD window
form.move(mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center())
form.field_pset.clear()
form.field_pset.addItems(psets)
if pset and (pset in psets):
form.field_pset.setCurrentIndex(psets.index(pset))
# TODO check for name duplicates while typing
# execute
result = form.exec_()
if not result:
return
pname = form.field_name.text()
if pname in obj.PropertiesList:
print("DEBUG: property already exists",pname)
return
pset = form.field_pset.currentText()
if not pset:
# TODO disable the OK button if empty
t = translate("BIM","No Property set provided")
FreeCAD.Console.PrintError(t+"\n")
ptype = form.field_type.currentIndex()
ptype = ["IfcLabel", "IfcBoolean",
"IfcInteger", "IfcReal",
"IfcLengthMeasure", "IfcAreaMeasure"][ptype]
fctype = ifc_psets.get_freecad_type(ptype)
FreeCAD.ActiveDocument.openTransaction(translate("BIM","add property"))
for obj in sel:
obj.addProperty(fctype, pname, pset, ptype+":"+pname)
ifc_psets.edit_pset(obj, pname, force=True)
FreeCAD.ActiveDocument.commitTransaction()
def on_add_pset():
"""When the 'add pset' button is pressed"""
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
def get_fcprop(ifcprop):
if ifcprop == "IfcLengthMeasure":
return "App::PropertyDistance"
elif ifcprop == "IfcPositiveLengthMeasure":
return "App::PropertyLength"
elif ifcprop in ["IfcBoolean", "IfcLogical"]:
return "App::PropertyBool"
elif ifcprop == "IfcInteger":
return "App::PropertyInteger"
elif ifcprop == "IfcReal":
return "App::PropertyFloat"
elif ifcprop == "IfcAreaMeasure":
return "App::PropertyArea"
return "App::PropertyString"
sel = FreeCADGui.Selection.getSelection()
if not sel:
return
from PySide import QtCore, QtGui # lazy loading
from nativeifc import ifc_psets
obj = sel[0]
mw = FreeCADGui.getMainWindow()
# read standard psets
psetpath = os.path.join(
FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "pset_definitions.csv"
)
custompath = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "CustomPsets.csv")
psetdefs = read_csv(psetpath)
psetdefs.update(read_csv(custompath))
psetkeys = list(psetdefs.keys())
psetkeys.sort()
form = FreeCADGui.PySideUic.loadUi(":/ui/dialogAddPSet.ui")
# center the dialog over FreeCAD window
form.move(mw.frameGeometry().topLeft() + mw.rect().center() - form.rect().center())
form.field_pset.clear()
form.field_pset.addItems(psetkeys)
# execute
result = form.exec_()
if not result:
return
pset = form.field_pset.currentText()
existing_psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList]))
if pset in existing_psets:
t = translate("BIM","Property set already exists")
FreeCAD.Console.PrintError(t+": "+pset+"\n")
return
props = [psetdefs[pset][i:i+2] for i in range(0, len(psetdefs[pset]), 2)]
props = [[p[0], p[1]] for p in props]
FreeCAD.ActiveDocument.openTransaction(translate("BIM","add property set"))
for obj in sel:
existing_psets = list(set([obj.getGroupOfProperty(p) for p in obj.PropertiesList]))
if pset not in existing_psets:
ifc_psets.add_pset(obj, pset)
for prop in props:
if prop[0] in obj.PropertiesList:
t = translate("BIM","Property already exists")
FreeCAD.Console.PrintWarning(t+": "+obj.Label+","+prop[0]+"\n")
else:
obj.addProperty(get_fcprop(prop[1]),prop[0],pset,prop[1]+":"+prop[0])
FreeCAD.ActiveDocument.commitTransaction()
def on_toggle_lock(checked=None, noconvert=False, setchecked=False):

View File

@@ -745,6 +745,21 @@ def set_attribute(ifcfile, element, attribute, value):
# This function can become pure IFC
def differs(val1, val2):
if val1 == val2:
return False
if not val1 and not val2:
return False
if val1 is None and "NOTDEFINED" in str(val2).upper():
return False
if val1 is None and "UNDEFINED" in str(val2).upper():
return False
if val2 is None and "NOTDEFINED" in str(val1).upper():
return False
if val2 is None and "UNDEFINED" in str(val1).upper():
return False
return True
if not ifcfile or not element:
return False
if isinstance(value, FreeCAD.Units.Quantity):
@@ -774,7 +789,7 @@ def set_attribute(ifcfile, element, attribute, value):
):
# do not consider default FreeCAD names given to unnamed alements
return False
if getattr(element, attribute) != value:
if differs(getattr(element, attribute, None),value):
FreeCAD.Console.PrintLog(
"Changing IFC attribute value of "
+ str(attribute)
@@ -802,22 +817,25 @@ def set_colors(obj, colors):
else:
colors = [abs(c) for c in colors]
if hasattr(vobj, "ShapeColor"):
if isinstance(colors[0], (tuple, list)):
vobj.ShapeColor = colors[0][:3]
# do not set transparency when the object has more than one color
#if len(colors[0]) > 3:
# vobj.Transparency = int(colors[0][3] * 100)
else:
vobj.ShapeColor = colors[:3]
if len(colors) > 3:
vobj.Transparency = int(colors[3] * 100)
if hasattr(vobj, "DiffuseColor"):
# strip out transparency value because it currently gives ugly
# results in FreeCAD when combining transparent and non-transparent objects
if all([len(c) > 3 and c[3] != 0 for c in colors]):
vobj.DiffuseColor = colors
else:
vobj.DiffuseColor = [c[:3] for c in colors]
# 1.0 materials
if not isinstance(colors[0], (tuple, list)):
colors = [colors]
# set the first color to opaque otherwise it spoils object transparency
if len(colors) > 1:
#colors[0] = colors[0][:3] + (0.0,)
# TEMP HACK: if multiple colors, set everything to opaque because it looks wrong
colors = [color[:3] + (0.0,) for color in colors]
sapp = []
for color in colors:
sapp_mat = FreeCAD.Material()
if len(color) < 4:
sapp_mat.DiffuseColor = color + (1.0,)
else:
sapp_mat.DiffuseColor = color[:3] + (1.0 - color[3],)
sapp_mat.Transparency = color[3] if len(color) > 3 else 0.0
sapp.append(sapp_mat)
#print(vobj.Object.Label,[[m.DiffuseColor,m.Transparency] for m in sapp])
vobj.ShapeAppearance = sapp
def get_body_context_ids(ifcfile):