# -*- coding: utf-8 -*-
"""
Created the 05/09/2022
@author: Sebastien Weber
"""
from typing import List
import sys
from qtpy import QtWidgets
from qtpy.QtCore import Signal
from qtpy.QtWidgets import QVBoxLayout, QWidget, QComboBox
from pymodaq.utils.daq_utils import ThreadCommand
from pymodaq.control_modules.utils import ControlModuleUI
from pymodaq.utils.gui_utils.widgets import PushButtonIcon, LabelWithFont, QLED
from pymodaq.utils.gui_utils import Dock
from pymodaq.utils.config import Config
from pymodaq.control_modules.utils import DET_TYPES, DAQTypesEnum
from pymodaq.utils.plotting.data_viewers.viewer import ViewerFactory, ViewerDispatcher
from pymodaq.utils.plotting.data_viewers import ViewersEnum
from pymodaq.utils.enums import enum_checker
viewer_factory = ViewerFactory()
config = Config()
[docs]class DAQ_Viewer_UI(ControlModuleUI, ViewerDispatcher):
"""DAQ_Viewer 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
* grab
* snap
* stop
* show_log
* detector_changed
* daq_type_changed
* save_current
* save_new
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
"""
command_sig = Signal(ThreadCommand)
def __init__(self, parent, title="DAQ_Viewer", daq_type='DAQ2D', dock_settings=None, dock_viewer=None):
ControlModuleUI.__init__(self, parent)
ViewerDispatcher.__init__(self, self.dockarea, title=title, next_to_dock=dock_settings)
self.title = title
self._detector_widget = None
self._settings_widget = None
self._info_detector = None
self._daq_types_combo = None
self._detectors_combo = None
self._ini_det_pb = None
self._ini_state_led = None
self._do_bkg_cb = None
self._take_bkg_pb = None
self._settings_dock = dock_settings
self.setup_docks()
daq_type = enum_checker(DAQTypesEnum, daq_type)
self.daq_types = daq_type.names() # init the combobox through the daq_types attribute
self.daq_type = daq_type
self.detectors = [det['name'] for det in DET_TYPES[self.daq_type.name]]
self.setup_actions() # see ActionManager MixIn class
self.add_viewer(self.daq_type.to_viewer_type(), dock_viewer=dock_viewer)
self.connect_things()
self._enable_grab_buttons(False)
self._detector_widget.setVisible(False)
self._settings_widget.setVisible(False)
@property
def detector(self):
return self._detectors_combo.currentText()
@detector.setter
def detector(self, det_name: str):
self._detectors_combo.setCurrentText(det_name)
@property
def detectors(self):
return [self._detectors_combo.itemText(ind) for ind in range(self._detectors_combo.count())]
@detectors.setter
def detectors(self, detectors: List[str]):
#self._detectors_combo.currentTextChanged.disconnect()
self._detectors_combo.clear()
self._detectors_combo.addItems(detectors)
#self._detectors_combo.currentTextChanged.connect(
# lambda mod: self.command_sig.emit(ThreadCommand('detector_changed', mod)))
#self.detector = detectors[0]
@property
def daq_type(self):
return DAQTypesEnum[self._daq_types_combo.currentText()]
@daq_type.setter
def daq_type(self, dtype: DAQTypesEnum):
dtype = enum_checker(DAQTypesEnum, dtype)
self._daq_types_combo.setCurrentText(dtype.name)
@property
def daq_types(self):
return self.daq_type.names()
@daq_types.setter
def daq_types(self, dtypes: List[str]):
self._daq_types_combo.clear()
self._daq_types_combo.addItems(dtypes)
self.daq_type = DAQTypesEnum[dtypes[0]]
def close(self):
for dock in self.viewer_docks:
dock.close()
self._settings_dock.close()
[docs] def setup_docks(self):
if self._settings_dock is None:
self._settings_dock = Dock(self.title + "_Settings", size=(150, 250))
self.dockarea.addDock(self._settings_dock)
widget = QWidget()
widget.setLayout(QVBoxLayout())
#widget.layout().setSizeConstraint(QHBoxLayout.SetFixedSize)
widget.layout().setContentsMargins(2, 2, 2, 2)
self._settings_dock.addWidget(widget)
info_ui = QWidget()
self._detector_widget = QWidget()
self._settings_widget = QWidget()
self._settings_widget.setLayout(QtWidgets.QVBoxLayout())
bkg_widget = QWidget()
bkg_widget.setLayout(QtWidgets.QHBoxLayout())
widget.layout().addWidget(info_ui)
widget.layout().addWidget(self.toolbar)
widget.layout().addWidget(self._detector_widget)
widget.layout().addWidget(self._settings_widget)
widget.layout().addStretch(0)
info_ui.setLayout(QtWidgets.QHBoxLayout())
info_ui.layout().addWidget(LabelWithFont(self.title, font_name="Tahoma", font_size=14, isbold=True,
isitalic=True))
self._info_detector = LabelWithFont('', font_name="Tahoma", font_size=8, isbold=True, isitalic=True)
info_ui.layout().addWidget(self._info_detector)
self._detector_widget.setLayout(QtWidgets.QGridLayout())
self._daq_types_combo = QComboBox()
self._daq_types_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self._detectors_combo = QComboBox()
self._detectors_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self._ini_det_pb = PushButtonIcon('ini', 'Init. Detector', True, 'Initialize selected detector')
self._ini_state_led = QLED(readonly=True)
self._do_bkg_cb = QtWidgets.QCheckBox('Do Bkg')
self._take_bkg_pb = QtWidgets.QPushButton('Take Bkg')
self._take_bkg_pb.setChecked(False)
self._detector_widget.layout().addWidget(LabelWithFont('DAQ type:'), 0, 0)
self._detector_widget.layout().addWidget(self._daq_types_combo, 0, 1)
self._detector_widget.layout().addWidget(LabelWithFont('Detector:'), 1, 0)
self._detector_widget.layout().addWidget(self._detectors_combo, 1, 1)
self._detector_widget.layout().addWidget(self._ini_det_pb, 0, 2)
self._detector_widget.layout().addWidget(self._ini_state_led, 1, 2)
self._detector_widget.layout().addWidget(bkg_widget, 2, 0, 1, 3)
bkg_widget.layout().addWidget(self._do_bkg_cb)
bkg_widget.layout().addWidget(self._take_bkg_pb)
self.statusbar = QtWidgets.QStatusBar()
self.statusbar.setMaximumHeight(30)
widget.layout().addWidget(self.statusbar)
def add_setting_tree(self, tree):
self._settings_widget.layout().addWidget(tree)
[docs] def setup_actions(self):
self.add_action('grab', 'Grab', 'run2', "Grab data from the detector", checkable=True)
self.add_action('snap', 'Snap', 'snap', "Take a snapshot from the detector")
self.add_action('stop', 'Stop', 'stop', "Stop grabing")
self.add_action('save_current', 'Save Current Data', 'SaveAs', "Save Current Data")
self.add_action('save_new', 'Save New Data', 'Snap&Save', "Save New Data")
self.add_action('open', 'Load Data', 'Open', "Load Saved Data")
self.add_action('show_controls', 'Show Controls', 'Settings', "Show Controls to set DAQ and Detector type",
checkable=True)
self.add_action('show_settings', 'Show Settings', 'tree', "Show Settings", checkable=True)
self.add_action('quit', 'Quit the module', 'close2')
self.add_action('show_config', 'Show Config', 'Settings', "Show PyMoDAQ Config", checkable=False,
toolbar=self.toolbar)
self.add_action('log', 'Show Log file', 'information2')
self._data_ready_led = QLED(readonly=True)
self.toolbar.addWidget(self._data_ready_led)
[docs] def connect_things(self):
self.connect_action('show_controls', lambda show: self._detector_widget.setVisible(show))
self.connect_action('show_settings', lambda show: self._settings_widget.setVisible(show))
self.connect_action('quit', lambda: self.command_sig.emit(ThreadCommand('quit', )))
self.connect_action('show_config', lambda: self.command_sig.emit(ThreadCommand('show_config', )))
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('stop', lambda: self.get_action('grab').setChecked(False))
self.connect_action('stop', lambda: self._enable_ini_buttons(True))
self.connect_action('stop', lambda: self._settings_widget.setEnabled(True))
self.connect_action('grab', self._grab)
self.connect_action('snap', lambda: self.command_sig.emit(ThreadCommand('snap', )))
self.connect_action('save_current', lambda: self.command_sig.emit(ThreadCommand('save_current', )))
self.connect_action('save_new', lambda: self.command_sig.emit(ThreadCommand('save_new', )))
self.connect_action('open', lambda: self.command_sig.emit(ThreadCommand('open', )))
self._ini_det_pb.clicked.connect(self.send_init)
self._detectors_combo.currentTextChanged.connect(
lambda mod: self.command_sig.emit(ThreadCommand('detector_changed', mod)))
self._daq_types_combo.currentTextChanged.connect(self._daq_type_changed)
self._do_bkg_cb.clicked.connect(lambda checked: self.command_sig.emit(ThreadCommand('do_bkg', checked)))
self._take_bkg_pb.clicked.connect(lambda: self.command_sig.emit(ThreadCommand('take_bkg')))
[docs] def update_viewers(self, viewers_type: List[ViewersEnum]):
super().update_viewers(viewers_type)
self.command_sig.emit(ThreadCommand('viewers_changed', attribute=dict(viewer_types=self.viewer_types,
viewers=self.viewers)))
@property
def data_ready(self):
return self._data_ready_led.get_state()
@data_ready.setter
def data_ready(self, status):
self._data_ready_led.set_as(status)
def _daq_type_changed(self, daq_type: DAQTypesEnum):
try:
daq_type = enum_checker(DAQTypesEnum, daq_type)
self.command_sig.emit(ThreadCommand('daq_type_changed', daq_type))
if self.viewer_types != [daq_type.to_viewer_type()]:
self.update_viewers([daq_type.to_viewer_type()])
except ValueError as e:
pass
def show_settings(self, show=True):
if (self.is_action_checked('show_settings') and not show) or \
(not self.is_action_checked('show_settings') and show):
self.get_action('show_settings').trigger()
def show_controls(self, show=True):
if (self.is_action_checked('show_controls') and not show) or \
(not self.is_action_checked('show_controls') and show):
self.get_action('show_controls').trigger()
def _grab(self):
"""Slot from the *grab* action"""
self.command_sig.emit(ThreadCommand('grab', attribute=self.is_action_checked('grab')))
self._enable_ini_buttons(not self.is_action_checked('grab'))
if not self.config('viewer', 'allow_settings_edition'):
self._settings_widget.setEnabled(not self.is_action_checked('grab'))
[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_det_pb.isChecked():
self._ini_det_pb.click()
[docs] def do_grab(self, do_grab=True):
"""Programmatically press the Grab button
API entry
Parameters
----------
do_grab: bool
will fire the Init button depending on the argument value and the button check state
"""
if (do_grab and not self.is_action_checked('grab')) or ((not do_grab) and self.is_action_checked('grab')):
self.get_action('grab').trigger()
[docs] def do_snap(self):
"""Programmatically press the Snap button
API entry
"""
self.get_action('snap').trigger()
[docs] def do_stop(self):
"""Programmatically press the Stop button
API entry
"""
self.get_action('stop').trigger()
if self.is_action_checked('grab'):
self.get_action('grab').trigger()
[docs] def send_init(self, checked: bool):
self._enable_detchoices(not checked)
self.command_sig.emit(ThreadCommand('init', [checked,
self._daq_types_combo.currentText(),
self._detectors_combo.currentText()]))
def _enable_detchoices(self, enable=True):
self._detectors_combo.setEnabled(enable)
self._daq_types_combo.setEnabled(enable)
@property
def detector_init(self):
"""bool: the status of the init LED."""
return self._ini_state_led.get_state()
@detector_init.setter
def detector_init(self, status):
if status:
self._info_detector.setText(f'{self.daq_type.name} : {self.detector}')
else:
self._info_detector.setText('')
self._ini_state_led.set_as(status)
self._enable_grab_buttons(status)
def _enable_grab_buttons(self, status):
self.get_action('grab').setEnabled(status)
self.get_action('snap').setEnabled(status)
self.get_action('stop').setEnabled(status)
self.get_action('save_current').setEnabled(status)
self.get_action('save_new').setEnabled(status)
def _enable_ini_buttons(self, status):
self._ini_det_pb.setEnabled(status)
self.get_action('quit').setEnabled(status)
def main(init_qt=True):
from pymodaq.utils.gui_utils.dock import DockArea
from pymodaq.utils.parameter import ParameterTree, Parameter
from pymodaq.control_modules.viewer_utility_classes import params as daq_viewer_params
if init_qt: # used for the test suite
app = QtWidgets.QApplication(sys.argv)
param = Parameter.create(name='settings', type='group', children=daq_viewer_params)
tree = ParameterTree()
tree.setParameters(param, showTop=False)
dockarea = DockArea()
prog = DAQ_Viewer_UI(dockarea)
dockarea.show()
def print_command_sig(cmd_sig):
print(cmd_sig)
prog.display_status(str(cmd_sig))
if cmd_sig.command == 'init':
prog._enable_grab_buttons(cmd_sig.attribute[0])
prog.detector_init = cmd_sig.attribute[0]
# prog.detectors = detectors
prog.command_sig.connect(print_command_sig)
prog.add_setting_tree(tree)
prog.update_viewers([ViewersEnum['Viewer0D'], ViewersEnum['Viewer1D'], ViewersEnum['Viewer2D']])
if init_qt:
sys.exit(app.exec_())
return prog, dockarea
if __name__ == '__main__':
main()