# *************************************************************************** # * * # * Copyright (c) 2017 Yorik van Havre * # * * # * 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 module contains FreeCAD commands for the BIM workbench""" import sys import os import FreeCAD import FreeCADGui QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate import importlib import inspect tests = [ "testAll", "testIFC4", "testHierarchy", "testSites", "testBuildings", "testStoreys", "testUndefined", "testSolid", "testQuantities", "testCommonPsets", "testPsets", "testMaterials", "testStandards", "testExtrusions", "testStandardCases", "testTinyLines", "testRectangleProfileDef", ] class BIM_Preflight: def GetResources(self): return { "Pixmap": "BIM_Preflight", "MenuText": QT_TRANSLATE_NOOP("BIM_Preflight", "Preflight checks..."), "ToolTip": QT_TRANSLATE_NOOP( "BIM_Preflight", "Checks several characteristics of this model before exporting to IFC", ), } def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") return v def Activated(self): FreeCADGui.BIMPreflightDone = False FreeCADGui.Control.showDialog(BIM_Preflight_TaskPanel()) class BIM_Preflight_TaskPanel: def __init__(self): from PySide import QtCore, QtGui self.results = {} # to store the result message self.culprits = {} # to store objects to highlight self.rform = None # to store the results dialog self.form = FreeCADGui.PySideUic.loadUi(":/ui/dialogPreflight.ui") self.form.setWindowIcon(QtGui.QIcon(":/icons/BIM_Preflight.svg")) for test in tests: getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_right.svg")) getattr(self.form, test).setToolTip( translate("BIM", "Press to perform the test") ) if hasattr(self, test): getattr(self.form, test).clicked.connect(getattr(self, test)) self.results[test] = None self.culprits[test] = None # setup custom tests self.customTests = {} customModulePath = os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "Preflight") if os.path.exists(customModulePath): customModules = [ m[:-3] for m in os.listdir(customModulePath) if m.endswith(".py") ] if customModules: sys.path.append(customModulePath) for customModule in customModules: mod = importlib.import_module(customModule) if not "Preflight" in mod.__file__: # prevent from using other modules with same name FreeCAD.Console.PrintLog( "Preflight: loaded wrong module - skipping: " + customModule + " " + str(mod) + "\n" ) continue FreeCAD.Console.PrintLog( "Preflight: found custom module: " + customModule + " " + str(mod) + "\n" ) functions = [ o[0] for o in inspect.getmembers(mod) if inspect.isfunction(o[1]) ] if functions: box = QtGui.QGroupBox(customModule) lay = QtGui.QGridLayout(box) self.form.layout().addWidget(box) for funcname in functions: FreeCAD.Console.PrintLog( "Preflight: found custom test: " + funcname + "\n" ) func = getattr(mod, funcname) descr = func.__doc__ if not descr: descr = "Undefined" lab = QtGui.QLabel(descr) lab.setWordWrap(True) but = QtGui.QPushButton() butname = "Custom_" + customModule + "_" + funcname but.setObjectName(butname) setattr(self.form, butname, but) self.reset(butname) row = lay.rowCount() lay.addWidget(lab, row, 0) lay.addWidget(but, row, 1) but.clicked.connect(lambda: self.testCustom(butname)) self.customTests[butname] = func def getStandardButtons(self): from PySide import QtCore, QtGui return int(QtGui.QDialogButtonBox.Close) def reject(self): from PySide import QtCore, QtGui QtGui.QApplication.restoreOverrideCursor() FreeCADGui.Control.closeDialog() FreeCAD.ActiveDocument.recompute() def passed(self, test): "sets the button as passed" from PySide import QtCore, QtGui getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_valid.svg")) getattr(self.form, test).setText(translate("BIM", "Passed")) getattr(self.form, test).setToolTip( translate("BIM", "This test has succeeded.") ) def failed(self, test): "sets the button as failed" from PySide import QtCore, QtGui getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/process-stop.svg")) getattr(self.form, test).setText("Failed") getattr(self.form, test).setToolTip( translate("BIM", "This test has failed. Press the button to know more") ) def reset(self, test): "reset the button" from PySide import QtCore, QtGui getattr(self.form, test).setIcon(QtGui.QIcon(":/icons/button_right.svg")) getattr(self.form, test).setText(translate("BIM", "Test")) getattr(self.form, test).setToolTip( translate("BIM", "Press to perform the test") ) def show(self, test): "shows test results" if (test in self.results) and self.results[test]: if (test in self.culprits) and self.culprits[test]: FreeCADGui.Selection.clearSelection() for c in self.culprits[test]: FreeCADGui.Selection.addSelection(c) if not self.rform: self.rform = FreeCADGui.PySideUic.loadUi(":/ui/dialogPreflightResults.ui") # center the dialog over FreeCAD window mw = FreeCADGui.getMainWindow() self.rform.move( mw.frameGeometry().topLeft() + mw.rect().center() - self.rform.rect().center() ) self.rform.buttonReport.clicked.connect(self.toReport) self.rform.buttonOK.clicked.connect(self.closeReport) self.rform.textBrowser.setText(self.results[test]) label = test.replace("test", "label") self.rform.label.setText(getattr(self.form, label).text()) self.rform.test = test self.rform.show() def toReport(self): "copies the resulting text to the report view" if self.rform and hasattr(self.rform, "test") and self.rform.test: if self.results[self.rform.test]: FreeCAD.Console.PrintMessage(self.results[self.rform.test] + "\n") def closeReport(self): if self.rform: self.rform.test = None self.rform.hide() def getObjects(self): "selects target objects" import Draft import Arch objs = [] if self.form.getAll.isChecked(): objs = FreeCAD.ActiveDocument.Objects elif self.form.getVisible.isChecked(): objs = [ o for o in FreeCAD.ActiveDocument.Objects if o.ViewObject.Visibility == True ] else: objs = FreeCADGui.Selection.getSelection() # clean objects list of unwanted types objs = Draft.get_group_contents(objs, walls=True, addgroups=True) objs = [obj for obj in objs if not obj.isDerivedFrom("Part::Part2DObject")] objs = [obj for obj in objs if not obj.isDerivedFrom("App::Annotation")] objs = [ obj for obj in objs if ( hasattr(obj, "Shape") and obj.Shape and not (obj.Shape.Edges and (not obj.Shape.Faces)) ) ] objs = Arch.pruneIncluded(objs) objs = [ obj for obj in objs if not obj.isDerivedFrom("App::DocumentObjectGroup") ] objs = [ obj for obj in objs if Draft.getType(obj) not in ["DraftText", "Material", "MaterialContainer", "WorkingPlaneProxy"] ] return objs def getToolTip(self, test): "gets the toolTip text from the ui file" import re label = test.replace("test", "label") tooltip = getattr(self.form, label).toolTip() tooltip = tooltip.replace("

", "

\n\n") tooltip = re.sub("<.*?>", "", tooltip) # strip html tags return tooltip def testAll(self): "runs all tests" from PySide import QtCore, QtGui from DraftGui import todo for test in tests: if test != "testAll": QtGui.QApplication.processEvents() self.reset(test) if hasattr(self, test): todo.delay(getattr(self, test), None) for customTest in self.customTests.keys(): todo.delay(self.testCustom, customTest) FreeCADGui.BIMPreflightDone = True def testIFC4(self): "tests for IFC4 support" test = "testIFC4" if getattr(self.form, test).text() == "Failed": self.show(test) else: self.reset(test) self.results[test] = None self.culprits[test] = None msg = None try: import ifcopenshell except ImportError: msg = ( translate( "BIM", "ifcopenshell is not installed on your system or not available to FreeCAD. This library is responsible for IFC support in FreeCAD, and therefore IFC support is currently disabled. Check %1 to obtain more information.", ).replace("%1", "https://www.freecadweb.org/wiki/Extra_python_modules#IfcOpenShell") + " " ) self.failed(test) else: if hasattr( ifcopenshell, "schema_identifier" ) and ifcopenshell.schema_identifier.startswith("IFC4"): self.passed(test) elif hasattr(ifcopenshell, "version"): try: from packaging import version if "-" in ifcopenshell.version: # Prebuild version have a version like 'v0.7.0-, # trying to remove the commit id. cur_version = version.parse(ifcopenshell.version.split('-')[0]) else: cur_version = version.parse(ifcopenshell.version) min_version = version.parse("0.6") if cur_version >= min_version: self.passed(test) else: msg = self.getToolTip(test) msg = ( translate( "BIM", "The version of ifcopenshell installed on your system could not be parsed", ) + " " ) self.failed(test) except Exception as e: self.failed(test) else: msg = self.getToolTip(test) msg += ( translate( "BIM", "The version of ifcopenshell installed on your system will produce files with this schema version:", ) + "\n\n" ) if hasattr(ifcopenshell, "schema_identifier"): msg += ifcopenshell.schema_identifier + "\n\n" else: msg += "Unable to retrieve schemas information from ifcopenshell\n\n" self.failed(test) self.results[test] = msg def testHierarchy(self): "tests for project hierarchy support" import Draft from PySide import QtCore, QtGui test = "testHierarchy" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None sites = False buildings = False storeys = False for obj in self.getObjects(): if ( (Draft.getType(obj) == "Site") or (hasattr(obj, "IfcRole") and (obj.IfcRole == "Site")) or (hasattr(obj, "IfcType") and (obj.IfcType == "Site")) ): sites = True elif ( (Draft.getType(obj) == "Building") or (hasattr(obj, "IfcRole") and (obj.IfcRole == "Building")) or (hasattr(obj, "IfcType") and (obj.IfcType == "Building")) ): buildings = True elif ( hasattr(obj, "IfcRole") and (obj.IfcRole == "Building Storey") ) or (hasattr(obj, "IfcType") and (obj.IfcType == "Building Storey")): storeys = True if (not sites) or (not buildings) or (not storeys): msg = self.getToolTip(test) msg += ( translate( "BIM", "The following types were not found in the project:" ) + "\n" ) if not sites: msg += "\nSite" if not buildings: msg += "\nBuilding" if not storeys: msg += "\nBuilding Storey" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testSites(self): "tests for Sites support" import Draft from PySide import QtCore, QtGui test = "testSites" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if ( (Draft.getType(obj) == "Building") or (hasattr(obj, "IfcRole") and (obj.IfcRole == "Building")) or (hasattr(obj, "IfcType") and (obj.IfcType == "Building")) ): ok = False for parent in obj.InList: if ( (Draft.getType(parent) == "Site") or ( hasattr(parent, "IfcRole") and (parent.IfcRole == "Site") ) or ( hasattr(parent, "IfcType") and (parent.IfcType == "Site") ) ): if hasattr(parent, "Group") and parent.Group: if obj in parent.Group: ok = True break if not ok: self.culprits[test].append(obj) if not msg: msg = self.getToolTip(test) msg += ( translate( "BIM", "The following Building objects have been found to not be included in any Site. You can resolve the situation by creating a Site object, if none is present in your model, and drag and drop the Building objects into it in the tree view:", ) + "\n\n" ) msg += obj.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testBuildings(self): "tests for Buildings support" from PySide import QtCore, QtGui test = "testBuildings" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if (hasattr(obj, "IfcRole") and (obj.IfcRole == "Building Storey")) or ( hasattr(obj, "IfcType") and (obj.IfcType == "Building Storey") ): ok = False for parent in obj.InList: if ( hasattr(parent, "IfcRole") and (parent.IfcRole == "Building") ) or ( hasattr(parent, "IfcType") and (parent.IfcType == "Building") ): if hasattr(parent, "Group") and parent.Group: if obj in parent.Group: ok = True break if not ok: self.culprits[test].append(obj) if not msg: msg = self.getToolTip(test) msg += ( translate( "BIM", 'The following Building Storey (BuildingParts with their IFC role set as "Building Storey") objects have been found to not be included in any Building. You can resolve the situation by creating a Building object, if none is present in your model, and drag and drop the Building Storey objects into it in the tree view:', ) + "\n\n" ) msg += obj.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testStoreys(self): "tests for Building Storey support" from PySide import QtCore, QtGui test = "testStoreys" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if ( hasattr(obj, "IfcRole") and (not obj.IfcRole in ["Building", "Building Storey", "Site"]) ) or ( hasattr(obj, "IfcType") and (not obj.IfcType in ["Building", "Building Storey", "Site"]) ): ok = False ancestors = obj.InListRecursive # append extra objects not in InList if hasattr(obj,"Host") and not obj.Host in ancestors: ancestors.append(obj.Host) if hasattr(obj,"Hosts"): for h in obj.Hosts: if not h in ancestors: ancestors.append(h) for parent in ancestors: # just check if any of the ancestors is a Building Storey for now. Don't check any further... if ( hasattr(parent, "IfcRole") and (parent.IfcRole in ["Building Storey", "Building"]) ) or ( hasattr(parent, "IfcType") and (parent.IfcType in ["Building Storey", "Building"]) ): ok = True break if not ok: self.culprits[test].append(obj) if not msg: msg = self.getToolTip(test) msg += ( translate( "BIM", 'The following BIM objects have been found to not be included in any Building Storey (BuildingParts with their IFC role set as "Building Storey"). You can resolve the situation by creating a Building Storey object, if none is present in your model, and drag and drop these objects into it in the tree view:', ) + "\n\n" ) msg += obj.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testUndefined(self): "tests for undefined BIM objects" from PySide import QtCore, QtGui test = "testUndefined" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] undefined = [] notbim = [] msg = None for obj in self.getObjects(): if hasattr(obj, "IfcType"): if obj.IfcType == "Undefined": self.culprits[test].append(obj) undefined.append(obj) elif hasattr(obj, "IfcRole"): if obj.IfcRole == "Undefined": self.culprits[test].append(obj) undefined.append(obj) else: self.culprits[test].append(obj) notbim.append(obj) if undefined or notbim: msg = self.getToolTip(test) if undefined: msg += ( translate( "BIM", 'The following BIM objects have the "Undefined" type:', ) + "\n\n" ) for o in undefined: msg += o.Label + "\n" if notbim: msg += ( translate("BIM", "The following objects are not BIM objects:") + "\n\n" ) for o in notbim: msg += o.Label + "\n" msg += translate( "BIM", "You can turn these objects into BIM objects by using the Utils -> Make Component tool.", ) if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testSolid(self): "tests for invalid/non-solid BIM objects" from PySide import QtCore, QtGui test = "testSolid" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if obj.isDerivedFrom("Part::Feature"): if (not obj.Shape.isNull()) and ( (not obj.Shape.isValid()) or (not obj.Shape.Solids) ): self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The following BIM objects have an invalid or non-solid geometry:", ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testQuantities(self): "tests for explicit quantities export" from PySide import QtCore, QtGui test = "testQuantities" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if hasattr(obj, "IfcAttributes") and ( Draft.getType(obj) != "BuildingPart" ): for prop in ["Length", "Width", "Height"]: if prop in obj.PropertiesList: if (not "Export" + prop in obj.IfcAttributes) or ( obj.IfcAttributes["Export" + prop] == "False" ): self.culprits[test].append(obj) break if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The objects below have Length, Width or Height properties, but these properties won't be explicitly exported to IFC. This is not necessarily an issue, unless you specifically want these quantities to be exported:", ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" msg += "\n" + translate( "BIM", "To enable exporting of these quantities, use the IFC quantities manager tool located under menu Manage -> Manage IFC Quantities...", ) if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testCommonPsets(self): "tests for common property sets" from PySide import QtCore, QtGui import csv test = "testCommonPsets" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None psets = [] psetspath = os.path.join( FreeCAD.getResourceDir(), "Mod", "Arch", "Presets", "pset_definitions.csv", ) if os.path.exists(psetspath): with open(psetspath, "r") as csvfile: reader = csv.reader(csvfile, delimiter=";") for row in reader: if "Common" in row[0]: psets.append(row[0][5:-6]) psets = [ "".join(map(lambda x: x if x.islower() else " " + x, p)) for p in psets ] psets = [pset.strip() for pset in psets] # print(psets) for obj in self.getObjects(): ok = True if hasattr(obj, "IfcProperties") and isinstance( obj.IfcProperties, dict ): r = None if hasattr(obj, "IfcType"): r = obj.IfcType if hasattr(obj, "IfcRole"): r = obj.IfcRole if r and (r in psets): ok = False if "Pset_" + r.replace(" ", "") + "Common" in ",".join( obj.IfcProperties.values() ): ok = True if not ok: self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The objects below have a defined IFC type but do not have the associated common property set:", ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" msg += "\n" + translate( "BIM", "To add common property sets to these objects, use the IFC properties manager tool located under menu Manage -> Manage IFC Properties...", ) if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testPsets(self): "tests for property sets integrity" from PySide import QtCore, QtGui import csv test = "testPsets" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None psets = {} psetspath = os.path.join( FreeCAD.getResourceDir(), "Mod", "Arch", "Presets", "pset_definitions.csv", ) if os.path.exists(psetspath): with open(psetspath, "r") as csvfile: reader = csv.reader(csvfile, delimiter=";") for row in reader: if "Common" in row[0]: psets[row[0]] = row[1:] for obj in self.getObjects(): ok = True if hasattr(obj, "IfcProperties") and isinstance( obj.IfcProperties, dict ): r = None if hasattr(obj, "IfcType"): r = obj.IfcType elif hasattr(obj, "IfcRole"): r = obj.IfcRole if r and (r != "Undefined"): found = None for pset in psets.keys(): for val in obj.IfcProperties.values(): if pset in val: found = pset break if found: for i in range(int(len(psets[found]) / 2)): p = psets[found][i * 2] t = psets[found][i * 2 + 1] # print("testing for ",p,t,found," in ",obj.IfcProperties) if p in obj.IfcProperties: if (not found in obj.IfcProperties[p]) or ( not t in obj.IfcProperties[p] ): ok = False else: ok = False if not ok: self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The objects below have a common property set but that property set doesn't contain all the needed properties:", ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" msg += ( "\n" + translate( "BIM", "Verify which properties a certain property set must contain on %1", ).replace("%1", "https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/annex/annex-b/alphabeticalorder_psets.htm") + "\n\n" ) msg += translate( "BIM", "To fix the property sets of these objects, use the IFC properties manager tool located under menu Manage -> Manage IFC Properties...", ) if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testMaterials(self): "tests for materials in BIM objects" from PySide import QtCore, QtGui test = "testMaterials" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if "Material" in obj.PropertiesList: if not obj.Material: self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The following BIM objects have no material attributed:" ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testStandards(self): "tests for standards in BIM objects" from PySide import QtCore, QtGui test = "testStandards" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if "StandardCode" in obj.PropertiesList: if not obj.StandardCode: self.culprits[test].append(obj) if "Material" in obj.PropertiesList: if obj.Material: if "StandardCode" in obj.Material.PropertiesList: if not obj.Material.StandardCode: self.culprits[test].append(obj.Material) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The following BIM objects have no defined standard code:", ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testExtrusions(self): "tests is all objects are extrusions" from PySide import QtCore, QtGui import Draft test = "testExtrusions" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if hasattr(obj, "Proxy"): if ( hasattr(obj, "IfcAttributes") and ("FlagForceBrep" in obj.IfcAttributes.keys()) and (obj.IfcAttributes["FlagForceBrep"] == "True") ): self.culprits[test].append(obj) elif hasattr( obj.Proxy, "getExtrusionData" ) and not obj.Proxy.getExtrusionData(obj): self.culprits[test].append(obj) elif Draft.getType(obj) == "BuildingPart": pass elif obj.isDerivedFrom("Part::Extrusion"): pass elif obj.isDerivedFrom("App::DocumentObjectGroup"): pass elif obj.isDerivedFrom("App::MaterialObject"): pass else: self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate("BIM", "The following BIM objects are not extrusions:") + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testStandardCases(self): "tests for structs and wall standard cases" import Draft from PySide import QtCore, QtGui test = "testStandardCases" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None for obj in self.getObjects(): if Draft.getType(obj) == "Wall": if obj.Base and (len(obj.Base.Shape.Edges) != 1): self.culprits[test].append(obj) elif Draft.getType(obj) == "Structure": if obj.Base and ( (len(obj.Base.Shape.Wires) != 1) or (not obj.Base.Shape.Wires[0].isClosed()) ): self.culprits[test].append(obj) if self.culprits[test]: msg = self.getToolTip(test) msg += ( translate( "BIM", "The following BIM objects are not standard cases:" ) + "\n\n" ) for o in self.culprits[test]: msg += o.Label + "\n" if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testTinyLines(self): "tests for objects with tiny lines (< 0.8mm)" from PySide import QtCore, QtGui test = "testTinyLines" if getattr(self.form, test).text() == "Failed": self.show(test) else: QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) self.reset(test) self.results[test] = None self.culprits[test] = [] msg = None minl = 0.79376 # min 1/32" edges = [] objs = [] for obj in self.getObjects(): if obj.isDerivedFrom("Part::Feature"): if obj.Shape: for e in obj.Shape.Edges: if e.Length <= minl: edges.append(e) if not obj in objs: objs.append(obj) if edges: import Part result = FreeCAD.ActiveDocument.addObject( "Part::Feature", "TinyLinesResult" ) result.Shape = Part.makeCompound(edges) result.ViewObject.LineWidth = 5 self.culprits[test] = [result] msg = self.getToolTip(test) msg += ( translate( "BIM", "The objects below have lines smaller than 1/32 inch or 0.79 mm, which is the smallest line size that Revit accepts. These objects will be discarded when imported into Revit:", ) + "\n\n" ) for obj in objs: msg += obj.Label + "\n" msg += ( "\n" + translate( "BIM", 'An additional object, called "TinyLinesResult" has been added to this model, and selected. It contains all the tiny lines found, so you can inspect them and fix the needed objects. Be sure to delete the TinyLinesResult object when you are done!', ) + "\n\n" ) msg += translate( "BIM", "Tip: The results are best viewed in Wireframe mode (menu Views -> Draw Style -> Wireframe)", ) if msg: self.failed(test) else: self.passed(test) self.results[test] = msg QtGui.QApplication.restoreOverrideCursor() def testRectangleProfileDef(self): "tests for RectangleProfileDef disable" test = "testRectangleProfileDef" if getattr(self.form, test).text() == "Failed": self.show(test) else: self.reset(test) self.results[test] = None self.culprits[test] = None msg = None if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool( "DisableIfcRectangleProfileDef", False ): self.passed(test) else: msg = self.getToolTip(test) self.failed(test) self.results[test] = msg def testCustom(self, test): "performs a custom test" if test in self.customTests: if getattr(self.form, test).text() == "Failed": self.show(test) else: self.reset(test) self.results[test] = None result = self.customTests[test]() if result == True: self.passed(test) else: self.failed(test) self.results[test] = result FreeCADGui.addCommand("BIM_Preflight", BIM_Preflight())