BIM: Fixed project creation and refactored status bar toggle system

This commit is contained in:
Yorik van Havre
2024-05-14 09:30:49 +02:00
committed by Yorik van Havre
parent f7ec184f32
commit d48a47f120
7 changed files with 140 additions and 157 deletions

View File

@@ -22,8 +22,8 @@
# add import/export types
FreeCAD.addImportType("Industry Foundation Classes (*.ifc)","importers.exportIFC")
# FreeCAD.addExportType("Industry Foundation Classes (*.ifc)","importIFC")
FreeCAD.addExportType("Industry Foundation Classes (*.ifc)","importers.exportIFC")
# FreeCAD.addImportType("Industry Foundation Classes (*.ifc)","importIFC")
FreeCAD.addImportType("Industry Foundation Classes (*.ifc)", "nativeifc.ifc_import")
FreeCAD.addExportType("Industry Foundation Classes - IFCJSON (*.ifcJSON)","importers.exportIFC")
FreeCAD.addImportType("Wavefront OBJ - Arch module (*.obj *.OBJ)","importers.importOBJ")

View File

@@ -41,6 +41,9 @@ class BIM_Project:
"Create an empty NativeIFC project"),
}
def IsActive(self):
return not hasattr(FreeCAD.ActiveDocument, "IfcFilePath")
def Activated(self):
from nativeifc import ifc_tools
project = ifc_tools.create_document(FreeCAD.ActiveDocument)

View File

@@ -190,9 +190,8 @@ class IFC_Save:
def IsActive(self):
doc = FreeCAD.ActiveDocument
if getattr(doc, "IfcFilePath", None):
if getattr(getattr(doc, "Proxy", None), "ifcfile", None):
return True
if hasattr(doc, "IfcFilePath"):
return True
return False
def Activated(self):
@@ -223,9 +222,8 @@ class IFC_SaveAs:
def IsActive(self):
doc = FreeCAD.ActiveDocument
if getattr(doc, "IfcFilePath", None):
if getattr(getattr(doc, "Proxy", None), "ifcfile", None):
return True
if hasattr(doc, "IfcFilePath"):
return True
return False
def Activated(self):

View File

@@ -36,7 +36,7 @@ if FreeCAD.GuiUp:
import Arch_rc
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
def open(filename):
@@ -78,7 +78,7 @@ def insert(
except:
document = FreeCAD.newDocument()
if singledoc is None:
singledoc = params.GetBool("SingleDoc", False)
singledoc = PARAMS.GetBool("SingleDoc", False)
if singledoc:
prj_obj = ifc_tools.convert_document(document, filename, shapemode, strategy)
QtCore.QTimer.singleShot(100, toggle_lock_on)
@@ -87,17 +87,17 @@ def insert(
document, filename, shapemode, strategy
)
QtCore.QTimer.singleShot(100, toggle_lock_off)
if params.GetBool("LoadOrphans", True):
if PARAMS.GetBool("LoadOrphans", True):
ifc_tools.load_orphans(prj_obj)
if not silent and params.GetBool("LoadMaterials", False):
if not silent and PARAMS.GetBool("LoadMaterials", False):
ifc_materials.load_materials(prj_obj)
if params.GetBool("LoadLayers", False):
if PARAMS.GetBool("LoadLayers", False):
ifc_layers.load_layers(prj_obj)
if params.GetBool("LoadPsets", False):
if PARAMS.GetBool("LoadPsets", False):
ifc_psets.load_psets(prj_obj)
document.recompute()
# print a reference to the IFC file on the console
if FreeCAD.GuiUp and params.GetBool("IfcFileToConsole", False):
if FreeCAD.GuiUp and PARAMS.GetBool("IfcFileToConsole", False):
if isinstance(prj_obj, FreeCAD.DocumentObject):
pstr = "FreeCAD.getDocument('{}').{}.Proxy.ifcfile"
pstr = pstr.format(prj_obj.Document.Name, prj_obj.Name)
@@ -128,19 +128,19 @@ def get_options(strategy=None, shapemode=None, switchwb=None, silent=False):
2 = all children
"""
psets = params.GetBool("LoadPsets", False)
materials = params.GetBool("LoadMaterials", False)
layers = params.GetBool("LoadLayers", False)
singledoc = params.GetBool("SingleDoc", False)
psets = PARAMS.GetBool("LoadPsets", False)
materials = PARAMS.GetBool("LoadMaterials", False)
layers = PARAMS.GetBool("LoadLayers", False)
singledoc = PARAMS.GetBool("SingleDoc", False)
if strategy is None:
strategy = params.GetInt("ImportStrategy", 0)
strategy = PARAMS.GetInt("ImportStrategy", 0)
if shapemode is None:
shapemode = params.GetInt("ShapeMode", 0)
shapemode = PARAMS.GetInt("ShapeMode", 0)
if switchwb is None:
switchwb = params.GetBool("SwitchWB", True)
switchwb = PARAMS.GetBool("SwitchWB", True)
if silent:
return strategy, shapemode, switchwb
ask = params.GetBool("AskAgain", False)
ask = PARAMS.GetBool("AskAgain", False)
if ask and FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtGui
@@ -166,22 +166,22 @@ def get_options(strategy=None, shapemode=None, switchwb=None, silent=False):
materials = dlg.checkLoadMaterials.isChecked()
layers = dlg.checkLoadLayers.isChecked()
singledoc = dlg.comboSingleDoc.currentIndex()
params.SetInt("ImportStrategy", strategy)
params.SetInt("ShapeMode", shapemode)
params.SetBool("SwitchWB", switchwb)
params.SetBool("AskAgain", ask)
params.SetBool("LoadPsets", psets)
params.SetBool("LoadMaterials", materials)
params.SetBool("LoadLayers", layers)
params.SetBool("SingleDoc", bool(1 - singledoc))
PARAMS.SetInt("ImportStrategy", strategy)
PARAMS.SetInt("ShapeMode", shapemode)
PARAMS.SetBool("SwitchWB", switchwb)
PARAMS.SetBool("AskAgain", ask)
PARAMS.SetBool("LoadPsets", psets)
PARAMS.SetBool("LoadMaterials", materials)
PARAMS.SetBool("LoadLayers", layers)
PARAMS.SetBool("SingleDoc", bool(1 - singledoc))
return strategy, shapemode, switchwb
def get_project_type(silent=False):
"""Gets the type of project to make"""
ask = params.GetBool("ProjectAskAgain", True)
ptype = params.GetBool("ProjectFull", False)
ask = PARAMS.GetBool("ProjectAskAgain", True)
ptype = PARAMS.GetBool("ProjectFull", False)
if silent:
return ptype
if ask and FreeCAD.GuiUp:
@@ -192,8 +192,8 @@ def get_project_type(silent=False):
result = dlg.exec_()
ask = not (dlg.checkBox.isChecked())
ptype = bool(result)
params.SetBool("ProjectAskAgain", ask)
params.SetBool("ProjectFull", ptype)
PARAMS.SetBool("ProjectAskAgain", ask)
PARAMS.SetBool("ProjectFull", ptype)
return ptype
@@ -201,11 +201,11 @@ def get_project_type(silent=False):
def toggle_lock_on():
ifc_status.toggle_lock(True)
ifc_status.on_toggle_lock(True, noconvert=True, setchecked=True)
def toggle_lock_off():
ifc_status.toggle_lock(False)
ifc_status.on_toggle_lock(False, noconvert=True, setchecked=True)
def unset_modified():

View File

@@ -32,14 +32,16 @@ params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
def add_observer():
"""Adds an observer to the running FreeCAD instance"""
observer = ifc_observer()
FreeCAD.addDocumentObserver(observer)
FreeCAD.BIMobserver = ifc_observer()
FreeCAD.addDocumentObserver(FreeCAD.BIMobserver)
def remove_observer():
"""Removes this observer if present"""
pass # TODO implement this!!
if hasattr(FreeCAD, "BIMobserver"):
FreeCAD.removeDocumentObserver(FreeCAD.BIMobserver)
del FreeCAD.BIMobserver
class ifc_observer:
@@ -99,9 +101,6 @@ class ifc_observer:
]
if len(child) == 1:
child[0].StepId = new_id
ifc_status.toggle_lock(True)
else:
ifc_status.toggle_lock(False)
def slotCreatedObject(self, obj):
"""If this is an IFC document, turn the object into IFC"""
@@ -119,16 +118,9 @@ class ifc_observer:
def slotActivateDocument(self, doc):
"""Check if we need to lock"""
from PySide2 import QtCore # lazy loading
from nativeifc import ifc_status
ifc_status.on_activate()
if hasattr(doc, "IfcFilePath"):
ifc_status.toggle_lock(True)
else:
ifc_status.toggle_lock(False)
if not hasattr(doc, "Proxy"):
# this is a new file, wait a bit to make sure all components are populated
QtCore.QTimer.singleShot(1000, self.propose_conversion)
# implementation methods
@@ -207,67 +199,3 @@ class ifc_observer:
newobj = ifc_tools.aggregate(obj, doc)
ifc_geometry.add_geom_properties(newobj)
doc.recompute()
def propose_conversion(self):
"""Propose a conversion of the current document"""
from nativeifc import ifc_status # lazy loading
doc = FreeCAD.ActiveDocument
if not getattr(FreeCAD, "IsOpeningIFC", False):
if not hasattr(doc, "Proxy"):
if not getattr(doc, "Objects", True):
if not doc.FileName:
if not hasattr(doc, "IfcFilePath"):
# this is really a new, empty document
import FreeCADGui
import Arch_rc
from PySide import QtCore, QtGui # lazy loading
if FreeCADGui.activeWorkbench().name() != "BIMWorkbench":
return
if not params.GetBool("SingleDocAskAgain", True):
if params.GetBool("SingleDoc", True):
self.full = params.GetBool("ProjectFull", False)
QtCore.QTimer.singleShot(
1000, self.convert_document
)
return
else:
ifc_status.toggle_lock(False)
return
d = os.path.dirname(__file__)
dlg = FreeCADGui.PySideUic.loadUi(
os.path.join(d, "ui", "dialogConvertDocument.ui")
)
dlg.checkStructure.setChecked(
params.GetBool("ProjectFull", False)
)
result = dlg.exec_()
self.full = dlg.checkStructure.isChecked()
params.SetBool(
"SingleDocAskAgain", not dlg.checkAskAgain.isChecked()
)
if result:
params.SetBool("SingleDoc", True)
params.SetBool("ProjectFull", self.full)
QtCore.QTimer.singleShot(1000, self.convert_document)
else:
params.SetBool("SingleDoc", False)
def convert_document(self):
"""Converts the active document"""
from nativeifc import ifc_tools # lazy loading
from nativeifc import ifc_status
doc = FreeCAD.ActiveDocument
ifc_tools.convert_document(doc, strategy=2, silent=True)
if self.full:
import Arch
site = ifc_tools.aggregate(Arch.makeSite(), doc)
building = ifc_tools.aggregate(Arch.makeBuilding(), site)
storey = ifc_tools.aggregate(Arch.makeFloor(), building)
ifc_status.toggle_lock(True)
doc.recompute()

View File

@@ -46,20 +46,63 @@ def set_status_widget(statuswidget):
lock_button.setIcon(icon)
lock_button.setCheckable(True)
doc = FreeCAD.ActiveDocument
statuswidget.addAction(lock_button)
statuswidget.lock_button = lock_button
if doc and "IfcFilePath" in doc.PropertiesList:
checked = True
else:
checked = params.GetBool("SingleDoc", False)
toggle_lock(checked)
set_button(lock_button, checked)
lock_button.triggered.connect(do_lock)
lock_button.triggered.connect(toggle_lock)
statuswidget.addAction(lock_button)
statuswidget.lock_button = lock_button
checked = False
# set the button first, without converting the document
lock_button.setChecked(checked)
on_toggle_lock(checked, noconvert=True)
lock_button.triggered.connect(on_toggle_lock)
def toggle_lock(checked=False):
"""Sets the lock button on/off"""
def on_toggle_lock(checked=None, noconvert=False, setchecked=False):
"""When the toolbar button is pressed"""
if checked is None:
checked = get_lock_status()
set_menu(checked)
set_button(checked, setchecked)
if not noconvert:
if checked:
lock_document()
else:
unlock_document()
def on_open():
"""What happens when opening an existing document"""
pass # TODO implement
def on_activate():
"""What happens when activating a document"""
from PySide import QtGui # lazy import
doc = FreeCAD.ActiveDocument
if doc and "IfcFilePath" in doc.PropertiesList:
checked = True
else:
checked = False
mw = FreeCADGui.getMainWindow()
statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
if hasattr(statuswidget, "lock_button"):
statuswidget.lock_button.setChecked(checked)
on_toggle_lock(checked, noconvert=True)
def on_new():
"""What happens when creating a new document"""
pass # TODO implement
def set_menu(locked=False):
"""Sets the File menu items"""
from PySide import QtCore, QtGui # lazy loading
@@ -67,7 +110,7 @@ def toggle_lock(checked=False):
mw = FreeCADGui.getMainWindow()
wb = FreeCADGui.activeWorkbench()
save_action = mw.findChild(QtGui.QAction, "Std_Save")
if checked and "IFC_Save" in FreeCADGui.listCommands():
if locked and "IFC_Save" in FreeCADGui.listCommands():
if not hasattr(FreeCADGui,"IFC_WBManipulator"):
FreeCADGui.IFC_WBManipulator = IFC_WBManipulator()
# we need to void the shortcut otherwise it keeps active
@@ -84,40 +127,33 @@ def toggle_lock(checked=False):
FreeCADGui.removeWorkbenchManipulator(FreeCADGui.IFC_WBManipulator)
del FreeCADGui.IFC_WBManipulator
wb.reloadActive()
# set the lock button
statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
if hasattr(statuswidget, "lock_button"):
set_button(statuswidget.lock_button, checked)
def set_button(lock_button, checked):
def set_button(checked=False, setchecked=False):
"""Sets the lock button"""
from PySide import QtGui # lazy loading
if checked:
lock_button.setChecked(True)
lock_button.setToolTip(text_on)
icon = QtGui.QIcon(":/icons/IFC.svg")
lock_button.setIcon(icon)
else:
lock_button.setChecked(False)
lock_button.setToolTip(text_off)
image = QtGui.QImage(":/icons/IFC.svg")
grayscale = image.convertToFormat(QtGui.QImage.Format_Grayscale8)
grayscale = grayscale.convertToFormat(image.format())
grayscale.setAlphaChannel(image)
icon = QtGui.QIcon(QtGui.QPixmap.fromImage(grayscale))
lock_button.setIcon(icon)
def do_lock(checked):
"""Locks or unlocks the document"""
if checked:
lock_document()
else:
unlock_document()
mw = FreeCADGui.getMainWindow()
statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
if hasattr(statuswidget, "lock_button"):
lock_button = statuswidget.lock_button
if checked:
lock_button.setToolTip(text_on)
icon = QtGui.QIcon(":/icons/IFC.svg")
lock_button.setIcon(icon)
if setchecked:
lock_button.setChecked(True)
else:
lock_button.setToolTip(text_off)
image = QtGui.QImage(":/icons/IFC.svg")
grayscale = image.convertToFormat(QtGui.QImage.Format_Grayscale8)
grayscale = grayscale.convertToFormat(image.format())
grayscale.setAlphaChannel(image)
icon = QtGui.QIcon(QtGui.QPixmap.fromImage(grayscale))
lock_button.setIcon(icon)
if setchecked:
lock_button.setChecked(False)
def unlock_document():
@@ -203,7 +239,7 @@ def lock_document():
FreeCAD.Console.PrintError(
"Unable to lock this document because it contains several IFC documents\n"
)
QtCore.QTimer.singleShot(100, toggle_lock)
QtCore.QTimer.singleShot(100, on_toggle_lock)
elif doc.Objects:
# 3 there is no project but objects
doc.openTransaction("Lock document")
@@ -220,7 +256,10 @@ def lock_document():
doc.recompute()
else:
# 4 this is an empty document
ifc_tools.convert_document(doc, silent=True)
doc.openTransaction("Create IFC document")
ifc_tools.convert_document(doc)
doc.commitTransaction()
doc.recompute()
def find_toplevel(objs):
@@ -248,6 +287,20 @@ def find_toplevel(objs):
return nobjs
def get_lock_status():
"""Returns the status of the IFC lock button"""
if not FreeCAD.GuiUp:
return PARAMS.GetBool("SingleDoc")
from PySide import QtGui
mw = FreeCADGui.getMainWindow()
statuswidget = mw.findChild(QtGui.QToolBar, "BIMStatusWidget")
if hasattr(statuswidget, "lock_button"):
return statuswidget.lock_button.isChecked()
else:
return False
# add entry to File menu
# https://github.com/FreeCAD/FreeCAD/pull/10933
class IFC_WBManipulator:

View File

@@ -45,6 +45,7 @@ from nativeifc import ifc_objects
from nativeifc import ifc_viewproviders
from nativeifc import ifc_import
from nativeifc import ifc_layers
from nativeifc import ifc_status
SCALE = 1000.0 # IfcOpenShell works in meters, FreeCAD works in mm
SHORT = False # If True, only Step ID attribute is created
@@ -55,7 +56,7 @@ PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
def create_document(document, filename=None, shapemode=0, strategy=0, silent=False):
"""Creates a IFC document object in the given FreeCAD document or converts that
document into an IFC document, depending on the value of SingleDoc preference.
document into an IFC document, depending on the state of the statusbar lock button.
filename: If not given, a blank IFC document is created
shapemode: 0 = full shape
@@ -66,7 +67,7 @@ def create_document(document, filename=None, shapemode=0, strategy=0, silent=Fal
2 = all children
"""
if PARAMS.GetBool("SingleDoc", False):
if ifc_status.get_lock_status():
return convert_document(document, filename, shapemode, strategy, silent)
else:
return create_document_object(document, filename, shapemode, strategy, silent)