BIM: Add support for deactivation active object to BIM Views Tree (#21570)

* BIM: Use checkbox in model tree for Activation/Deactivation of WP

* BIM: Set active object after deactivating current object if it exists

Currently we can get into a scenario where user can activate two working
planes, one after another. For example, Level, and  then Level001. If
they activate both, and then deactivate Level001, working plane switches
back to Level. But, we didn't set the object as the active one, so user
didn't have clear information that they can deactivate it, only the
working plane was switching it.

So this patch sets the object as the active one, if it exists.

* BIM: Add support for deactivation active object to BIM Views Tree

As the title says - it adds the checkbox that's similarly done in Part
workbench, so user can select/deselect the item and if they had previous
active object, it will also fall back to the previous object.

Also, moved out part of the common logic from ArchBuildingPart and
BimViews to utils.

* BIM: Handle correct context on activating WP for NativeIFC/BIM

* BIM: Remove redundant logic from BIM Views upon double click

As all of the logic is being handled now in `activate` function in
BimViews, this logic is redundant

* BIM: Rename button for taskbar and BIM Views from Activate to Active
This commit is contained in:
tetektoza
2025-05-28 11:27:11 +02:00
committed by GitHub
parent 1f6ecf83b2
commit 8ec6605fc4
4 changed files with 117 additions and 40 deletions

View File

@@ -811,15 +811,14 @@ class ViewProviderBuildingPart:
if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench':
return
if (not hasattr(vobj,"DoubleClickActivates")) or vobj.DoubleClickActivates:
menuTxt = translate("Arch", "Active")
actionActivate = QtGui.QAction(menuTxt, menu)
actionActivate.setCheckable(True)
if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") == self.Object:
menuTxt = translate("Arch", "Deactivate")
actionActivate.setChecked(True)
else:
menuTxt = translate("Arch", "Activate")
actionActivate = QtGui.QAction(menuTxt,
menu)
QtCore.QObject.connect(actionActivate,
QtCore.SIGNAL("triggered()"),
self.activate)
actionActivate.setChecked(False)
actionActivate.triggered.connect(lambda _: self.activate(actionActivate))
menu.addAction(actionActivate)
actionSetWorkingPlane = QtGui.QAction(QtGui.QIcon(":/icons/Draft_SelectPlane.svg"),
@@ -859,17 +858,15 @@ class ViewProviderBuildingPart:
self.cloneUp)
menu.addAction(actionCloneUp)
def activate(self):
def activate(self, action=None):
from draftutils.utils import toggle_working_plane
vobj = self.Object.ViewObject
if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") == self.Object:
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("Arch", None)
if vobj.SetWorkingPlane:
self.setWorkingPlane(restore=True)
elif (not hasattr(vobj,"DoubleClickActivates")) or vobj.DoubleClickActivates:
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("Arch", self.Object)
if vobj.SetWorkingPlane:
self.setWorkingPlane()
if (not hasattr(vobj,"DoubleClickActivates")) or vobj.DoubleClickActivates:
if toggle_working_plane(self.Object, action, restore=True):
print("Setting active working plane to: ", self.Object.Label)
else:
print("Deactivating working plane from: ", self.Object.Label)
FreeCADGui.Selection.clearSelection()
@@ -883,7 +880,24 @@ class ViewProviderBuildingPart:
autoclip = vobj.AutoCutView
if restore:
if wp.label.rstrip("*") == self.Object.Label:
wp._previous()
prev_data = wp._previous()
if prev_data:
prev_label = prev_data.get("label", "").rstrip("*")
prev_obj = None
for obj in FreeCAD.ActiveDocument.Objects:
if hasattr(obj, "Label") and obj.Label == prev_label:
prev_obj = obj
break
if prev_obj:
# check in which context we need to set the active object
context = "Arch"
obj_type = Draft.getType(prev_obj)
if obj_type == "IfcBuildingStorey":
context = "NativeIFC"
FreeCADGui.ActiveDocument.ActiveView.setActiveObject(context, prev_obj)
print(f"Set active object to: {prev_obj.Label} (context: {context})")
if autoclip:
vobj.CutView = False
else:

View File

@@ -86,7 +86,7 @@ class BIM_Views:
# set button
self.dialog.menu = QtGui.QMenu()
for button in [("Activate", translate("BIM","Activate (default)")),
for button in [("Active", translate("BIM","Active (default)")),
("AddLevel", translate("BIM","Add level")),
("AddProxy", translate("BIM","Add proxy")),
("Delete", translate("BIM","Delete")),
@@ -97,10 +97,11 @@ class BIM_Views:
action = QtGui.QAction(button[1])
# Make the "Activate" button bold, as this is the default one
if button[0] == "Activate":
if button[0] == "Active":
font = action.font()
font.setBold(True)
action.setFont(font)
action.setCheckable(True)
self.dialog.menu.addAction(action)
setattr(self.dialog,"button"+button[0], action)
@@ -115,7 +116,6 @@ class BIM_Views:
self.dialog.buttonRename.setIcon(
QtGui.QIcon(":/icons/accessories-text-editor.svg")
)
self.dialog.buttonActivate.setIcon(QtGui.QIcon(":/icons/edit_OK.svg"))
# set tooltips
self.dialog.buttonAddLevel.setToolTip(translate("BIM","Creates a new level"))
@@ -125,7 +125,7 @@ class BIM_Views:
self.dialog.buttonIsolate.setToolTip(translate("BIM","Turns all items off except the selected ones"))
self.dialog.buttonSaveView.setToolTip(translate("BIM","Saves the current camera position to the selected items"))
self.dialog.buttonRename.setToolTip(translate("BIM","Renames the selected item"))
self.dialog.buttonActivate.setToolTip(translate("BIM","Activates the selected item"))
self.dialog.buttonActive.setToolTip(translate("BIM","Activates the selected item"))
# connect signals
self.dialog.buttonAddLevel.triggered.connect(self.addLevel)
@@ -135,7 +135,7 @@ class BIM_Views:
self.dialog.buttonIsolate.triggered.connect(self.isolate)
self.dialog.buttonSaveView.triggered.connect(self.saveView)
self.dialog.buttonRename.triggered.connect(self.rename)
self.dialog.buttonActivate.triggered.connect(self.activate)
self.dialog.buttonActive.triggered.connect(lambda: BIM_Views.activate(self.dialog))
self.dialog.tree.itemClicked.connect(self.select)
self.dialog.tree.itemDoubleClicked.connect(show)
self.dialog.viewtree.itemDoubleClicked.connect(show)
@@ -431,14 +431,16 @@ class BIM_Views:
item = vm.tree.selectedItems()[-1]
vm.tree.editItem(item, 0)
@staticmethod
def activate():
def activate(dialog=None):
from draftutils.utils import toggle_working_plane
vm = findWidget()
if vm:
if vm.tree.selectedItems():
if vm.tree.selectedItems():
item = vm.tree.selectedItems()[-1]
obj = FreeCAD.ActiveDocument.getObject(item.toolTip(0))
obj.ViewObject.Proxy.setWorkingPlane()
item = vm.tree.selectedItems()[-1]
obj = FreeCAD.ActiveDocument.getObject(item.toolTip(0))
if obj:
toggle_working_plane(obj, None, restore=True, dialog=dialog)
FreeCADGui.Selection.clearSelection()
def editObject(self, item, column):
"renames or edit height of the actual object"
@@ -564,6 +566,10 @@ class BIM_Views:
if selobj:
if Draft.getType(selobj).startswith("Ifc"):
self.dialog.buttonAddProxy.setEnabled(False)
if FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") == selobj:
self.dialog.buttonActive.setChecked(True)
else:
self.dialog.buttonActive.setChecked(False)
self.dialog.menu.exec_(self.dialog.tree.mapToGlobal(pos))
def getViews(self):
@@ -655,19 +661,10 @@ def show(item, column=None):
vparam.SetBool("RadialGradient", False)
else:
# case 3: This is maybe a BuildingPart. Place the WP on it")
BIM_Views.activate()
type = Draft.getType(obj)
if type == "BuildingPart" or type == "IfcBuildingStorey":
BIM_Views.activate()
# perform stored interactions
if getattr(obj.ViewObject, "SetWorkingPlane", False):
obj.ViewObject.Proxy.setWorkingPlane()
if getattr(obj.ViewObject, "DoubleClickActivates", True):
if Draft.getType(obj) == "BuildingPart":
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("Arch", obj)
elif Draft.getType(obj) == "IfcBuildingStorey":
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("NativeIFC", obj)
else:
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("Arch", None)
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("NativeIFC", None)
if vm:
# store the last double-clicked item for the BIM WPView command
if isinstance(item, str) or (

View File

@@ -1667,6 +1667,7 @@ class PlaneGui(PlaneBase):
self.set_parameters(self._history["data_list"][idx])
self._history["idx"] = idx
self._update_all(_hist_add=False)
return self._history["data_list"][idx]
def _next(self):
idx = self._history["idx"]

View File

@@ -1068,4 +1068,69 @@ def pyopen(file, mode='r', buffering=-1, encoding=None, errors=None, newline=Non
encoding = 'utf-8'
return open(file, mode, buffering, encoding, errors, newline, closefd, opener)
def toggle_working_plane(obj, action=None, restore=False, dialog=None):
"""Toggle the active state of a working plane object.
This function handles the common logic for activating and deactivating
working plane objects like BuildingParts and WorkingPlaneProxies.
It can be used by different modules that need to implement similar
working plane activation behavior.
Parameters
----------
obj : App::DocumentObject
The object to activate or deactivate as a working plane.
action : QAction, optional
The action button that triggered this function, to update its checked state.
restore : bool, optional
If True, will restore the previous working plane when deactivating.
Defaults to False.
dialog : QDialog, optional
If provided, will update the checked state of the activate button in the dialog.
Returns
-------
bool
True if the object was activated, False if it was deactivated.
"""
import FreeCADGui
import Draft
# Determine the appropriate context based on object type
context = "Arch"
obj_type = get_type(obj)
if obj_type == "IfcBuildingStorey":
context = "NativeIFC"
# Check if the object is already active in its context
is_active_arch = (FreeCADGui.ActiveDocument.ActiveView.getActiveObject("Arch") == obj)
is_active_ifc = (FreeCADGui.ActiveDocument.ActiveView.getActiveObject("NativeIFC") == obj)
is_active = is_active_arch or is_active_ifc
if is_active:
# Deactivate the object
if is_active_arch:
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("Arch", None)
if is_active_ifc:
FreeCADGui.ActiveDocument.ActiveView.setActiveObject("NativeIFC", None)
if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "Proxy") and \
hasattr(obj.ViewObject.Proxy, "setWorkingPlane"):
obj.ViewObject.Proxy.setWorkingPlane(restore=True)
if action:
action.setChecked(False)
if dialog and hasattr(dialog, "buttonActive"):
dialog.buttonActive.setChecked(False)
return False
else:
# Activate the object
FreeCADGui.ActiveDocument.ActiveView.setActiveObject(context, obj)
if hasattr(obj, "ViewObject") and hasattr(obj.ViewObject, "Proxy") and \
hasattr(obj.ViewObject.Proxy, "setWorkingPlane"):
obj.ViewObject.Proxy.setWorkingPlane()
if action:
action.setChecked(True)
if dialog and hasattr(dialog, "buttonActive"):
dialog.buttonActive.setChecked(True)
return True
## @}