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

Switch interface rework #623

Merged
merged 64 commits into from
Dec 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3e5afd5
rework of the switch interface. currently only one-directional from g…
kay-jahnke Oct 9, 2020
10f8a4f
signals in logic and gui tweaking
kay-jahnke Oct 15, 2020
9e35422
convenience functions and better error handling
kay-jahnke Oct 15, 2020
4ed2d83
improving gui and clean up
kay-jahnke Oct 15, 2020
e6fc300
adding magic command for easier script control
kay-jahnke Oct 16, 2020
3cf14f1
adding NI Card for digital switching
kay-jahnke Oct 16, 2020
acda3ba
fixes to hardware files
kay-jahnke Oct 16, 2020
997b537
more bug fixes
kay-jahnke Oct 16, 2020
3624570
converting flipmirror.py
kay-jahnke Oct 16, 2020
a84cb87
renaming and documentation
kay-jahnke Oct 16, 2020
2028925
Merge remote-tracking branch 'origin/master' into switch_interface_re…
kay-jahnke Oct 16, 2020
b006dd9
converting OK-switch
kay-jahnke Oct 16, 2020
9679958
converted hbridge and considerable bug fixing
kay-jahnke Oct 16, 2020
1315dc1
bug fixes
kay-jahnke Oct 19, 2020
068f4fa
bug fix for advanced gui (the labels were the wrong way around)
kay-jahnke Oct 19, 2020
002752c
converted osw12.py and again a few bug fixes: all hardware files done…
kay-jahnke Oct 20, 2020
de4b269
reworking the switch logic to use interfuses instead of arbitrary con…
kay-jahnke Oct 21, 2020
1182aa0
adding option to interfuse
kay-jahnke Oct 21, 2020
0a98dab
implementing the states setter in the logic
kay-jahnke Oct 21, 2020
91d3eb0
more convenient way of defining names_of_switches in case of only one…
kay-jahnke Oct 21, 2020
608d233
adding documentation to the hardware files
kay-jahnke Oct 21, 2020
ac00ce1
finishing the documentation and improving the elegance of the logic
kay-jahnke Oct 22, 2020
59fa4a8
adding a watchdog to the switch_logic.py in order to catch switch cha…
kay-jahnke Oct 22, 2020
646e344
osw12 small rework
alrik-durand Oct 22, 2020
59a8d6e
changing to state_dicts and more than two states
kay-jahnke Oct 22, 2020
74b46e6
fully getting rid of all index related voodoo
kay-jahnke Oct 22, 2020
47afe19
no string conversion anymore
kay-jahnke Oct 22, 2020
6ab94af
Merge remote-tracking branch 'alrik/switch_interface_rework' into swi…
kay-jahnke Oct 22, 2020
fc7514c
WIP
kay-jahnke Oct 22, 2020
14068a4
new interface implemented for most of the hardware and update of docu…
kay-jahnke Oct 23, 2020
0723b02
convertign the rest of the hardware files
kay-jahnke Oct 26, 2020
624a8a7
added interfuse for buffering switch hardware
kay-jahnke Oct 26, 2020
e058474
reduced complexity of ConfigOptions
kay-jahnke Oct 28, 2020
5fb5f71
the default remember_states should be False in most cases
kay-jahnke Oct 29, 2020
5482d5d
bug fixes to the combiner interface
kay-jahnke Oct 29, 2020
b2c7507
better default configs and abort in on_activation in the the case of …
kay-jahnke Nov 9, 2020
ccc2a71
bug fix to non-initialized state variables
kay-jahnke Nov 9, 2020
76a2ae7
another bug fix to non-initialized state variables
kay-jahnke Nov 9, 2020
629f0d0
replacing ":" in name of NI Task
kay-jahnke Nov 10, 2020
4f415d6
bug fix, nidaq wants arrays and not binaries
kay-jahnke Nov 11, 2020
067dae1
explicit bool cast
kay-jahnke Nov 11, 2020
b88b20b
adding config changes to documentation
kay-jahnke Nov 12, 2020
96afe10
bug fix names_of_states
kay-jahnke Nov 16, 2020
cbf4ca0
another bug fix to the osw12
kay-jahnke Nov 17, 2020
e04fa07
Not so small revision of the new switch module toolchain. Related int…
Neverhorst Nov 18, 2020
9bdb656
Removed switch buffer interfuse. If the need arises for such a featur…
Neverhorst Nov 18, 2020
4621920
Basic watchdog control introduced in GUI menu.
Neverhorst Nov 18, 2020
0ad9498
Adjusted config examples for switch modules
Neverhorst Nov 18, 2020
830ef5b
Reworked the SwitchGui to be more "Qt-like".
Neverhorst Nov 18, 2020
836bbdf
doc update
Neverhorst Nov 18, 2020
952985b
Better GUI formatting
Neverhorst Nov 18, 2020
cf6847b
bug fixing in some of the hardware files and some cosmetic adjustment…
kay-jahnke Nov 20, 2020
6e15c90
- Highly configurable SwitchGui appearance
Neverhorst Nov 20, 2020
43830a1
Merge remote-tracking branch 'origin/switch_interface_rework' into sw…
Neverhorst Nov 20, 2020
013e625
Disabled non-functional CheckBox option for switches
Neverhorst Nov 20, 2020
d4b407d
Got rid of QCheckBox preparation/template for switches. It is in ever…
Neverhorst Nov 20, 2020
1ad5712
Merge branch 'master' into switch_interface_rework
Neverhorst Nov 20, 2020
11be066
Workaround for IntEnum representation in SwitchGui.
Neverhorst Nov 23, 2020
f2bc6ed
Fixed a bug when changing remembered-state switch names in config in …
Neverhorst Nov 23, 2020
710b979
Removed red-green colorscheme and introduced a highlight colorscheme …
Neverhorst Nov 24, 2020
0c842fd
Circumvented an ugly bug of Qt with Palette and StyleSheets mess...
Neverhorst Nov 24, 2020
afcfc38
Fixed a bug with switch ordering in StatusVars
Neverhorst Nov 24, 2020
d3b62a2
Better size policy for ToggleSwitch
Neverhorst Nov 24, 2020
98d67c1
changed add to query (add is legacy) and added a delay for the switch…
Yawghmoth Dec 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion config/example/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,21 @@ hardware:

mydummyswitch1:
module.Class: 'switches.switch_dummy.SwitchDummy'
name: 'First' # optional
remember_states: True # optional
switches:
one: ['down', 'up']
two: ['down', 'up']
three: ['low', 'middle', 'high']

mydummyswitch2:
module.Class: 'switches.switch_dummy.SwitchDummy'
name: 'Second' # optional
remember_states: True # optional
switches:
'An even longer name of the switch itself':
- 'Very long name of a random state'
- 'Another very long name of a random state'

myspectrometer:
module.Class: 'spectrometer.spectrometer_dummy.SpectrometerInterfaceDummy'
Expand Down Expand Up @@ -181,9 +193,17 @@ logic:

switchlogic:
module.Class: 'switch_logic.SwitchLogic'
watchdog_interval: 1
autostart_watchdog: True
connect:
switch: 'switchinterfuse'

switchinterfuse:
module.Class: 'interfuse.switch_combiner_interfuse.SwitchCombinerInterfuse'
connect:
switch1: 'mydummyswitch1'
switch2: 'mydummyswitch2'
extend_hardware_name: True

scannerlogic:
module.Class: 'confocal_logic.ConfocalLogic'
Expand Down Expand Up @@ -397,7 +417,7 @@ gui:
savelogic: 'savelogic'

switches:
module.Class: 'switcher.switchgui.SwitchGui'
module.Class: 'switch.switch_gui.SwitchGui'
connect:
switchlogic: 'switchlogic'

Expand Down
4 changes: 4 additions & 0 deletions documentation/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ please use _ni_x_series_in_streamer.py_ as hardware module.
* Added a hardware file to interface Thorlabs filter wheels via scripts
* Bug fixes to core: made error messages sticky, respecting dependencies when restarting.
* Added a config option to regulate pid logic timestep length
* New SwitchInterface and updated logic plus GUI
* Added biexponential fit function, model and estimator


Expand All @@ -98,6 +99,9 @@ or a list of strings for multiple paths.
* There is an option for the fit logic, to give an additional path: `additional_fit_methods_path`
* The connectors and file names of the GUI and logic modules of the QDPlotter have been changed.
* QDPlotter now needs a new connection to the fit logic.
* The tool chain for the switch logic has changed.
To combine multiple switches one needs to use the `switch_combiner_interfuse`
instead of multiple connectors in the logic.

## Release 0.10
Released on 14 Mar 2019
Expand Down
292 changes: 292 additions & 0 deletions gui/switch/switch_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
# -*- coding: utf-8 -*-
"""
This file contains the qudi switch GUI module.

Qudi is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Qudi is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Qudi. If not, see <http://www.gnu.org/licenses/>.

Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the
top-level directory of this distribution and at <https://github.com/Ulm-IQO/qudi/>
"""

from enum import IntEnum
from core.connector import Connector
from core.statusvariable import StatusVar
from gui.guibase import GUIBase
from qtpy import QtWidgets, QtCore, QtGui
from .switch_state_widgets import SwitchRadioButtonWidget, ToggleSwitchWidget


class SwitchStyle(IntEnum):
TOGGLE_SWITCH = 0
RADIO_BUTTON = 1


class StateColorScheme(IntEnum):
DEFAULT = 0
HIGHLIGHT = 1


class SwitchMainWindow(QtWidgets.QMainWindow):
""" Main Window for the SwitchGui module """

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setWindowTitle('qudi: <INSERT HARDWARE NAME>')
# Create main layout and central widget
self.main_layout = QtWidgets.QGridLayout()
self.main_layout.setColumnStretch(1, 1)
self.main_layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.main_layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
widget = QtWidgets.QWidget()
widget.setLayout(self.main_layout)
widget.setFixedSize(1, 1)
self.setCentralWidget(widget)

# Create QActions and menu bar
menu_bar = QtWidgets.QMenuBar()
self.setMenuBar(menu_bar)

menu = menu_bar.addMenu('Menu')
self.action_close = QtWidgets.QAction('Close Window')
self.action_close.setCheckable(False)
self.action_close.setIcon(QtGui.QIcon('artwork/icons/oxygen/22x22/application-exit.png'))
self.addAction(self.action_close)
menu.addAction(self.action_close)

menu = menu_bar.addMenu('View')
self.action_periodic_state_check = QtWidgets.QAction('Periodic State Checking')
self.action_periodic_state_check.setCheckable(True)
menu.addAction(self.action_periodic_state_check)
separator = menu.addSeparator()
separator.setText('Switch Appearance')
self.switch_view_actions = [QtWidgets.QAction('use toggle switches'),
QtWidgets.QAction('use radio buttons')]
self.switch_view_action_group = QtWidgets.QActionGroup(self)
for action in self.switch_view_actions:
action.setCheckable(True)
self.switch_view_action_group.addAction(action)
menu.addAction(action)
self.action_view_highlight_state = QtWidgets.QAction('highlight state labels')
self.action_view_highlight_state.setCheckable(True)
menu.addAction(self.action_view_highlight_state)
self.action_view_alt_toggle_style = QtWidgets.QAction('alternative toggle switch')
self.action_view_alt_toggle_style.setCheckable(True)
menu.addAction(self.action_view_alt_toggle_style)

# close window upon triggering close action
self.action_close.triggered.connect(self.close)
return


class SwitchGui(GUIBase):
""" A graphical interface to switch a hardware by hand.
"""

# declare connectors
switchlogic = Connector(interface='SwitchLogic')

# declare status variables
_switch_style = StatusVar(name='switch_style',
default=SwitchStyle.TOGGLE_SWITCH,
representer=lambda _, x: int(x),
constructor=lambda _, x: SwitchStyle(x))
_state_colorscheme = StatusVar(name='state_colorscheme',
default=StateColorScheme.DEFAULT,
representer=lambda _, x: int(x),
constructor=lambda _, x: StateColorScheme(x))
_alt_toggle_switch_style = StatusVar(name='alt_toggle_switch_style', default=False)

# declare signals
sigSwitchChanged = QtCore.Signal(str, str)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._mw = None
self._widgets = dict()

def on_activate(self):
""" Create all UI objects and show the window.
"""
self._mw = SwitchMainWindow()
self.restoreWindowPos(self._mw)
try:
self._mw.switch_view_actions[self._switch_style].setChecked(True)
except IndexError:
self._mw.switch_view_actions[0].setChecked(True)
self._switch_style = SwitchStyle(0)
self._mw.action_view_highlight_state.setChecked(
self._state_colorscheme == StateColorScheme.HIGHLIGHT
)
self._mw.action_view_alt_toggle_style.setChecked(self._alt_toggle_switch_style)
self._mw.setWindowTitle(f'qudi: {self.switchlogic().device_name.title()}')

self._populate_switches()

self.sigSwitchChanged.connect(self.switchlogic().set_state, QtCore.Qt.QueuedConnection)
self._mw.action_periodic_state_check.toggled.connect(
self.switchlogic().toggle_watchdog, QtCore.Qt.QueuedConnection
)
self._mw.switch_view_action_group.triggered.connect(self._update_switch_appearance)
self._mw.action_view_highlight_state.triggered.connect(self._update_state_colorscheme)
self._mw.action_view_alt_toggle_style.triggered.connect(self._update_toggle_switch_style)
self.switchlogic().sigWatchdogToggled.connect(
self._watchdog_updated, QtCore.Qt.QueuedConnection
)
self.switchlogic().sigSwitchesChanged.connect(
self._switches_updated, QtCore.Qt.QueuedConnection
)

self._watchdog_updated(self.switchlogic().watchdog_active)
self._switches_updated(self.switchlogic().states)
self._update_state_colorscheme()
self.show()

def on_deactivate(self):
""" Hide window empty the GUI and disconnect signals
"""
self.switchlogic().sigSwitchesChanged.disconnect(self._switches_updated)
self.switchlogic().sigWatchdogToggled.disconnect(self._watchdog_updated)
self._mw.action_view_highlight_state.triggered.disconnect()
self._mw.action_view_alt_toggle_style.triggered.disconnect()
self._mw.switch_view_action_group.triggered.disconnect()
self._mw.action_periodic_state_check.toggled.disconnect()
self.sigSwitchChanged.disconnect()

self.saveWindowPos(self._mw)
self._delete_switches()
self._mw.close()

def show(self):
""" Make sure that the window is visible and at the top.
"""
self._mw.show()

def _populate_switches(self):
""" Dynamically build the gui
"""
self._widgets = dict()
for ii, (switch, states) in enumerate(self.switchlogic().available_states.items()):
label = self._get_switch_label(switch)
if len(states) > 2 or self._switch_style == SwitchStyle.RADIO_BUTTON:
switch_widget = SwitchRadioButtonWidget(switch_states=states)
self._widgets[switch] = (label, switch_widget)
self._mw.main_layout.addWidget(self._widgets[switch][0], ii, 0)
self._mw.main_layout.addWidget(self._widgets[switch][1], ii, 1)
switch_widget.sigStateChanged.connect(self.__get_state_update_func(switch))
elif self._switch_style == SwitchStyle.TOGGLE_SWITCH:
if self._alt_toggle_switch_style:
switch_widget = ToggleSwitchWidget(switch_states=states, thumb_track_ratio=1.35)
else:
switch_widget = ToggleSwitchWidget(switch_states=states, thumb_track_ratio=0.9)
self._widgets[switch] = (label, switch_widget)
switch_widget.setSizePolicy(QtWidgets.QSizePolicy.Fixed,
QtWidgets.QSizePolicy.Fixed)
self._mw.main_layout.addWidget(self._widgets[switch][0], ii, 0)
self._mw.main_layout.addWidget(switch_widget, ii, 1)
switch_widget.sigStateChanged.connect(self.__get_state_update_func(switch))

@staticmethod
def _get_switch_label(switch):
""" Helper function to create a QLabel for a single switch.

@param str switch: The name of the switch to create the label for
@return QWidget: QLabel with switch name
"""
label = QtWidgets.QLabel(f'{switch}:')
font = QtGui.QFont()
font.setBold(True)
font.setPointSize(11)
# font.setPixelSize(int(round(0.75 * QtWidgets.QLineEdit().sizeHint().height())))
label.setFont(font)
# label.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
# QtWidgets.QSizePolicy.MinimumExpanding)
label.setMinimumWidth(label.sizeHint().width())
label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
return label

def _delete_switches(self):
""" Delete all the buttons from the main layout. """
for switch in reversed(tuple(self._widgets)):
label, widget = self._widgets[switch]
widget.sigStateChanged.disconnect()
self._mw.main_layout.removeWidget(label)
self._mw.main_layout.removeWidget(widget)
label.setParent(None)
widget.setParent(None)
del self._widgets[switch]
label.deleteLater()
widget.deleteLater()

@QtCore.Slot(dict)
def _switches_updated(self, states):
""" Helper function to update the GUI on a change of the states in the logic.
This function is connected to the signal coming from the switchlogic signaling a change in states.
@param dict states: The state dict of the form {"switch": "state"}
@return: None
"""
for switch, state in states.items():
self._widgets[switch][1].set_state(state)

@QtCore.Slot(bool)
def _watchdog_updated(self, enabled):
""" Update the menu action accordingly if the watchdog has been (de-)activated.

@param bool enabled: Watchdog active (True) or inactive (False)
"""
if enabled != self._mw.action_periodic_state_check.isChecked():
self._mw.action_periodic_state_check.blockSignals(True)
self._mw.action_periodic_state_check.setChecked(enabled)
self._mw.action_periodic_state_check.blockSignals(False)

def _update_switch_appearance(self, action):
index = self._mw.switch_view_actions.index(action)
if index != self._switch_style:
self._switch_style = SwitchStyle(index)
self._mw.close()
self._delete_switches()
self._mw.centralWidget().setFixedSize(1, 1)
self._populate_switches()
self._switches_updated(self.switchlogic().states)
self._update_state_colorscheme()
self._mw.show()

def _update_state_colorscheme(self):
self._state_colorscheme = StateColorScheme(self._mw.action_view_highlight_state.isChecked())
if self._state_colorscheme is StateColorScheme.HIGHLIGHT:
checked_color = self._mw.palette().highlight().color()
unchecked_color = None
else:
checked_color = None
unchecked_color = None
for widget in self._widgets.values():
widget[1].set_state_colors(unchecked_color, checked_color)
widget[1].update()

@QtCore.Slot(bool)
def _update_toggle_switch_style(self, checked):
if self._alt_toggle_switch_style != checked:
self._alt_toggle_switch_style = checked
if self._switch_style == SwitchStyle.TOGGLE_SWITCH:
self._mw.close()
self._delete_switches()
self._mw.centralWidget().setFixedSize(1, 1)
self._populate_switches()
self._switches_updated(self.switchlogic().states)
self._update_state_colorscheme()
self._mw.show()

def __get_state_update_func(self, switch):
def update_func(state):
self.sigSwitchChanged.emit(switch, state)
return update_func
Loading