From 477c912ad09f96e4841779c084bb11086859907e Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Sun, 3 Apr 2022 22:57:49 -0500 Subject: [PATCH 1/2] Path: Fix delayed extension length updates These changes allow for the extensions to be updated immediately upon changing either the Default Length expression or spinbox value, without having to click outside the spinbox. --- .../PathScripts/PathFeatureExtensionsGui.py | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py index bc8f88d865..4933aee0c9 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py @@ -194,6 +194,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.extensionsCache = dict() self.extensionsReady = False self.enabled = True + self.lastDefaultLength = "" self.extensions = list() @@ -284,6 +285,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self._initializeExtensions(obj) # Efficiently initialize Extensions self.defaultLength.updateSpinBox() self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value + self.lastDefaultLength = self.form.defaultLength.text() self.fieldsSet = True # flag to identify initial values set def _initializeExtensions(self, obj): @@ -299,16 +301,33 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.extensionEdit.setDisabled(True) self.setExtensions(self.extensions) - def updateQuantitySpinBoxes(self, index=None): - prevValue = self.form.defaultLength.text() - self.defaultLength.updateSpinBox() - postValue = self.form.defaultLength.text() + def _isDefaultLengthExpression(self): + """_isDefaultLengthExpression()... Return True if Default Length is determined by an expression.""" + for prop, __ in self.obj.ExpressionEngine: + if prop == "ExtensionLengthDefault": + return True + return False - if postValue != prevValue: - PathLog.debug("updateQuantitySpinBoxes() post != prev value") + def _applyDefaultLengthChange(self, index=None): + """_applyDefaultLengthChange(index=None)... + Helper method to update Default Length spinbox, and update extensions due to change in Default Length.""" + self.defaultLength.updateSpinBox() + if self.form.defaultLength.text() != self.lastDefaultLength: + self.lastDefaultLength = self.form.defaultLength.text() self._resetCachedExtensions() # Reset extension cache because extension dimensions likely changed self._enableExtensions() # Recalculate extensions + def _defaultLengthChanged(self): + """_defaultLengthChanged()... Slot method for determining if a change in Default Length + value is determined from an expression edit, or a simple spinbox change. If the former, + emit a `editingFinished` signal manually because the Formula Editor window returned + a value to the base SpinBox.""" + if ( + self._isDefaultLengthExpression() + and self.form.defaultLength.text() != self.lastDefaultLength + ): + self.form.defaultLength.editingFinished.emit() + def createItemForBaseModel(self, base, sub, edges, extensions): PathLog.track( base.Label, sub, "+", len(edges), len(base.Shape.getElement(sub).Edges) @@ -490,7 +509,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): if self.fieldsSet: if self.form.enableExtensions.isChecked(): if prop == "ExtensionLengthDefault": - self.updateQuantitySpinBoxes() + self._applyDefaultLengthChange() elif prop == "Base": self.extensionsReady = False self.setExtensions(FeatureExtensions.getExtensions(obj)) @@ -625,9 +644,12 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.buttonClear.clicked.connect(self.extensionsClear) self.form.buttonDisable.clicked.connect(self.extensionsDisable) self.form.buttonEnable.clicked.connect(self.extensionsEnable) - self.form.defaultLength.editingFinished.connect(self.updateQuantitySpinBoxes) self.form.enableExtensions.toggled.connect(self._enableExtensions) + # These two handlers are needed to provide immediate updates to extension length + self.form.defaultLength.editingFinished.connect(self._applyDefaultLengthChange) + self.form.defaultLength.textChanged.connect(self._defaultLengthChanged) + self.model.itemChanged.connect(self.updateItemEnabled) self.selectionModel = self.form.extensionTree.selectionModel() From 03c56616907bd6fc75f614baead7be3dc0427e6f Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Tue, 5 Apr 2022 17:56:23 -0500 Subject: [PATCH 2/2] Path: Relocate `valueChanged` signal translation to QSB class Moved the translation method from PathFeatureExtensionsGui module to proper location within QuantitySpinBox class in PathGui module. This change adds a missing translation method for passing the `editingFinished` signal to the parent task panel when the user is finished editing a QuantitySpinBox object with an active expression in the Gui. If no expression is active, this new method is dormant. Some cleanup of initial changes related to development of this fix are included. --- .../PathScripts/PathFeatureExtensionsGui.py | 26 +++---------------- src/Mod/Path/PathScripts/PathGui.py | 16 ++++++++++++ 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py index 4933aee0c9..b133c80aff 100644 --- a/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py +++ b/src/Mod/Path/PathScripts/PathFeatureExtensionsGui.py @@ -285,7 +285,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self._initializeExtensions(obj) # Efficiently initialize Extensions self.defaultLength.updateSpinBox() self._getUseOutlineState() # Find `useOutline` checkbox and get its boolean value - self.lastDefaultLength = self.form.defaultLength.text() + self.lastDefaultLength = self.form.defaultLength.text() # set last DL value self.fieldsSet = True # flag to identify initial values set def _initializeExtensions(self, obj): @@ -301,33 +301,16 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.extensionEdit.setDisabled(True) self.setExtensions(self.extensions) - def _isDefaultLengthExpression(self): - """_isDefaultLengthExpression()... Return True if Default Length is determined by an expression.""" - for prop, __ in self.obj.ExpressionEngine: - if prop == "ExtensionLengthDefault": - return True - return False - def _applyDefaultLengthChange(self, index=None): """_applyDefaultLengthChange(index=None)... - Helper method to update Default Length spinbox, and update extensions due to change in Default Length.""" + Helper method to update Default Length spinbox, + and update extensions due to change in Default Length.""" self.defaultLength.updateSpinBox() if self.form.defaultLength.text() != self.lastDefaultLength: self.lastDefaultLength = self.form.defaultLength.text() self._resetCachedExtensions() # Reset extension cache because extension dimensions likely changed self._enableExtensions() # Recalculate extensions - def _defaultLengthChanged(self): - """_defaultLengthChanged()... Slot method for determining if a change in Default Length - value is determined from an expression edit, or a simple spinbox change. If the former, - emit a `editingFinished` signal manually because the Formula Editor window returned - a value to the base SpinBox.""" - if ( - self._isDefaultLengthExpression() - and self.form.defaultLength.text() != self.lastDefaultLength - ): - self.form.defaultLength.editingFinished.emit() - def createItemForBaseModel(self, base, sub, edges, extensions): PathLog.track( base.Label, sub, "+", len(edges), len(base.Shape.getElement(sub).Edges) @@ -645,10 +628,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage): self.form.buttonDisable.clicked.connect(self.extensionsDisable) self.form.buttonEnable.clicked.connect(self.extensionsEnable) self.form.enableExtensions.toggled.connect(self._enableExtensions) - - # These two handlers are needed to provide immediate updates to extension length self.form.defaultLength.editingFinished.connect(self._applyDefaultLengthChange) - self.form.defaultLength.textChanged.connect(self._defaultLengthChanged) self.model.itemChanged.connect(self.updateItemEnabled) diff --git a/src/Mod/Path/PathScripts/PathGui.py b/src/Mod/Path/PathScripts/PathGui.py index a39f506c4f..bc825bc2ce 100644 --- a/src/Mod/Path/PathScripts/PathGui.py +++ b/src/Mod/Path/PathScripts/PathGui.py @@ -125,14 +125,29 @@ class QuantitySpinBox(QtCore.QObject): self.onBeforeChange = onBeforeChange self.prop = None self.obj = obj + self.lastWidgetText = self.widget.text() self.attachTo(obj, prop) self.widget.installEventFilter(self) + # Connect local class method as slot + self.widget.textChanged.connect(self.onWidgetValueChanged) def eventFilter(self, obj, event): if event.type() == QtCore.QEvent.Type.FocusIn: self.updateSpinBox() return False + def onWidgetValueChanged(self): + """onWidgetValueChanged()... Slot method for determining if a change + in widget value is a result of an expression edit, or a simple spinbox change. + If the former, emit a manual `editingFinished` signal because the Formula Editor + window returned a value to the base widget, leaving it in read-only mode, + and finishing the editing of the value. Otherwise, due nothing if the value + has not changed, or there is no active expression for the property. + If the user closes the Formula Editor to cancel the edit, the value will not + be changed, and this manual signal will not be emitted.""" + if self._hasExpression() and self.widget.text() != self.lastWidgetText: + self.widget.editingFinished.emit() + def attachTo(self, obj, prop=None): """attachTo(obj, prop=None) ... use an existing editor for the given object and property""" PathLog.track(self.prop, prop) @@ -180,6 +195,7 @@ class QuantitySpinBox(QtCore.QObject): quantity = PathUtil.getProperty(self.obj, self.prop) value = quantity.Value if hasattr(quantity, "Value") else quantity self.widget.setProperty("rawValue", value) + self.lastWidgetText = self.widget.text() # update last widget value if expr: self.widget.setReadOnly(True) self.widget.setStyleSheet("color: gray")