diff --git a/user_tabs/template_sidebar/template_sidebar.py b/user_tabs/template_sidebar/template_sidebar.py index 1bcd807..7112b6a 100644 --- a/user_tabs/template_sidebar/template_sidebar.py +++ b/user_tabs/template_sidebar/template_sidebar.py @@ -1,12 +1,15 @@ import os import linuxcnc +from enum import Enum from qtpy import uic from qtpy.QtCore import Qt -from qtpy.QtWidgets import QWidget +from qtpy.QtWidgets import QWidget, QPushButton +from qtpyvcp.actions import machine_actions from qtpyvcp.plugins import getPlugin from qtpyvcp.utilities import logger +from widgets.conversational.float_line_edit import FloatLineEdit LOG = logger.getLogger(__name__) @@ -14,10 +17,135 @@ STATUS = getPlugin('status') TOOL_TABLE = getPlugin('tooltable') INI_FILE = linuxcnc.ini(os.getenv('INI_FILE_NAME')) +CMD = linuxcnc.command() + + +class MoveMode(Enum): + """Defines the available movement modes (absolute, work coordinate system, relative).""" + ABS = 1 + WCS = 2 + REL = 3 class UserTab(QWidget): + """A sidebar widget for controlling machine axis movements.""" + def __init__(self, parent=None): super(UserTab, self).__init__(parent) ui_file = os.path.splitext(os.path.basename(__file__))[0] + ".ui" uic.loadUi(os.path.join(os.path.dirname(__file__), ui_file), self) + + self.position_inputs: list[FloatLineEdit] = self.findChildren(FloatLineEdit) + self.move_buttons: list[QPushButton] = self.findChildren(QPushButton) + + self._connect_signals() + self.update_wcs_label() + + def _connect_signals(self): + """Connects widget signals to corresponding slots.""" + STATUS.on.notify(self._update_control_states) + STATUS.all_axes_homed.notify(self._update_control_states) + STATUS.g5x_index.notify(self.update_wcs_label) + + for line_edit in self.position_inputs: + line_edit.setEnabled(False) + line_edit.editingFinished.connect(self._format_sender_float_value) + + for button in self.move_buttons: + button.setEnabled(False) + button.clicked.connect(self._on_move_button_clicked) + + def _format_sender_float_value(self): + """Formats the text of the sending FloatLineEdit to its specified format.""" + sender_widget = self.sender() + if not isinstance(sender_widget, FloatLineEdit): + return + + LOG.debug(f"Formatting float for {sender_widget.objectName()}") + value = sender_widget.value() + formatted_value = sender_widget.format_string.format(value) + sender_widget.setText(formatted_value) + + def update_wcs_label(self, *args): + """Updates the WCS status label with the current coordinate system.""" + wcs_index = STATUS.g5x_index + self.wcs_status_label.setText(f"WCS {wcs_index}") + + def _on_move_button_clicked(self): + """Handles clicks on any of the move buttons.""" + sender = self.sender() + if not isinstance(sender, QPushButton): + return + + button_name = sender.objectName() + LOG.debug(f"Move button clicked: {button_name}") + + try: + _, mode_str, axes_str = button_name.replace('_button', '').split('_') + except ValueError: + LOG.error(f"Could not parse button name: {button_name}") + return + + move_configs = { + 'abs': (MoveMode.ABS, self.abs_x_input, self.abs_y_input, self.abs_z_input), + 'wcs': (MoveMode.WCS, self.wcs_x_input, self.wcs_y_input, self.wcs_z_input), + 'rel': (MoveMode.REL, self.rel_x_input, self.rel_y_input, self.rel_z_input) + } + + if mode_str not in move_configs: + LOG.error(f"Unknown move mode '{mode_str}' in button name: {button_name}") + return + + mode, x_input, y_input, z_input = move_configs[mode_str] + + x, y, z = None, None, None + if 'x' in axes_str or 'all' in axes_str: + x = x_input.value() + if 'y' in axes_str or 'all' in axes_str: + y = y_input.value() + if 'z' in axes_str or 'all' in axes_str: + z = z_input.value() + + self._execute_move_command(mode, x, y, z) + LOG.debug(f"Executed move: {mode.name}, X={x}, Y={y}, Z={z}") + + def _execute_move_command(self, mode: MoveMode, x=None, y=None, z=None): + """Builds and sends an MDI command for the specified movement.""" + mdi_command = "" + match mode: + case MoveMode.ABS: + mdi_command = "G53 G0" + case MoveMode.WCS: + mdi_command = "G0" + case MoveMode.REL: + mdi_command = "G91 G0" + + if x is not None: + mdi_command += f" X{x}" + if y is not None: + mdi_command += f" Y{y}" + if z is not None: + mdi_command += f" Z{z}" + + if mode == MoveMode.REL: + mdi_command += " ;G90" + + LOG.debug(f"Sending MDI command: {mdi_command}") + machine_actions.issue_mdi(mdi_command) + + def _is_machine_ready(self) -> bool: + """Checks if the machine is on and all axes are homed.""" + is_ready = STATUS.on() and STATUS.all_axes_homed() + LOG.debug(f"Machine ready check: on={STATUS.on()}, homed={STATUS.all_axes_homed()} -> {is_ready}") + return is_ready + + def _update_control_states(self, *args): + """Enables or disables UI controls based on the machine's ready state.""" + is_ready = self._is_machine_ready() + LOG.debug(f"Updating control states. Machine ready: {is_ready}") + + for button in self.move_buttons: + button.setEnabled(is_ready) + + for line_edit in self.position_inputs: + line_edit.setEnabled(is_ready) \ No newline at end of file diff --git a/user_tabs/template_sidebar/template_sidebar.ui b/user_tabs/template_sidebar/template_sidebar.ui index 6b28ca9..fd7a3dd 100644 --- a/user_tabs/template_sidebar/template_sidebar.ui +++ b/user_tabs/template_sidebar/template_sidebar.ui @@ -1,7 +1,7 @@ - USER - + Move + 0 @@ -22,7 +22,493 @@ true + + + + + + + QLabel{ + color: rgb(238, 238, 236); + font: 16pt "Bebas Kai"; +} + + + ABSOLUTE + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + 0 + + + [{"name": "postion_x", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:abs?string&axis=x", "trigger": true}]}] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO X + + + true + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [{"name": "position_y", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:abs?string&axis=y", "trigger": true}]}] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Y + + + true + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [{"name": "position_z", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:abs?string&axis=z", "trigger": true}]}] + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Z + + + true + + + + + + + + + GO XYZ + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QLabel{ + color: rgb(238, 238, 236); + font: 16pt "Bebas Kai"; +} + + + [] + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + 0 + + + [{"name": "postion_x", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:Relative?string&axis=x", "trigger": true}]}] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO X + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [{"name": "position_y", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:Relative?string&axis=y", "trigger": true}]}] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Y + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [{"name": "position_z", "property": "Text", "expression": "ch[0]", "channels": [{"url": "position:Relative?string&axis=z", "trigger": true}]}] + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Z + + + + + + + + + GO XYZ + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QLabel{ + color: rgb(238, 238, 236); + font: 16pt "Bebas Kai"; +} + + + RELATIVE + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + 0 + + + [] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO X + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [] + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Y + + + + + + + + + + + 0.0000 + + + Qt::AlignCenter + + + [] + + + + + + + + 0 + 0 + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + GO Z + + + + + + + + + GO XYZ + + + true + + + + + + + + + StatusLabel + QLabel +
qtpyvcp.widgets.display_widgets.status_label
+
+ + VCPLineEdit + QLineEdit +
qtpyvcp.widgets.input_widgets.line_edit
+
+ + FloatLineEdit + VCPLineEdit +
widgets.conversational.float_line_edit
+
+