From 2a983b587d374f584039797a1846bbb86753593d Mon Sep 17 00:00:00 2001 From: tetektoza Date: Fri, 18 Apr 2025 17:56:21 +0200 Subject: [PATCH] Draft/BIM: Change Continue behavior and caching among commands (#20748) * Draft: Cache ContinueMode setting for every tool separately Currently ContinueMode is done to be held globally, so this patch changes this to cache it inside `user.cfg` for every cmd separately. * Draft: Add Chained Mode option for Dimension Added new option under Dimension, although this is simply porting the existing logic of "Continue" under "Chained Mode", whereas allowing existing "Continue" mode to retrigger the command instead of placing Dimensions in a chain. --- src/Mod/BIM/ArchStructure.py | 33 ++++------------ src/Mod/BIM/bimcommands/BimBeam.py | 1 + src/Mod/BIM/bimcommands/BimColumn.py | 1 + src/Mod/BIM/bimcommands/BimPanel.py | 32 +++++---------- src/Mod/BIM/bimcommands/BimWall.py | 34 ++++------------ src/Mod/Draft/DraftGui.py | 25 ++++++++++-- src/Mod/Draft/draftguitools/gui_dimensions.py | 36 ++++++++++------- src/Mod/Draft/draftutils/params.py | 39 +++++++++++++++++-- 8 files changed, 106 insertions(+), 95 deletions(-) diff --git a/src/Mod/BIM/ArchStructure.py b/src/Mod/BIM/ArchStructure.py index f48c02732b..942fb8bee2 100644 --- a/src/Mod/BIM/ArchStructure.py +++ b/src/Mod/BIM/ArchStructure.py @@ -295,7 +295,6 @@ class _CommandStructure: self.Length = params.get_param_arch("StructureLength") self.Height = params.get_param_arch("StructureHeight") self.Profile = None - self.continueCmd = False self.bpoint = None self.bmode = False self.precastvalues = None @@ -338,6 +337,7 @@ class _CommandStructure: title=translate("Arch","Base point of column")+":" FreeCAD.activeDraftCommand = self # register as a Draft command for auto grid on/off FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=[self.taskbox(),self.precast.form,self.dents.form],title=title) + FreeCADGui.draftToolBar.continueCmd.show() def getPoint(self,point=None,obj=None): @@ -423,7 +423,7 @@ class _CommandStructure: FreeCAD.ActiveDocument.recompute() # gui_utils.end_all_events() # Causes a crash on Linux. self.tracker.finalize() - if self.continueCmd: + if FreeCADGui.draftToolBar.continueCmd.isChecked(): self.Activated() def _createItemlist(self, baselist): @@ -505,21 +505,10 @@ class _CommandStructure: grid.addWidget(self.vHeight,6,1,1,1) # horizontal button - value5 = QtGui.QPushButton(translate("Arch","Switch Length/Height")) - grid.addWidget(value5,7,0,1,1) - value6 = QtGui.QPushButton(translate("Arch","Switch Length/Width")) - grid.addWidget(value6,7,1,1,1) - - # continue button - label4 = QtGui.QLabel(translate("Arch","Con&tinue")) - value4 = QtGui.QCheckBox() - value4.setObjectName("ContinueCmd") - value4.setLayoutDirection(QtCore.Qt.RightToLeft) - label4.setBuddy(value4) - self.continueCmd = params.get_param("ContinueMode") - value4.setChecked(self.continueCmd) - grid.addWidget(label4,8,0,1,1) - grid.addWidget(value4,8,1,1,1) + value4 = QtGui.QPushButton(translate("Arch","Switch Length/Height")) + grid.addWidget(value4,7,0,1,1) + value5 = QtGui.QPushButton(translate("Arch","Switch Length/Width")) + grid.addWidget(value5,7,1,1,1) # connect slots QtCore.QObject.connect(self.valuec,QtCore.SIGNAL("currentIndexChanged(int)"),self.setCategory) @@ -527,9 +516,8 @@ class _CommandStructure: QtCore.QObject.connect(self.vLength,QtCore.SIGNAL("valueChanged(double)"),self.setLength) QtCore.QObject.connect(self.vWidth,QtCore.SIGNAL("valueChanged(double)"),self.setWidth) QtCore.QObject.connect(self.vHeight,QtCore.SIGNAL("valueChanged(double)"),self.setHeight) - QtCore.QObject.connect(value4,QtCore.SIGNAL("stateChanged(int)"),self.setContinue) - QtCore.QObject.connect(value5,QtCore.SIGNAL("pressed()"),self.rotateLH) - QtCore.QObject.connect(value6,QtCore.SIGNAL("pressed()"),self.rotateLW) + QtCore.QObject.connect(value4,QtCore.SIGNAL("pressed()"),self.rotateLH) + QtCore.QObject.connect(value5,QtCore.SIGNAL("pressed()"),self.rotateLW) QtCore.QObject.connect(self.modeb,QtCore.SIGNAL("toggled(bool)"),self.switchLH) # restore preset @@ -602,11 +590,6 @@ class _CommandStructure: else: params.set_param_arch("StructureLength",d) - def setContinue(self,i): - - self.continueCmd = bool(i) - params.set_param("ContinueMode", bool(i)) - def setCategory(self,i): self.vPresets.clear() diff --git a/src/Mod/BIM/bimcommands/BimBeam.py b/src/Mod/BIM/bimcommands/BimBeam.py index c86010468b..6bf4a7096c 100644 --- a/src/Mod/BIM/bimcommands/BimBeam.py +++ b/src/Mod/BIM/bimcommands/BimBeam.py @@ -38,6 +38,7 @@ class BIM_Beam(ArchStructure._CommandStructure): def __init__(self): super().__init__() self.beammode = True + self.featureName = "Beam" def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") diff --git a/src/Mod/BIM/bimcommands/BimColumn.py b/src/Mod/BIM/bimcommands/BimColumn.py index f5d3734442..cd5d0e87a4 100644 --- a/src/Mod/BIM/bimcommands/BimColumn.py +++ b/src/Mod/BIM/bimcommands/BimColumn.py @@ -38,6 +38,7 @@ class BIM_Column(ArchStructure._CommandStructure): def __init__(self): super().__init__() self.beammode = False + self.featureName = "Column" def IsActive(self): v = hasattr(FreeCADGui.getMainWindow().getActiveWindow(), "getSceneGraph") diff --git a/src/Mod/BIM/bimcommands/BimPanel.py b/src/Mod/BIM/bimcommands/BimPanel.py index 23f98a788d..c2d0740404 100644 --- a/src/Mod/BIM/bimcommands/BimPanel.py +++ b/src/Mod/BIM/bimcommands/BimPanel.py @@ -70,7 +70,7 @@ class Arch_Panel: self.Width = params.get_param_arch("PanelWidth") self.Thickness = params.get_param_arch("PanelThickness") self.Profile = None - self.continueCmd = False + self.featureName = "Panel" self.rotated = False sel = FreeCADGui.Selection.getSelection() if sel: @@ -96,7 +96,9 @@ class Arch_Panel: self.tracker.height(self.Thickness) self.tracker.length(self.Length) self.tracker.on() + FreeCAD.activeDraftCommand = self FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox()) + FreeCADGui.draftToolBar.continueCmd.show() def getPoint(self,point=None,obj=None): @@ -105,6 +107,7 @@ class Arch_Panel: import DraftVecUtils self.tracker.finalize() if point is None: + FreeCAD.activeDraftCommand = None return FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Panel")) FreeCADGui.addModule("Arch") @@ -120,7 +123,8 @@ class Arch_Panel: FreeCADGui.doCommand('s.Placement.Rotation = FreeCAD.Rotation(FreeCAD.Vector(1.00,0.00,0.00),90.00)') FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() - if self.continueCmd: + FreeCAD.activeDraftCommand = None + if FreeCADGui.draftToolBar.continueCmd.isChecked(): self.Activated() def taskbox(self): @@ -166,26 +170,14 @@ class Arch_Panel: grid.addWidget(self.vHeight,3,1,1,1) # horizontal button - value5 = QtGui.QPushButton(translate("Arch","Rotate")) - grid.addWidget(value5,4,0,1,2) - - # continue button - label4 = QtGui.QLabel(translate("Arch","Con&tinue")) - value4 = QtGui.QCheckBox() - value4.setObjectName("ContinueCmd") - value4.setLayoutDirection(QtCore.Qt.RightToLeft) - label4.setBuddy(value4) - self.continueCmd = params.get_param("ContinueMode") - value4.setChecked(self.continueCmd) - grid.addWidget(label4,5,0,1,1) - grid.addWidget(value4,5,1,1,1) + value4= QtGui.QPushButton(translate("Arch","Rotate")) + grid.addWidget(value4,4,0,1,2) valuep.currentIndexChanged.connect(self.setPreset) self.vLength.valueChanged.connect(self.setLength) self.vWidth.valueChanged.connect(self.setWidth) self.vHeight.valueChanged.connect(self.setThickness) - value4.stateChanged.connect(self.setContinue) - value5.pressed.connect(self.rotate) + value4.pressed.connect(self.rotate) return w def update(self,point,info): @@ -221,12 +213,6 @@ class Arch_Panel: self.Length = d.Value params.set_param_arch("PanelLength",d) - def setContinue(self,i): - - from draftutils import params - self.continueCmd = bool(i) - params.set_param("ContinueMode", bool(i)) - def setPreset(self,i): if i > 0: diff --git a/src/Mod/BIM/bimcommands/BimWall.py b/src/Mod/BIM/bimcommands/BimWall.py index e9926ebb4c..bbc681a5b8 100644 --- a/src/Mod/BIM/bimcommands/BimWall.py +++ b/src/Mod/BIM/bimcommands/BimWall.py @@ -75,7 +75,7 @@ class Arch_Wall: self.MultiMat = None self.Length = None self.lengthValue = 0 - self.continueCmd = False + self.featureName = "Wall" self.Width = params.get_param_arch("WallWidth") self.Height = params.get_param_arch("WallHeight") self.JOIN_WALLS_SKETCHES = params.get_param_arch("joinWallSketches") @@ -118,6 +118,7 @@ class Arch_Wall: FreeCADGui.Snapper.getPoint(callback=self.getPoint, extradlg=self.taskbox(), title=translate("Arch","First point of wall")+":") + FreeCADGui.draftToolBar.continueCmd.show() def getPoint(self,point=None,obj=None): """Callback for clicks during interactive mode. @@ -194,7 +195,7 @@ class Arch_Wall: FreeCAD.ActiveDocument.recompute() # gui_utils.end_all_events() # Causes a crash on Linux. self.tracker.finalize() - if self.continueCmd: + if FreeCADGui.draftToolBar.continueCmd.isChecked(): self.Activated() def addDefault(self): @@ -319,31 +320,20 @@ class Arch_Wall: grid.addWidget(label3,4,0,1,1) grid.addWidget(value3,4,1,1,1) - label4 = QtGui.QLabel(translate("Arch","Con&tinue")) + label4 = QtGui.QLabel(translate("Arch","Use sketches")) value4 = QtGui.QCheckBox() - value4.setObjectName("ContinueCmd") + value4.setObjectName("UseSketches") value4.setLayoutDirection(QtCore.Qt.RightToLeft) label4.setBuddy(value4) - self.continueCmd = params.get_param("ContinueMode") - value4.setChecked(self.continueCmd) + value4.setChecked(params.get_param_arch("WallSketches")) grid.addWidget(label4,5,0,1,1) grid.addWidget(value4,5,1,1,1) - label5 = QtGui.QLabel(translate("Arch","Use sketches")) - value5 = QtGui.QCheckBox() - value5.setObjectName("UseSketches") - value5.setLayoutDirection(QtCore.Qt.RightToLeft) - label5.setBuddy(value5) - value5.setChecked(params.get_param_arch("WallSketches")) - grid.addWidget(label5,6,0,1,1) - grid.addWidget(value5,6,1,1,1) - self.Length.valueChanged.connect(self.setLength) value1.valueChanged.connect(self.setWidth) value2.valueChanged.connect(self.setHeight) value3.currentIndexChanged.connect(self.setAlign) - value4.stateChanged.connect(self.setContinue) - value5.stateChanged.connect(self.setUseSketch) + value4.stateChanged.connect(self.setUseSketch) self.Length.returnPressed.connect(value1.setFocus) self.Length.returnPressed.connect(value1.selectAll) value1.returnPressed.connect(value2.setFocus) @@ -397,16 +387,6 @@ class Arch_Wall: self.Align = ["Center","Left","Right"][i] params.set_param_arch("WallAlignment",i) - def setContinue(self,i): - """Simple callback to set if the interactive mode will restart when finished. - - This allows for several walls to be placed one after another. - """ - - from draftutils import params - self.continueCmd = bool(i) - params.set_param("ContinueMode", bool(i)) - def setUseSketch(self,i): """Simple callback to set if walls should based on sketches.""" diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index d3c702a7f5..37cd00f6f3 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -156,6 +156,7 @@ class DraftToolBar: self.paramconstr = utils.rgba_to_argb(params.get_param("constructioncolor")) self.constrMode = False self.continueMode = False + self.chainedMode = False self.relativeMode = True self.globalMode = False self.state = None @@ -390,7 +391,11 @@ class DraftToolBar: self.relativeMode = params.get_param("RelativeMode") self.globalMode = params.get_param("GlobalMode") self.makeFaceMode = params.get_param("MakeFaceMode") - self.continueMode = params.get_param("ContinueMode") + + feature_name = getattr(FreeCAD.activeDraftCommand, "featureName", None) + self.continueMode = params.get_param(feature_name, "Mod/Draft/ContinueMode", silent=True) + + self.chainedMode = params.get_param("ChainedMode") # Note: The order of the calls to self._checkbox() below controls # the position of the checkboxes in the task panel. @@ -399,7 +404,11 @@ class DraftToolBar: self.isRelative = self._checkbox("isRelative", self.layout, checked=self.relativeMode) self.isGlobal = self._checkbox("isGlobal", self.layout, checked=self.globalMode) self.makeFace = self._checkbox("makeFace", self.layout, checked=self.makeFaceMode) - self.continueCmd = self._checkbox("continueCmd", self.layout, checked=self.continueMode) + self.continueCmd = self._checkbox("continueCmd", self.layout, checked=bool(self.continueMode)) + self.chainedModeCmd = self._checkbox("chainedModeCmd", self.layout, checked=self.chainedMode) + + self.chainedModeCmd.setEnabled(not (hasattr(self.sourceCmd, "contMode") and self.continueMode)) + self.continueCmd.setEnabled(not (hasattr(self.sourceCmd, "chain") and self.chainedMode)) # update checkboxes without parameters and without internal modes: self.occOffset = self._checkbox("occOffset", self.layout, checked=False) @@ -449,6 +458,7 @@ class DraftToolBar: QtCore.QObject.connect(self.undoButton,QtCore.SIGNAL("pressed()"),self.undoSegment) QtCore.QObject.connect(self.selectButton,QtCore.SIGNAL("pressed()"),self.selectEdge) QtCore.QObject.connect(self.continueCmd,QtCore.SIGNAL("stateChanged(int)"),self.setContinue) + QtCore.QObject.connect(self.chainedModeCmd,QtCore.SIGNAL("stateChanged(int)"),self.setChainedMode) QtCore.QObject.connect(self.isCopy,QtCore.SIGNAL("stateChanged(int)"),self.setCopymode) QtCore.QObject.connect(self.isSubelementMode, QtCore.SIGNAL("stateChanged(int)"), self.setSubelementMode) @@ -549,6 +559,9 @@ class DraftToolBar: + "the command button again")) self.continueCmd.setText(translate( "draft", "Continue") + " (" + _get_incmd_shortcut("Continue") + ")") + self.chainedModeCmd.setText(translate("draft", "Chained Mode")) + self.chainedModeCmd.setToolTip(translate("draft", "If checked, next Dimension will be placed in a chain" \ + " with the previously placed Dimension")) self.occOffset.setToolTip(translate( "draft", "If checked, an OCC-style offset will be performed" + " instead of the classic offset")) @@ -920,8 +933,14 @@ class DraftToolBar: #--------------------------------------------------------------------------- def setContinue(self, val): - params.set_param("ContinueMode", bool(val)) + params.set_param(FreeCAD.activeDraftCommand.featureName, bool(val), "Mod/Draft/ContinueMode") self.continueMode = bool(val) + self.chainedModeCmd.setEnabled(not val) + + def setChainedMode(self, val): + params.set_param("ChainedMode", bool(val)) + self.chainedMode = bool(val) + self.continueCmd.setEnabled(not val) # val=-1 is used to temporarily switch to relativeMode and disable the checkbox. # val=-2 is used to switch back. diff --git a/src/Mod/Draft/draftguitools/gui_dimensions.py b/src/Mod/Draft/draftguitools/gui_dimensions.py index 1bdd2eeb77..778d46df04 100644 --- a/src/Mod/Draft/draftguitools/gui_dimensions.py +++ b/src/Mod/Draft/draftguitools/gui_dimensions.py @@ -74,8 +74,10 @@ class Dimension(gui_base_original.Creator): def __init__(self): super().__init__() self.max = 2 - self.cont = None + self.chain = None + self.contMode = None self.dir = None + self.featureName = "Dimension" def GetResources(self): """Set icon, menu and tooltip.""" @@ -87,13 +89,14 @@ class Dimension(gui_base_original.Creator): def Activated(self): """Execute when the command is called.""" - if self.cont: + if self.chain and not self.contMode: self.finish() else: - super().Activated(name="Dimension") + super().Activated(name=self.featureName) if self.ui: - self.ui.pointUi(title=translate("draft", "Dimension"), icon="Draft_Dimension") + self.ui.pointUi(title=translate("draft", self.featureName), icon="Draft_Dimension") self.ui.continueCmd.show() + self.ui.chainedModeCmd.show() self.ui.selectButton.show() self.altdown = False self.call = self.view.addEventCallback("SoEvent", self.action) @@ -159,7 +162,8 @@ class Dimension(gui_base_original.Creator): def finish(self, cont=False): """Terminate the operation.""" self.end_callbacks(self.call) - self.cont = None + self.chain = None + self.contMode = None self.dir = None if self.ui: self.dimtrack.finalize() @@ -283,8 +287,9 @@ class Dimension(gui_base_original.Creator): # Linear dimension, not linked to any edge self.create_linear_dimension() - if self.ui.continueMode: - self.cont = self.node[2] + if self.ui.chainedMode or self.ui.continueMode: + if self.ui.chainedMode: + self.chain = self.node[2] if not self.dir: if self.link: v1 = self.link[0].Shape.Vertexes[self.link[1]].Point @@ -339,7 +344,7 @@ class Dimension(gui_base_original.Creator): ed = ob.Shape.Edges[num] v1 = ed.Vertexes[0].Point v2 = ed.Vertexes[-1].Point - self.dimtrack.update([v1, v2, self.cont]) + self.dimtrack.update([v1, v2, self.chain]) else: if self.node and (len(self.edges) < 2): self.dimtrack.on() @@ -413,7 +418,7 @@ class Dimension(gui_base_original.Creator): # update the dimline if self.node and not self.arcmode: self.dimtrack.update(self.node - + [self.point] + [self.cont]) + + [self.point] + [self.chain]) gui_tool_utils.redraw3DView() elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): @@ -493,10 +498,10 @@ class Dimension(gui_base_original.Creator): self.dimtrack.on() if self.planetrack: self.planetrack.set(self.node[0]) - elif len(self.node) == 2 and self.cont: - self.node.append(self.cont) + elif len(self.node) == 2 and self.chain: + self.node.append(self.chain) self.createObject() - if not self.cont: + if not self.chain: self.finish() elif len(self.node) == 3: # for unlinked arc mode: @@ -506,7 +511,10 @@ class Dimension(gui_base_original.Creator): # cen = self.node[0].add(v) # self.node = [self.node[0], self.node[1], cen] self.createObject() - if not self.cont: + if self.ui.continueMode: + self.contMode = True + self.Activated() + elif not self.chain: self.finish() elif self.angledata: self.node.append(self.point) @@ -526,7 +534,7 @@ class Dimension(gui_base_original.Creator): self.dimtrack.on() elif len(self.node) == 3: self.createObject() - if not self.cont: + if not self.chain: self.finish() def set_constraint_node(self): diff --git a/src/Mod/Draft/draftutils/params.py b/src/Mod/Draft/draftutils/params.py index a08f2bec32..de872df14e 100644 --- a/src/Mod/Draft/draftutils/params.py +++ b/src/Mod/Draft/draftutils/params.py @@ -404,8 +404,8 @@ def _get_param_dictionary(): param_dict["Mod/Draft"] = { "AnnotationStyleEditorHeight": ("int", 450), "AnnotationStyleEditorWidth": ("int", 450), + "ChainedMode": ("bool", False), "CenterPlaneOnView": ("bool", False), - "ContinueMode": ("bool", False), "CopyMode": ("bool", False), "DefaultAnnoDisplayMode": ("int", 0), "DefaultDisplayMode": ("int", 0), @@ -443,6 +443,35 @@ def _get_param_dictionary(): "useSupport": ("bool", False), } + param_dict["Mod/Draft/ContinueMode"] = { + # Draft + "Line": ("bool", False), + "Polyline": ("bool", False), + "Arc": ("bool", False), + "Arc_3Points": ("bool", False), + "Circle": ("bool", False), + "Ellipse": ("bool", False), + "Rectangle": ("bool", False), + "Polygon": ("bool", False), + "Bspline": ("bool", False), + "CubicBezCurve": ("bool", False), + "BezCurve": ("bool", False), + "Point": ("bool", False), + "Text": ("bool", False), + "Dimension": ("bool", False), + + # Standard operations (Draft) + "Move": ("bool", False), + "Copy": ("bool", False), + "Rotate": ("bool", False), + + # Arch/BIM + "Wall": ("bool", False), + "Column": ("bool", False), + "Beam": ("bool", False), + "Panel": ("bool", False), + } + # Arch parameters that are not in the preferences: param_dict["Mod/Arch"] = { "applyConstructionStyle": ("bool", True), @@ -634,7 +663,7 @@ def _get_param_dictionary(): PARAM_DICT = _get_param_dictionary() -def get_param(entry, path="Mod/Draft", ret_default=False): +def get_param(entry, path="Mod/Draft", ret_default=False, silent=False): """Return a stored parameter value or its default. Parameters @@ -648,13 +677,17 @@ def get_param(entry, path="Mod/Draft", ret_default=False): ret_default: bool, optional Defaults to `False`. If `True`, always return the default value even if a stored value is available. + silent: bool, optional + Defaults to `False`. + If `True`, do not log anything if entry wasn't found. Returns ------- bool, float, int or str (if successful) or `None`. """ if path not in PARAM_DICT or entry not in PARAM_DICT[path]: - print(f"draftutils.params.get_param: Unable to find '{entry}' in '{path}'") + if not silent: + print(f"draftutils.params.get_param: Unable to find '{entry}' in '{path}'") return None param_grp = App.ParamGet("User parameter:BaseApp/Preferences/" + path) typ, default = PARAM_DICT[path][entry]