285 lines
12 KiB
Python
285 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# ***************************************************************************
|
|
# * 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 *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
"""Widget for editing a ToolBit object."""
|
|
|
|
from functools import partial
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
from PySide import QtGui, QtCore
|
|
from ..models.base import ToolBit
|
|
from ...shape.ui.shapewidget import ShapeWidget
|
|
from ...ui.docobject import DocumentObjectEditorWidget
|
|
|
|
|
|
class ToolBitPropertiesWidget(QtGui.QWidget):
|
|
"""
|
|
A composite widget for editing the properties and shape of a ToolBit.
|
|
"""
|
|
|
|
# Signal emitted when the toolbit data has been modified
|
|
toolBitChanged = QtCore.Signal()
|
|
|
|
def __init__(self, toolbit: ToolBit | None = None, parent=None, icon: bool = True):
|
|
super().__init__(parent)
|
|
self._toolbit = None
|
|
self._show_shape = icon
|
|
|
|
# UI Elements
|
|
self._label_edit = QtGui.QLineEdit()
|
|
self._id_label = QtGui.QLabel() # Read-only ID
|
|
self._id_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
|
|
|
|
theicon = toolbit.get_icon() if toolbit else None
|
|
abbr = theicon.abbreviations if theicon else {}
|
|
self._property_editor = DocumentObjectEditorWidget(property_suffixes=abbr)
|
|
self._property_editor.setSizePolicy(
|
|
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding
|
|
)
|
|
self._shape_widget = None # Will be created in load_toolbit
|
|
|
|
# Layout
|
|
toolbit_group_box = QtGui.QGroupBox(FreeCAD.Qt.translate("CAM", "Tool Bit"))
|
|
form_layout = QtGui.QFormLayout(toolbit_group_box)
|
|
form_layout.addRow("Label:", self._label_edit)
|
|
form_layout.addRow("ID:", self._id_label)
|
|
|
|
main_layout = QtGui.QVBoxLayout(self)
|
|
main_layout.addWidget(toolbit_group_box)
|
|
|
|
properties_group_box = QtGui.QGroupBox(FreeCAD.Qt.translate("CAM", "Properties"))
|
|
properties_group_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
|
|
properties_layout = QtGui.QVBoxLayout(properties_group_box)
|
|
properties_layout.setSpacing(5)
|
|
properties_layout.addWidget(self._property_editor)
|
|
|
|
# Ensure the layout expands horizontally
|
|
properties_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
|
|
|
|
# Set stretch factor to make property editor expand
|
|
properties_layout.setStretchFactor(self._property_editor, 1)
|
|
|
|
main_layout.addWidget(properties_group_box)
|
|
|
|
# Add stretch before shape widget to push it towards the bottom
|
|
main_layout.addStretch(1)
|
|
|
|
# Layout for centering the shape widget (created later)
|
|
self._shape_display_layout = QtGui.QHBoxLayout()
|
|
self._shape_display_layout.addStretch(1)
|
|
|
|
# Placeholder for the widget
|
|
self._shape_display_layout.addStretch(1)
|
|
main_layout.addLayout(self._shape_display_layout)
|
|
|
|
# Connections
|
|
self._label_edit.editingFinished.connect(self._on_label_changed)
|
|
self._property_editor.propertyChanged.connect(self.toolBitChanged)
|
|
|
|
if toolbit:
|
|
self.load_toolbit(toolbit)
|
|
|
|
def _on_label_changed(self):
|
|
"""Update the toolbit's label when the line edit changes."""
|
|
if self._toolbit and self._toolbit.obj:
|
|
new_label = self._label_edit.text()
|
|
if self._toolbit.obj.Label != new_label:
|
|
self._toolbit.obj.Label = new_label
|
|
self.toolBitChanged.emit()
|
|
|
|
def load_toolbit(self, toolbit: ToolBit):
|
|
"""Load a ToolBit object into the editor."""
|
|
self._toolbit = toolbit
|
|
if not self._toolbit or not self._toolbit.obj:
|
|
# Clear or disable fields if toolbit is invalid
|
|
self._label_edit.clear()
|
|
self._label_edit.setEnabled(False)
|
|
self._id_label.clear()
|
|
self._property_editor.setObject(None)
|
|
# Clear existing shape widget if any
|
|
if self._shape_widget:
|
|
self._shape_display_layout.removeWidget(self._shape_widget)
|
|
self._shape_widget.deleteLater()
|
|
self._shape_widget = None
|
|
self.setEnabled(False)
|
|
return
|
|
|
|
self.setEnabled(True)
|
|
self._label_edit.setEnabled(True)
|
|
self._label_edit.setText(self._toolbit.obj.Label)
|
|
self._id_label.setText(self._toolbit.get_id())
|
|
|
|
# Get properties and suffixes
|
|
props_to_show = self._toolbit._get_props(("Shape", "Attributes"))
|
|
icon = self._toolbit._tool_bit_shape.get_icon()
|
|
suffixes = icon.abbreviations if icon else {}
|
|
self._property_editor.setObject(self._toolbit.obj)
|
|
self._property_editor.setPropertiesToShow(props_to_show, suffixes)
|
|
|
|
# Clear old shape widget and create/add new one if shape exists
|
|
if self._shape_widget:
|
|
self._shape_display_layout.removeWidget(self._shape_widget)
|
|
self._shape_widget.deleteLater()
|
|
self._shape_widget = None
|
|
|
|
if self._show_shape and self._toolbit._tool_bit_shape:
|
|
self._shape_widget = ShapeWidget(shape=self._toolbit._tool_bit_shape, parent=self)
|
|
self._shape_widget.setMinimumSize(200, 150)
|
|
# Insert into the middle slot of the HBox layout
|
|
self._shape_display_layout.insertWidget(1, self._shape_widget)
|
|
|
|
def save_toolbit(self):
|
|
"""
|
|
Applies changes from the editor widgets back to the ToolBit object.
|
|
Note: Most changes are applied via signals, but this can be called
|
|
for explicit save actions.
|
|
"""
|
|
# Ensure label is updated if focus is lost without pressing Enter
|
|
self._on_label_changed()
|
|
|
|
# No need to explicitly save the toolbit object itself here,
|
|
# as properties were modified directly on toolbit.obj
|
|
|
|
|
|
class ToolBitEditorPanel(QtGui.QWidget):
|
|
"""
|
|
A widget for editing a ToolBit object, wrapping ToolBitEditorWidget
|
|
and providing standard dialog buttons.
|
|
"""
|
|
|
|
# Signals
|
|
accepted = QtCore.Signal(ToolBit)
|
|
rejected = QtCore.Signal()
|
|
toolBitChanged = QtCore.Signal() # Re-emit signal from inner widget
|
|
|
|
def __init__(self, toolbit: ToolBit | None = None, parent=None):
|
|
super().__init__(parent)
|
|
|
|
# Create the main editor widget
|
|
self._editor_widget = ToolBitPropertiesWidget(toolbit, self)
|
|
|
|
# Create the button box
|
|
buttons = QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel
|
|
self._button_box = QtGui.QDialogButtonBox(buttons)
|
|
|
|
# Connect button box signals to custom signals
|
|
self._button_box.accepted.connect(self._accepted)
|
|
self._button_box.rejected.connect(self.rejected.emit)
|
|
|
|
# Layout
|
|
main_layout = QtGui.QVBoxLayout(self)
|
|
main_layout.addWidget(self._editor_widget)
|
|
main_layout.addWidget(self._button_box)
|
|
|
|
# Connect the toolBitChanged signal from the inner widget
|
|
self._editor_widget.toolBitChanged.connect(self.toolBitChanged)
|
|
|
|
def _accepted(self):
|
|
self.accepted.emit(self._editor_widget._toolbit)
|
|
|
|
def load_toolbit(self, toolbit: ToolBit):
|
|
"""Load a ToolBit object into the editor."""
|
|
self._editor_widget.load_toolbit(toolbit)
|
|
|
|
def save_toolbit(self):
|
|
"""Applies changes from the editor widgets back to the ToolBit object."""
|
|
self._editor_widget.save_toolbit()
|
|
|
|
|
|
class ToolBitEditor(QtGui.QWidget):
|
|
"""
|
|
A widget for editing a ToolBit object, wrapping ToolBitEditorWidget
|
|
and providing standard dialog buttons.
|
|
"""
|
|
|
|
# Signals
|
|
toolBitChanged = QtCore.Signal() # Re-emit signal from inner widget
|
|
|
|
def __init__(self, toolbit: ToolBit, parent=None):
|
|
super().__init__(parent)
|
|
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitEditor.ui")
|
|
|
|
self.toolbit = toolbit
|
|
# self.tool_no = tool_no
|
|
self.default_title = self.form.windowTitle()
|
|
|
|
# Get first tab from the form, add the shape widget at the top.
|
|
tool_tab_layout = self.form.toolTabLayout
|
|
widget = ShapeWidget(toolbit._tool_bit_shape)
|
|
tool_tab_layout.addWidget(widget)
|
|
|
|
# Add tool properties editor to the same tab.
|
|
props = ToolBitPropertiesWidget(toolbit, self, icon=False)
|
|
props.toolBitChanged.connect(self._update)
|
|
# props.toolNoChanged.connect(self._on_tool_no_changed)
|
|
tool_tab_layout.addWidget(props)
|
|
|
|
self.form.tabWidget.setCurrentIndex(0)
|
|
self.form.tabWidget.currentChanged.connect(self._on_tab_switched)
|
|
|
|
# Hide second tab (tool notes) for now.
|
|
self.form.tabWidget.setTabVisible(1, False)
|
|
|
|
# Feeds & Speeds
|
|
self.feeds_tab_idx = None
|
|
"""
|
|
TODO: disabled for now.
|
|
if tool.supports_feeds_and_speeds():
|
|
label = translate('CAM', 'Feeds && Speeds')
|
|
self.feeds = FeedsAndSpeedsWidget(db, serializer, tool, parent=self)
|
|
self.feeds_tab_idx = self.form.tabWidget.insertTab(1, self.feeds, label)
|
|
else:
|
|
self.feeds = None
|
|
self.feeds_tab_idx = None
|
|
|
|
self.form.lineEditCoating.setText(toolbit.get_coating())
|
|
self.form.lineEditCoating.textChanged.connect(toolbit.set_coating)
|
|
self.form.lineEditHardness.setText(toolbit.get_hardness())
|
|
self.form.lineEditHardness.textChanged.connect(toolbit.set_hardness)
|
|
self.form.lineEditMaterials.setText(toolbit.get_materials())
|
|
self.form.lineEditMaterials.textChanged.connect(toolbit.set_materials)
|
|
self.form.lineEditSupplier.setText(toolbit.get_supplier())
|
|
self.form.lineEditSupplier.textChanged.connect(toolbit.set_supplier)
|
|
self.form.plainTextEditNotes.setPlainText(tool.get_notes())
|
|
self.form.plainTextEditNotes.textChanged.connect(self._on_notes_changed)
|
|
"""
|
|
|
|
def _update(self):
|
|
title = self.default_title
|
|
tool_name = self.toolbit.label
|
|
if tool_name:
|
|
title = "{} - {}".format(tool_name, title)
|
|
self.form.setWindowTitle(title)
|
|
|
|
def _on_tab_switched(self, index):
|
|
if index == self.feeds_tab_idx:
|
|
self.feeds.update()
|
|
|
|
def _on_notes_changed(self):
|
|
self.toolbit.set_notes(self.form.plainTextEditNotes.toPlainText())
|
|
|
|
def _on_tool_no_changed(self, value):
|
|
self.tool_no = value
|
|
|
|
def show(self):
|
|
return self.form.exec_()
|