Download src/Mod/CAM/Path/Tool/docobject/ui/property.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/docobject/ui/property.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Tool/docobject/ui/property.py
-
curl -L -o property.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/docobject/ui/property.py
11.9 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2025 Samuel Abels <knipknap@gmail.com> * | |
| # * * | |
| # * 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 * | |
| # * * | |
| # *************************************************************************** | |
| """ | |
| Widgets for editing specific types of DocumentObject properties. | |
| Includes a factory method to create the appropriate widget based on type. | |
| """ | |
| import FreeCAD | |
| import FreeCADGui | |
| from PySide import QtGui, QtCore | |
| from typing import Optional | |
| class BasePropertyEditorWidget(QtGui.QWidget): | |
| """ | |
| Base class for property editor widgets. Includes a factory method | |
| to create specific subclasses based on property type. | |
| """ | |
| # Signal emitted when the underlying property value might have changed | |
| propertyChanged = QtCore.Signal() | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(parent) | |
| self._obj = obj | |
| self._prop_name = prop_name | |
| self._layout = QtGui.QHBoxLayout(self) | |
| self._layout.setContentsMargins(0, 0, 0, 0) | |
| self._editor_widget: QtGui.QWidget = None # The actual input widget (SpinBox, ComboBox) | |
| self._editor_mode: int = 0 # Default to editable | |
| self._is_read_only: bool = False | |
| self._update_editor_mode() | |
| self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) | |
| def attachTo(self, obj: FreeCAD.DocumentObject, prop_name: Optional[str] = None): | |
| """Attach the editor to a (potentially different) object/property.""" | |
| self._obj = obj | |
| self._prop_name = prop_name if prop_name else self._prop_name | |
| self._update_editor_mode() | |
| self.updateWidget() # Ensure widget reflects new state | |
| def _update_editor_mode(self): | |
| """Fetch and store the current editor mode for the property.""" | |
| if self._obj and self._prop_name: | |
| self._editor_mode = self._obj.getEditorMode(self._prop_name) | |
| self._is_read_only = self._editor_mode == 2 | |
| return | |
| self._editor_mode = 0 | |
| self._is_read_only = False | |
| def updateWidget(self): | |
| """Update the editor widget's display from the object property.""" | |
| # Implementation specific to subclasses | |
| raise NotImplementedError | |
| def updateProperty(self): | |
| """Update the object property from the editor widget's value.""" | |
| # Implementation specific to subclasses | |
| # Should emit propertyChanged signal if value actually changes | |
| raise NotImplementedError | |
| def for_property( | |
| cls, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None | |
| ) -> "BasePropertyEditorWidget": | |
| """ | |
| Factory method to create the appropriate editor widget subclass. | |
| """ | |
| if not obj or not hasattr(obj, "getPropertyByName"): | |
| return LabelPropertyEditorWidget(obj, prop_name, parent) | |
| prop_value = obj.getPropertyByName(prop_name) | |
| prop_type = obj.getTypeIdOfProperty(prop_name) | |
| if isinstance(prop_value, FreeCAD.Units.Quantity): | |
| return QuantityPropertyEditorWidget(obj, prop_name, parent) | |
| elif isinstance(prop_value, bool): | |
| return BoolPropertyEditorWidget(obj, prop_name, parent) | |
| elif isinstance(prop_value, int): | |
| return IntPropertyEditorWidget(obj, prop_name, parent) | |
| elif prop_type == "App::PropertyEnumeration": | |
| return EnumPropertyEditorWidget(obj, prop_name, parent) | |
| else: | |
| # Default to a read-only label for other types | |
| return LabelPropertyEditorWidget(obj, prop_name, parent) | |
| class QuantityPropertyEditorWidget(BasePropertyEditorWidget): | |
| """Editor widget for Quantity properties.""" | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(obj, prop_name, parent) | |
| ui = FreeCADGui.UiLoader() | |
| self._editor_widget: FreeCADGui.QuantitySpinBox = ui.createWidget("Gui::QuantitySpinBox") | |
| self._layout.addWidget(self._editor_widget) | |
| self.updateWidget() # Set initial value | |
| # Connect signal after setting initial value to avoid premature update | |
| self._editor_widget.editingFinished.connect(self.updateProperty) | |
| def updateWidget(self): | |
| value: FreeCAD.Units.Quantity = self._obj.getPropertyByName(self._prop_name) | |
| # Block signals temporarily to prevent feedback loops | |
| self._editor_widget.blockSignals(True) | |
| self._editor_widget.setProperty("value", value) | |
| self._editor_widget.blockSignals(False) | |
| self._editor_widget.setEnabled(not self._is_read_only) | |
| def updateProperty(self): | |
| current_value = self._obj.getPropertyByName(self._prop_name) | |
| new_value_str: str = self._editor_widget.property("value").UserString | |
| new_value = FreeCAD.Units.Quantity(new_value_str) | |
| if new_value_str != current_value: | |
| self._obj.setPropertyByName(self._prop_name, new_value) | |
| self.propertyChanged.emit() | |
| class BoolPropertyEditorWidget(BasePropertyEditorWidget): | |
| """Editor widget for Boolean properties.""" | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(obj, prop_name, parent) | |
| self._editor_widget: QtGui.QComboBox = QtGui.QComboBox() | |
| self._editor_widget.addItems(["False", "True"]) | |
| self._layout.addWidget(self._editor_widget) | |
| self.updateWidget() | |
| self._editor_widget.currentIndexChanged.connect(self._on_index_changed) | |
| def updateWidget(self): | |
| value: bool = self._obj.getPropertyByName(self._prop_name) | |
| self._editor_widget.blockSignals(True) | |
| self._editor_widget.setCurrentIndex(1 if value else 0) | |
| self._editor_widget.blockSignals(False) | |
| self._editor_widget.setEnabled(not self._is_read_only) | |
| def _on_index_changed(self, index: int): | |
| """Slot connected to currentIndexChanged signal.""" | |
| if self._is_read_only: | |
| return | |
| current_value: bool = self._obj.getPropertyByName(self._prop_name) | |
| new_value: bool = bool(index) | |
| if new_value != current_value: | |
| self._obj.setPropertyByName(self._prop_name, new_value) | |
| self.propertyChanged.emit() | |
| def updateProperty(self): | |
| """Update property based on current widget state (for consistency).""" | |
| self._on_index_changed(self._editor_widget.currentIndex()) | |
| class IntPropertyEditorWidget(BasePropertyEditorWidget): | |
| """Editor widget for Integer properties.""" | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(obj, prop_name, parent) | |
| self._editor_widget: QtGui.QSpinBox = QtGui.QSpinBox() | |
| self._editor_widget.setMinimum(-2147483648) | |
| self._editor_widget.setMaximum(2147483647) | |
| self._layout.addWidget(self._editor_widget) | |
| self.updateWidget() | |
| self._editor_widget.editingFinished.connect(self.updateProperty) | |
| def updateWidget(self): | |
| value = self._obj.getPropertyByName(self._prop_name) | |
| self._editor_widget.blockSignals(True) | |
| self._editor_widget.setValue(value or 0) | |
| self._editor_widget.blockSignals(False) | |
| self._editor_widget.setEnabled(not self._is_read_only) | |
| def updateProperty(self): | |
| current_value: int = self._obj.getPropertyByName(self._prop_name) | |
| new_value: int = self._editor_widget.value() | |
| if new_value != current_value: | |
| self._obj.setPropertyByName(self._prop_name, new_value) | |
| self.propertyChanged.emit() | |
| class EnumPropertyEditorWidget(BasePropertyEditorWidget): | |
| """Editor widget for Enumeration properties.""" | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(obj, prop_name, parent) | |
| self._editor_widget: QtGui.QComboBox = QtGui.QComboBox() | |
| self._layout.addWidget(self._editor_widget) | |
| self._populate_enum() | |
| self.updateWidget() | |
| self._editor_widget.currentIndexChanged.connect(self._on_index_changed) | |
| def _populate_enum(self): | |
| self._editor_widget.clear() | |
| enums: list[str] = self._obj.getEnumerationsOfProperty(self._prop_name) | |
| self._editor_widget.addItems(enums) | |
| def attachTo(self, obj: FreeCAD.DocumentObject, prop_name: Optional[str] = None): | |
| """Override attachTo to repopulate enums if object changes.""" | |
| super().attachTo(obj, prop_name) | |
| self._populate_enum() # Repopulate in case enums are different | |
| def updateWidget(self): | |
| value: str = self._obj.getPropertyByName(self._prop_name) | |
| self._editor_widget.blockSignals(True) | |
| index: int = self._editor_widget.findText(value) | |
| self._editor_widget.setCurrentIndex(index if index >= 0 else 0) | |
| self._editor_widget.blockSignals(False) | |
| self._editor_widget.setEnabled(not self._is_read_only) | |
| def _on_index_changed(self, index: int): | |
| """Slot connected to currentIndexChanged signal.""" | |
| if self._is_read_only: | |
| return | |
| current_value: str = self._obj.getPropertyByName(self._prop_name) | |
| new_value: str = self._editor_widget.itemText(index) | |
| if new_value != current_value: | |
| self._obj.setPropertyByName(self._prop_name, new_value) | |
| self.propertyChanged.emit() | |
| def updateProperty(self): | |
| """Update property based on current widget state (for consistency).""" | |
| self._on_index_changed(self._editor_widget.currentIndex()) | |
| class LabelPropertyEditorWidget(BasePropertyEditorWidget): | |
| """Read-only label for unsupported or invalid property types.""" | |
| def __init__(self, obj: FreeCAD.DocumentObject, prop_name: str, parent: QtGui.QWidget = None): | |
| super().__init__(obj, prop_name, parent) | |
| self._editor_widget: QtGui.QLabel = QtGui.QLabel("N/A") | |
| self._editor_widget.setTextInteractionFlags( | |
| QtGui.Qt.TextSelectableByMouse | QtGui.Qt.TextSelectableByKeyboard | |
| ) | |
| self._layout.addWidget(self._editor_widget) | |
| self.updateWidget() | |
| def updateWidget(self): | |
| text = "N/A" | |
| text: str = str(self._obj.getPropertyByName(self._prop_name)) | |
| self._editor_widget.setText(text) | |
| def updateProperty(self): | |
| # Read-only, no action needed | |
| pass | |