Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A collection of small ConfigureGUI modifications #68

Merged
merged 8 commits into from
Jan 14, 2025
125 changes: 116 additions & 9 deletions bapsf_motion/gui/configure/motion_group_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio
import logging
import warnings

from PySide6.QtCore import Qt, Signal, Slot, QSize
from PySide6.QtGui import QDoubleValidator
Expand Down Expand Up @@ -39,6 +40,9 @@
class AxisControlWidget(QWidget):
axisLinked = Signal()
axisUnlinked = Signal()
movementStarted = Signal(int)
movementStopped = Signal(int)
axisStatusChanged = Signal()

def __init__(self, parent=None):
super().__init__(parent)
Expand Down Expand Up @@ -255,6 +259,10 @@ def link_axis(self, mg: MotionGroup, ax_index: int):

self.axis_name_label.setText(self.axis.name)
self.axis.motor.status_changed.connect(self._update_display_of_axis_status)
self.axis.motor.status_changed.connect(self.axisStatusChanged.emit)
self.axis.motor.movement_started.connect(self._emit_movement_started)
self.axis.motor.movement_finished.connect(self._emit_movement_finished)
self.axis.motor.movement_finished.connect(self._update_display_of_axis_status)
self._update_display_of_axis_status()

self.axisLinked.emit()
Expand All @@ -263,17 +271,41 @@ def unlink_axis(self):
if self.axis is not None:
# self.axis.terminate(delay_loop_stop=True)
self.axis.motor.status_changed.disconnect(self._update_display_of_axis_status)
self.axis.motor.status_changed.connect(self.axisStatusChanged.emit)
self.axis.motor.movement_started.connect(self._emit_movement_started)
self.axis.motor.movement_finished.connect(self._emit_movement_finished)
self.axis.motor.movement_finished.disconnect(
self._update_display_of_axis_status
)

self._mg = None
self._axis_index = None
self.axisUnlinked.emit()

def _emit_movement_started(self):
self.movementStarted.emit(self.axis_index)

def _emit_movement_finished(self):
self.movementStopped.emit(self.axis_index)

def closeEvent(self, event):
self.logger.info("Closing AxisControlWidget")

if isinstance(self.axis, Axis):
self.axis.motor.status_changed.disconnect(self._update_display_of_axis_status)
self.axis.motor.status_changed.disconnect(self.axisStatusChanged.emit)
self.axis.motor.movement_started.connect(self._emit_movement_started)
self.axis.motor.movement_finished.connect(self._emit_movement_finished)
self.axis.motor.movement_finished.disconnect(
self._update_display_of_axis_status
)

event.accept()


class DriveControlWidget(QWidget):
movementStarted = Signal()
movementStopped = Signal()

def __init__(self, parent=None):
super().__init__(parent)
Expand Down Expand Up @@ -474,6 +506,9 @@ def link_motion_group(self, mg):
for ii, ax in enumerate(self.mg.drive.axes):
acw = self._axis_control_widgets[ii]
acw.link_axis(self.mg, ii)
acw.movementStarted.connect(self._drive_movement_started)
acw.movementStopped.connect(self._drive_movement_finished)
acw.axisStatusChanged.connect(self._update_all_axis_displays)
acw.show()

self.setEnabled(not self._mg.terminated)
Expand All @@ -483,12 +518,42 @@ def unlink_motion_group(self):
visible = True if ii == 0 else False

acw.unlink_axis()

with warnings.catch_warnings():
warnings.simplefilter("ignore", category=RuntimeWarning)
acw.movementStarted.disconnect(self._drive_movement_started)
acw.movementStopped.disconnect(self._drive_movement_finished)
acw.axisStatusChanged.disconnect(self._update_all_axis_displays)

acw.setVisible(visible)

# self.mg.terminate(delay_loop_stop=True)
self._mg = None
self.setEnabled(False)

def _update_all_axis_displays(self):
for acw in self._axis_control_widgets:
if acw.isHidden():
continue
elif acw.axis.is_moving:
continue

acw._update_display_of_axis_status()

@Slot(int)
def _drive_movement_started(self, axis_index):
self.movementStarted.emit()

@Slot(int)
def _drive_movement_finished(self, axis_index):
if not isinstance(self.mg, MotionGroup) or not isinstance(self.mg.drive, Drive):
return

is_moving = [ax.is_moving for ax in self.mg.drive.axes]
is_moving[axis_index] = False
if not any(is_moving):
self.movementStopped.emit()

def closeEvent(self, event):
self.logger.info("Closing DriveControlWidget")
event.accept()
Expand Down Expand Up @@ -531,7 +596,6 @@ def __init__(
"ips": deployed_ips,
}


self._logger = gui_logger

self._mg = None
Expand Down Expand Up @@ -682,8 +746,8 @@ def __init__(
# if MGWidget launched without a drive then use a default
# drive (if defined)
if (
"drive" not in self.mg_config
and self.drive_defaults[0][0] != "Custom Drive"
"drive" not in self.mg_config
and self.drive_defaults[0][0] != "Custom Drive"
):
self._mg_config["drive"] = _deepcopy_dict(self.drive_defaults[0][1])

Expand Down Expand Up @@ -734,12 +798,7 @@ def _connect_signals(self):

self.mg_name_widget.editingFinished.connect(self._rename_motion_group)

self.configChanged.connect(self._update_toml_widget)
self.configChanged.connect(self._update_mg_name_widget)
self.configChanged.connect(self._validate_motion_group)
self.configChanged.connect(self._update_drive_dropdown)
self.configChanged.connect(self._update_mb_dropdown)
self.configChanged.connect(self._update_transform_dropdown)
self.configChanged.connect(self._config_changed_handler)

self.drive_dropdown.currentIndexChanged.connect(
self._drive_dropdown_new_selection
Expand All @@ -751,6 +810,9 @@ def _connect_signals(self):
self._transform_dropdown_new_selection
)

self.drive_control_widget.movementStarted.connect(self.disable_config_controls)
self.drive_control_widget.movementStopped.connect(self.enable_config_controls)

self.done_btn.clicked.connect(self.return_and_close)
self.discard_btn.clicked.connect(self.close)

Expand Down Expand Up @@ -987,6 +1049,22 @@ def _build_transform_defaults(self):

return self._transform_defaults

def _config_changed_handler(self):
# Note: none of the methods executed here should cause a
# configChanged event
self._validate_motion_group()

# now update displays
self._update_mg_name_widget()
self._update_toml_widget()
self._update_drive_dropdown()
self._update_mb_dropdown()
self._update_transform_dropdown()

# updating the drive control widget should always be the last
# step
self._update_drive_control_widget()

def _populate_drive_dropdown(self):
for item in self.drive_defaults:
self.drive_dropdown.addItem(item[0])
Expand Down Expand Up @@ -1354,6 +1432,15 @@ def _update_toml_widget(self):
def _update_mg_name_widget(self):
self.mg_name_widget.setText(self.mg_config["name"])

def _update_drive_control_widget(self):
if not self.drive_control_widget.isEnabled():
return

if self.drive_control_widget.mg is None:
self._refresh_drive_control()
else:
self.drive_control_widget._update_all_axis_displays()

def _rename_motion_group(self):
self.logger.info("Renaming motion group")
self.mg.config["name"] = self.mg_name_widget.text()
Expand Down Expand Up @@ -1624,6 +1711,26 @@ def _transform_dropdown_new_selection(self, index):

self._change_transform(tr_default_config)

def disable_config_controls(self):
self.drive_dropdown.setEnabled(False)
self.drive_btn.setEnabled(False)

self.mb_dropdown.setEnabled(False)
self.mb_btn.setEnabled(False)

self.transform_dropdown.setEnabled(False)
self.transform_btn.setEnabled(False)

def enable_config_controls(self):
self.drive_dropdown.setEnabled(True)
self.drive_btn.setEnabled(True)

self.mb_dropdown.setEnabled(True)
self.mb_btn.setEnabled(True)

self.transform_dropdown.setEnabled(True)
self.transform_btn.setEnabled(True)

def return_and_close(self):
config = _deepcopy_dict(self.mg.config)
index = -1 if self._mg_index is None else self._mg_index
Expand Down
Loading