From 72f31653849b19c554fadd8cb86fd0d0d6e9779f Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Wed, 3 Mar 2021 10:14:54 +0100 Subject: [PATCH 1/2] first version of generic widget for some of the interfaces --- pyobs_gui/genericwidgets.py | 133 ++++++++++++++++++++++++++++++++++++ pyobs_gui/mainwindow.py | 6 +- 2 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 pyobs_gui/genericwidgets.py diff --git a/pyobs_gui/genericwidgets.py b/pyobs_gui/genericwidgets.py new file mode 100644 index 0000000..d37e35b --- /dev/null +++ b/pyobs_gui/genericwidgets.py @@ -0,0 +1,133 @@ +import logging + +from PyQt5.QtCore import Qt +from PyQt5 import QtWidgets + +from pyobs_gui.basewidget import BaseWidget + +from pyobs.interfaces import IStoppable, IAltAz, IRaDec, ILatLon + +log = logging.getLogger(__name__) + + +class WidgetStoppable(QtWidgets.QWidget): + """Simple widget for any Module implementing the IStoppable interface.""" + def __init__(self, module, comm, parent=None): + super().__init__(parent=parent) + self.module = module + + # build GUI + layout = QtWidgets.QVBoxLayout(self) + self.running = QtWidgets.QCheckBox("Is running") + self.running.toggled.connect(self.on_toggle) + layout.addWidget(self.running) + + def _update(self): + self.running.setChecked(self.module.is_running().wait()) + + def on_toggle(self, running): + if running: + self.module.start() + else: + self.module.stop() + + +class WidgetCoordinates(QtWidgets.QWidget): + """ + Simple widget for any Module implementing any of the coordinate + interfaces like IAltAz. + + Support IAltAz, IRaDec and ILatLon + """ + def __init__(self, module, comm, parent=None): + super().__init__(parent=parent) + self.module = module + self.comm = comm + + coordtypes = [(IAltAz, "move_altaz", "get_altaz", + "Horizontal (Alt, Az) in deg"), + (IRaDec, "move_radec", "get_readec", + "Equatorial (Ra, Dec) in deg"), + (ILatLon, "move_latlon", "get_latlon", + "Geographic (Lat, Lon) in deg")] + + # create widgets + self.edittargetcoord = QtWidgets.QLineEdit("") + self.cmbcoordtype = QtWidgets.QComboBox() + self.cmbcoordtype.setEditable(False) + self.btnmovetotarget = QtWidgets.QPushButton("Move to target") + self.btnmovetotarget.clicked.connect(self.on_move_to_target) + + frmcurrentcoord = QtWidgets.QGroupBox("Current position") + lytcurrentcoord = QtWidgets.QFormLayout(frmcurrentcoord) + + self.interfaces = {} + for Interface, setter, getter, name in coordtypes: + if isinstance(self.module, Interface): + setterfn = getattr(self.module, setter) + getterfn = getattr(self.module, getter) + lblcurrentcoord = QtWidgets.QLabel("") + lytcurrentcoord.addRow(name, lblcurrentcoord) + self.interfaces[name] = setterfn, getterfn, lblcurrentcoord + self.cmbcoordtype.addItem(name) + + # build GUI + layout = QtWidgets.QFormLayout(self) + layout.addRow(frmcurrentcoord) + layout.addRow(self.cmbcoordtype, self.edittargetcoord) + layout.addRow(self.btnmovetotarget) + + def _update(self): + for name, (_, getter, lbl) in self.interfaces.items(): + lbl.setText("{:.3f}, {:.3f}".format(*getter().wait())) + + def on_move_to_target(self): + coordstr = self.edittargetcoord.text() + coords = [float(x) for x in coordstr.split(",")] + coordtype = self.cmbcoordtype.currentText() + setter = self.interfaces[coordtype][0] + setter(coords[0], coords[1]).wait() + + +class GenericWidget(BaseWidget): + """ + A container widget of all subwidgets for the Interfaces a module + implements. + """ + + @staticmethod + def create(module, comm): + """ + Convenience method to build a Generic widget containing all subwidgets + for the Interfaces the module implements. + + TODO: why can't this be done in the constructor? + """ + genericwidgets = [(IStoppable, WidgetStoppable), + ((IAltAz, IRaDec, ILatLon), WidgetCoordinates)] + + widgets = [widget(module, comm) + for Interface, widget in genericwidgets + if isinstance(module, Interface)] + if not widgets: + return None + + return GenericWidget(module, comm, widgets) + + def __init__(self, module, comm, widgets, parent=None): + BaseWidget.__init__(self, parent=parent, update_func=self._update) + self.module = module + self.comm = comm + self.widgets = widgets + + layout = QtWidgets.QVBoxLayout(self) + layout.setAlignment(Qt.AlignTop) + + for w in self.widgets: + layout.addWidget(w) + + layout.addWidget + + def _update(self): + for w in self.widgets: + w._update() diff --git a/pyobs_gui/mainwindow.py b/pyobs_gui/mainwindow.py index e21664b..caf415e 100644 --- a/pyobs_gui/mainwindow.py +++ b/pyobs_gui/mainwindow.py @@ -18,6 +18,7 @@ from pyobs_gui.widgetfocus import WidgetFocus from pyobs_gui.widgetscript import WidgetScript from pyobs_gui.widgetweather import WidgetWeather +from pyobs_gui.genericwidgets import GenericWidget class PagesListWidgetItem(QtWidgets.QListWidgetItem): @@ -258,7 +259,10 @@ def _client_connected(self, client: str): widget = WidgetScript(proxy, self.comm) icon = QtGui.QIcon(":/resources/Crystal_Clear_app_demo.png") else: - return + widget = GenericWidget.create(proxy, self.comm) + if widget is None: + return + icon = QtGui.QIcon(":/resources/Crystal_Clear_app_demo.png") # get label label = proxy.label().wait() From f5f1117b39cef4162002171f986997bec241025c Mon Sep 17 00:00:00 2001 From: Karl Wessel Date: Mon, 22 Mar 2021 10:02:12 +0100 Subject: [PATCH 2/2] replace checkbox with start and stop button starting/stopping a runable module chould be more heavy than just checking a checkbox --- pyobs_gui/genericwidgets.py | 44 +++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/pyobs_gui/genericwidgets.py b/pyobs_gui/genericwidgets.py index d37e35b..58eef3b 100644 --- a/pyobs_gui/genericwidgets.py +++ b/pyobs_gui/genericwidgets.py @@ -2,6 +2,7 @@ from PyQt5.QtCore import Qt from PyQt5 import QtWidgets +from PyQt5.QtCore import pyqtSignal from pyobs_gui.basewidget import BaseWidget @@ -10,20 +11,55 @@ log = logging.getLogger(__name__) +class QtToggleButtons(QtWidgets.QWidget): + toggled = pyqtSignal(bool) + + def __init__(self): + super().__init__() + + start = QtWidgets.QPushButton("Start") + start.setCheckable(True) + start.setAutoExclusive(True) + start.setChecked(False) + start.toggled.connect(self.toggled) + self.start = start + + stop = QtWidgets.QPushButton("Stop") + stop.setCheckable(True) + stop.setAutoExclusive(True) + stop.setChecked(True) + + lyt = QtWidgets.QHBoxLayout(self) + lyt.addWidget(start) + lyt.addWidget(stop) + + def setChecked(self, ischecked): + self.start.setChecked(ischecked) + + class WidgetStoppable(QtWidgets.QWidget): """Simple widget for any Module implementing the IStoppable interface.""" def __init__(self, module, comm, parent=None): super().__init__(parent=parent) self.module = module - # build GUI - layout = QtWidgets.QVBoxLayout(self) - self.running = QtWidgets.QCheckBox("Is running") + self.running = QtToggleButtons() self.running.toggled.connect(self.on_toggle) + + self.state = QtWidgets.QLabel("State: Unknown") + + # layout GUI + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.state) layout.addWidget(self.running) def _update(self): - self.running.setChecked(self.module.is_running().wait()) + isrunning = self.module.is_running().wait() + self.running.setChecked(isrunning) + if isrunning: + self.state.setText("State: Running") + else: + self.state.setText("State: Stopped") def on_toggle(self, running): if running: