Files
probe-basic-sim/configs/probe_basic/user_tabs/template_sidebar/template_sidebar.py
2025-12-29 08:25:34 +01:00

151 lines
5.3 KiB
Python

import os
import linuxcnc
from enum import Enum
from qtpy import uic
from qtpy.QtCore import Qt
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__)
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)