# -*- coding: utf-8 -*-
"""
Created the 28/07/2022
@author: Sebastien Weber
"""
from typing import List
import sys
from qtpy import QtWidgets
from qtpy.QtCore import Signal
from qtpy.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout, QWidget, QToolBar, QComboBox
from pymodaq.utils.daq_utils import ThreadCommand
from pymodaq.utils.gui_utils.custom_app import CustomApp
from pymodaq.utils.gui_utils.widgets import PushButtonIcon, LabelWithFont, SpinBox, QSpinBox_ro, QLED
from pymodaq.control_modules.utils import ControlModuleUI
from pymodaq.utils.gui_utils import DockArea
from pymodaq.utils.plotting.data_viewers.viewer import ViewerDispatcher
from pymodaq.utils.data import DataWithAxes, DataToExport, DataActuator
[docs]class DAQ_Move_UI(ControlModuleUI):
"""DAQ_Move user interface.
This class manages the UI and emit dedicated signals depending on actions from the user
Attributes
----------
command_sig: Signal[Threadcommand]
This signal is emitted whenever some actions done by the user has to be
applied on the main module. Possible commands are:
* init
* quit
* get_value
* loop_get_value
* find_home
* stop
* move_abs
* move_rel
* show_log
* actuator_changed
* rel_value
* show_config
* show_plugin_config
Methods
-------
display_value(value: float)
Update the display of the actuator's value on the UI
do_init()
Programmatic init
See Also
--------
pymodaq.utils.daq_utils.ThreadCommand
"""
def __init__(self, parent, title="DAQ_Move"):
super().__init__(parent)
self.title = title
self.setup_ui()
self.enable_move_buttons(False)
[docs] def display_value(self, value: DataActuator):
self.current_value_sb.setValue(value.value())
@property
def actuator_init(self):
"""bool: the status of the init LED."""
return self.ini_state_led.get_state()
@actuator_init.setter
def actuator_init(self, status):
self.ini_state_led.set_as(status)
self.enable_move_buttons(status)
@property
def actuator(self):
return self.actuators_combo.currentText()
@actuator.setter
def actuator(self, act_name: str):
self.actuators_combo.setCurrentText(act_name)
@property
def actuators(self):
return [self.actuators_combo.itemText(ind) for ind in range(self.actuators_combo.count())]
@actuators.setter
def actuators(self, actuators: List[str]):
self.actuators_combo.clear()
self.actuators_combo.addItems(actuators)
@property
def move_done(self):
"""bool: the status of the move_done LED."""
return self.move_done_led.get_state()
@move_done.setter
def move_done(self, status):
self.move_done_led.set_as(status)
def set_settings_tree(self, tree):
self.settings_ui.layout().addWidget(tree)
def enable_move_buttons(self, status):
self.abs_value_sb.setEnabled(status)
self.abs_value_sb_2.setEnabled(status)
self.control_ui.setEnabled(status)
self.get_action('move_abs').setEnabled(status)
self.get_action('move_abs_2').setEnabled(status)
[docs] def set_abs_spinbox_properties(self, **properties):
""" Change the Spinbox properties
Parameters
--------
properties: dict or named parameters
possible keys are :
* decimals: to set the number of displayed decimals
* 'minimum': to set the minimum value
* 'maximum': to set the maximum value
* 'step': to set the step value
"""
if 'decimals' in properties:
self.abs_value_sb.setDecimals(properties['decimals'])
self.abs_value_sb_2.setDecimals(properties['decimals'])
self.abs_value_sb_bis.setDecimals(properties['decimals'])
if 'minimum' in properties:
self.abs_value_sb.setMinimum(properties['minimum'])
self.abs_value_sb_2.setMinimum(properties['minimum'])
self.abs_value_sb_bis.setMinimum(properties['minimum'])
if 'maximum' in properties:
self.abs_value_sb.setMaximum(properties['maximum'])
self.abs_value_sb_2.setMaximum(properties['maximum'])
self.abs_value_sb_bis.setMaximum(properties['maximum'])
if 'step' in properties:
self.abs_value_sb.setSingleStep(properties['step'])
self.abs_value_sb_2.setSingleStep(properties['step'])
self.abs_value_sb_bis.setSingleStep(properties['step'])
def show_data(self, data: DataToExport):
self.viewer.show_data(data)
[docs] def setup_docks(self):
self.parent.setLayout(QVBoxLayout())
self.parent.layout().setSizeConstraint(QHBoxLayout.SetFixedSize)
self.parent.layout().setContentsMargins(2, 2, 2, 2)
widget = QWidget()
widget.setLayout(QHBoxLayout())
self.parent.layout().addWidget(widget)
self.main_ui = QWidget()
self.control_ui = QWidget()
self.settings_ui = QWidget()
self.graph_ui = QWidget()
self.graph_ui.setLayout(QtWidgets.QHBoxLayout())
self.graph_ui.layout().setContentsMargins(0, 0, 0, 0)
dockarea = DockArea()
self.graph_ui.layout().addWidget(dockarea)
self.viewer = ViewerDispatcher(dockarea)
left_widget = QWidget()
left_widget.setLayout(QVBoxLayout())
left_widget.layout().addWidget(self.main_ui)
left_widget.layout().addWidget(self.control_ui)
left_widget.layout().setContentsMargins(0, 0, 0, 0)
left_widget.layout().addStretch()
widget.layout().addWidget(left_widget)
widget.layout().addWidget(self.settings_ui)
widget.layout().addStretch()
# populate the main ui
self.move_toolbar = QToolBar()
self.main_ui.setLayout(QGridLayout())
self.main_ui.layout().setSpacing(0)
self.main_ui.layout().setContentsMargins(0, 0, 0, 0)
self.main_ui.layout().addWidget(self.toolbar, 0, 0, 1, 2)
self.main_ui.layout().addWidget(self.move_toolbar, 1, 0, 1, 2)
self.abs_value_sb = SpinBox()
self.abs_value_sb.setStyleSheet("background-color : lightgreen; color: black")
self.abs_value_sb_2 = SpinBox()
self.abs_value_sb_2.setStyleSheet("background-color : lightcoral; color: black")
self.move_toolbar.addWidget(self.abs_value_sb)
self.move_toolbar.addWidget(self.abs_value_sb_2)
self.main_ui.layout().addWidget(LabelWithFont('Actuator:'), 2, 0)
self.actuators_combo = QComboBox()
self.main_ui.layout().addWidget(self.actuators_combo, 2, 1)
self.ini_actuator_pb = PushButtonIcon('ini', 'Initialization', checkable=True,
tip='Start This actuator initialization')
self.main_ui.layout().addWidget(self.ini_actuator_pb, 3, 0)
self.ini_state_led = QLED(readonly=True)
self.main_ui.layout().addWidget(self.ini_state_led, 3, 1)
self.main_ui.layout().addWidget(LabelWithFont('Current value:'), 4, 0)
self.move_done_led = QLED(readonly=True)
self.main_ui.layout().addWidget(self.move_done_led, 4, 1)
self.current_value_sb = QSpinBox_ro(font_size=30, min_height=35)
self.main_ui.layout().addWidget(self.current_value_sb, 5, 0, 1, 2)
# populate the control ui
self.control_ui.setLayout(QGridLayout())
self.control_ui.layout().addWidget(LabelWithFont('Abs. Value'), 0, 0)
self.find_home_pb = PushButtonIcon('home2', 'Find Home')
self.control_ui.layout().addWidget(self.find_home_pb, 0, 1)
self.abs_value_sb_bis = SpinBox()
self.control_ui.layout().addWidget(self.abs_value_sb_bis, 1, 0)
self.move_abs_pb = PushButtonIcon('Move', 'Set Abs.',
tip='Set the value of the actuator to the set absolute value')
self.control_ui.layout().addWidget(self.move_abs_pb, 1, 1)
self.control_ui.layout().addWidget(LabelWithFont('Rel. Increment'), 2, 0)
self.move_rel_plus_pb = PushButtonIcon('MoveUp', 'Set Rel. (+)')
self.control_ui.layout().addWidget(self.move_rel_plus_pb, 2, 1)
self.rel_value_sb = SpinBox()
self.control_ui.layout().addWidget(self.rel_value_sb, 3, 0)
self.move_rel_minus_pb = PushButtonIcon('MoveDown', 'Set Rel. (-)')
self.control_ui.layout().addWidget(self.move_rel_minus_pb, 3, 1)
self.stop_pb = PushButtonIcon('stop', 'Stop')
self.control_ui.layout().addWidget(self.stop_pb, 4, 0)
self.get_value_pb = PushButtonIcon('Help_32', 'Update Value')
self.control_ui.layout().addWidget(self.get_value_pb, 4, 1)
self.control_ui.layout().setContentsMargins(0, 0, 0, 0)
self.settings_ui.setLayout(QHBoxLayout())
self.settings_ui.layout().setContentsMargins(0, 0, 0, 0)
self.control_ui.setVisible(False)
self.settings_ui.setVisible(False)
self.statusbar = QtWidgets.QStatusBar()
self.statusbar.setMaximumHeight(30)
self.parent.layout().addWidget(self.statusbar)
[docs] def setup_actions(self):
self.add_action('move_abs', 'Move Abs', 'go_to_1', "Move to the set absolute value",
toolbar=self.move_toolbar)
self.add_action('move_abs_2', 'Move Abs', 'go_to_2', "Move to the other set absolute value",
toolbar=self.move_toolbar)
self.add_action('show_controls', 'Show Controls', 'Add_Step', "Show more controls", checkable=True,
toolbar=self.toolbar)
self.add_action('show_settings', 'Show Settings', 'tree', "Show Settings", checkable=True,
toolbar=self.toolbar)
self.add_action('show_config', 'Show Config', 'Settings', "Show PyMoDAQ Config", checkable=False,
toolbar=self.toolbar)
self.add_action('show_graph', 'Show Graph', 'graph', "Show Graph", checkable=True,
toolbar=self.toolbar)
self.add_action('refresh_value', 'Refresh', 'Refresh2', "Refresh Value", checkable=True,
toolbar=self.toolbar)
self.add_action('stop', 'Stop', 'stop', "Stop Motion", checkable=False,
toolbar=self.toolbar)
self.add_action('quit', 'Quit the module', 'close2')
self.add_action('log', 'Show Log file', 'information2')
self.toolbar.addWidget(LabelWithFont(self.title, font_name="Tahoma", font_size=14, isbold=True, isitalic=True))
[docs] def connect_things(self):
self.connect_action('show_controls', lambda show: self.control_ui.setVisible(show))
self.connect_action('show_settings', lambda show: self.settings_ui.setVisible(show))
self.connect_action('show_graph', lambda show: self.graph_ui.setVisible(show))
self.connect_action('quit', lambda: self.command_sig.emit(ThreadCommand('quit', )))
self.connect_action('refresh_value',
lambda do_refresh: self.command_sig.emit(ThreadCommand('loop_get_value', do_refresh)))
self.connect_action('move_abs', lambda: self.emit_move_abs(self.abs_value_sb))
self.connect_action('move_abs_2', lambda: self.emit_move_abs(self.abs_value_sb_2))
self.connect_action('log', lambda: self.command_sig.emit(ThreadCommand('show_log', )))
self.connect_action('stop', lambda: self.command_sig.emit(ThreadCommand('stop', )))
self.connect_action('show_config', lambda: self.command_sig.emit(ThreadCommand('show_config', )))
self.move_abs_pb.clicked.connect(lambda: self.emit_move_abs(self.abs_value_sb_bis))
self.rel_value_sb.valueChanged.connect(lambda: self.command_sig.emit(
ThreadCommand('rel_value', self.rel_value_sb.value())))
self.move_rel_plus_pb.clicked.connect(lambda: self.emit_move_rel('+'))
self.move_rel_minus_pb.clicked.connect(lambda: self.emit_move_rel('-'))
self.find_home_pb.clicked.connect(lambda: self.command_sig.emit(ThreadCommand('find_home', )))
self.stop_pb.clicked.connect(lambda: self.command_sig.emit(ThreadCommand('stop', )))
self.get_value_pb.clicked.connect(lambda: self.command_sig.emit(ThreadCommand('get_value', )))
self.ini_actuator_pb.clicked.connect(self.send_init)
self.actuators_combo.currentTextChanged.connect(
lambda act: self.command_sig.emit(ThreadCommand('actuator_changed', act)))
[docs] def do_init(self, do_init=True):
"""Programmatically press the Init button
API entry
Parameters
----------
do_init: bool
will fire the Init button depending on the argument value and the button check state
"""
if do_init is not self.ini_actuator_pb.isChecked():
self.ini_actuator_pb.click()
[docs] def send_init(self, checked):
self.actuators_combo.setEnabled(not checked)
self.command_sig.emit(ThreadCommand('init', [self.ini_actuator_pb.isChecked(),
self.actuators_combo.currentText()]))
def emit_move_abs(self, spinbox):
self.command_sig.emit(ThreadCommand('move_abs', DataActuator(data=spinbox.value())))
def emit_move_rel(self, sign):
self.command_sig.emit(ThreadCommand('move_rel',
DataActuator(data=self.rel_value_sb.value() * (1 if sign == '+'
else -1))))
def close(self):
self.graph_ui.close()
self.parent.close()
def main(init_qt=True):
from pymodaq.utils.gui_utils.dock import DockArea
if init_qt: # used for the test suite
app = QtWidgets.QApplication(sys.argv)
actuators = [f'act{ind}' for ind in range(5)]
widget = QtWidgets.QWidget()
prog = DAQ_Move_UI(widget, title="test")
widget.show()
def print_command_sig(cmd_sig):
print(cmd_sig)
if cmd_sig.command == 'init':
prog.enable_move_buttons(True)
prog.command_sig.connect(print_command_sig)
prog.actuators = actuators
if init_qt:
sys.exit(app.exec_())
return prog, widget
if __name__ == '__main__':
main()