GUI: show grouping of parameters

still todo: grouping of modules

Change-Id: I67e8582004f16061dda96e455f424f5a12e6a163
This commit is contained in:
Enrico Faulhaber
2017-05-24 17:04:55 +02:00
parent 462b6a0a7e
commit f984129986
6 changed files with 160 additions and 40 deletions

View File

@ -8,7 +8,7 @@ description = short description
[interface tcp] [interface tcp]
interface=tcp interface=tcp
bindto=0.0.0.0 bindto=0.0.0.0
bindport=10769 bindport=10767
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=demo
@ -39,3 +39,8 @@ timeout=900
# some (non-default) parameter properties # some (non-default) parameter properties
pollinterval.export=False pollinterval.export=False
# some parameter grouping
p.group='pid'
i.group='pid'
d.group='pid'

View File

@ -469,8 +469,11 @@ class Client(object):
def getParameters(self, module): def getParameters(self, module):
return self.describing_data['modules'][module]['parameters'].keys() return self.describing_data['modules'][module]['parameters'].keys()
def getModuleProperties(self, module):
return self.describing_data['modules'][module]['properties']
def getModuleBaseClass(self, module): def getModuleBaseClass(self, module):
return self.describing_data['modules'][module]['interfaceclass'] return self.getModuleProperties(module)['interface']
def getCommands(self, module): def getCommands(self, module):
return self.describing_data['modules'][module]['commands'].keys() return self.describing_data['modules'][module]['commands'].keys()

View File

@ -65,15 +65,15 @@ class Cryostat(CryoBase):
maxpower=PARAM("Maximum heater power", maxpower=PARAM("Maximum heater power",
validator=nonnegative, default=1, unit="W", validator=nonnegative, default=1, unit="W",
readonly=False, readonly=False,
group='heater', group='heater_settings',
), ),
heater=PARAM("current heater setting", heater=PARAM("current heater setting",
validator=floatrange(0, 100), default=0, unit="%", validator=floatrange(0, 100), default=0, unit="%",
group='heater', group='heater_settings',
), ),
heaterpower=PARAM("current heater power", heaterpower=PARAM("current heater power",
validator=nonnegative, default=0, unit="W", validator=nonnegative, default=0, unit="W",
group='heater', group='heater_settings',
), ),
target=PARAM("target temperature", target=PARAM("target temperature",
validator=nonnegative, default=0, unit="K", validator=nonnegative, default=0, unit="K",
@ -110,17 +110,17 @@ class Cryostat(CryoBase):
tolerance=PARAM("temperature range for stability checking", tolerance=PARAM("temperature range for stability checking",
validator=floatrange(0, 100), default=0.1, unit='K', validator=floatrange(0, 100), default=0.1, unit='K',
readonly=False, readonly=False,
group='window', group='stability',
), ),
window=PARAM("time window for stability checking", window=PARAM("time window for stability checking",
validator=floatrange(1, 900), default=30, unit='s', validator=floatrange(1, 900), default=30, unit='s',
readonly=False, readonly=False,
group='window', group='stability',
), ),
timeout=PARAM("max waiting time for stabilisation check", timeout=PARAM("max waiting time for stabilisation check",
validator=floatrange(1, 36000), default=900, unit='s', validator=floatrange(1, 36000), default=900, unit='s',
readonly=False, readonly=False,
group='window', group='stability',
), ),
) )
CMDS = dict( CMDS = dict(

View File

@ -33,9 +33,9 @@ from secop.client.baseclient import Client as SECNode
import sys import sys
ITEM_TYPE_NODE = QTreeWidgetItem.UserType + 1 ITEM_TYPE_NODE = QTreeWidgetItem.UserType + 1
ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 2 ITEM_TYPE_GROUP = QTreeWidgetItem.UserType + 2
ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 3 ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 3
ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 4
class QSECNode(SECNode, QObject): class QSECNode(SECNode, QObject):
@ -111,6 +111,8 @@ class MainWindow(QMainWindow):
def on_treeWidget_currentItemChanged(self, current, previous): def on_treeWidget_currentItemChanged(self, current, previous):
if current.type() == ITEM_TYPE_NODE: if current.type() == ITEM_TYPE_NODE:
self._displayNode(current.text(0)) self._displayNode(current.text(0))
elif current.type() == ITEM_TYPE_GROUP:
self._displayGroup(current.parent().text(0), current.text(0))
elif current.type() == ITEM_TYPE_MODULE: elif current.type() == ITEM_TYPE_MODULE:
self._displayModule(current.parent().text(0), current.text(0)) self._displayModule(current.parent().text(0), current.text(0))
elif current.type() == ITEM_TYPE_PARAMETER: elif current.type() == ITEM_TYPE_PARAMETER:

View File

@ -21,7 +21,7 @@
# #
# ***************************************************************************** # *****************************************************************************
from PyQt4.QtGui import QWidget, QLabel, QMessageBox from PyQt4.QtGui import QWidget, QLabel, QMessageBox, QCheckBox
from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal
from secop.gui.util import loadUi from secop.gui.util import loadUi
@ -55,6 +55,38 @@ class ParameterButtons(QWidget):
self.setLineEdit.text()) self.setLineEdit.text())
class ParameterGroup(QWidget):
def __init__(self, groupname, parent=None):
super(ParameterGroup, self).__init__(parent)
loadUi(self, 'paramgroup.ui')
self._groupname = groupname
self._row = 0
self._widgets = []
self.paramGroupBox.setTitle('Group: ' + str(groupname))
self.paramGroupBox.toggled.connect(self.on_toggle_clicked)
self.paramGroupBox.setChecked(False)
def addWidgets(self, label, widget):
self._widgets.extend((label, widget))
self.paramGroupBox.layout().addWidget(label, self._row, 0)
self.paramGroupBox.layout().addWidget(widget, self._row, 1)
label.hide()
widget.hide()
self._row += 1
def on_toggle_clicked(self):
print "ParameterGroup.on_toggle_clicked"
if self.paramGroupBox.isChecked():
for w in self._widgets:
w.show()
else:
for w in self._widgets:
w.hide()
class ModuleCtrl(QWidget): class ModuleCtrl(QWidget):
def __init__(self, node, module, parent=None): def __init__(self, node, module, parent=None):
@ -65,50 +97,128 @@ class ModuleCtrl(QWidget):
self._lastclick = None self._lastclick = None
self._paramWidgets = {} # widget cache do avoid garbage collection self._paramWidgets = {} # widget cache do avoid garbage collection
self._groupWidgets = {} # cache of grouping widgets
self._labelfont = self.font()
self._labelfont.setBold(True)
self.moduleNameLabel.setText(module) self.moduleNameLabel.setText(module)
self._initModuleWidgets() self._initModuleWidgets()
self._node.newData.connect(self._updateValue) self._node.newData.connect(self._updateValue)
def _initModuleWidgets(self): def _initModuleWidgets(self):
initValues = self._node.queryCache(self._module) initValues = self._node.queryCache(self._module)
row = 0 row = 0
font = self.font()
font.setBold(True)
for param in sorted(self._node.getParameters(self._module)):
labelstr = param + ':'
unit = self._node.getProperties(self._module, param).get('unit',
'')
descr = self._node.getProperties(self._module,
param).get('description', '')
if unit:
labelstr = "%s (%s):" % (param, unit)
label = QLabel(labelstr)
label.setFont(font)
# collect grouping information
paramsByGroup = {} # groupname -> [paramnames]
allGroups = set()
params = self._node.getParameters(self._module)
for param in params:
props = self._node.getProperties(self._module, param) props = self._node.getProperties(self._module, param)
group = props.get('group',None)
if group is not None:
allGroups.add(group)
paramsByGroup.setdefault(group, []).append(param)
buttons = ParameterButtons(self._module, param, groupWidgets = {} # groupname -> CheckBoxWidget for (un)folding
initValues[param].value,
props['readonly'])
# buttons.setRequested.connect(self._node.setParameter) # create and insert widgets into our QGridLayout
buttons.setRequested.connect(self._set_Button_pressed) for param in sorted(allGroups.union(set(params))):
labelstr = param + ':'
if param in paramsByGroup:
group = param
# is the name of a group -> create (un)foldable label
checkbox = QCheckBox(labelstr)
checkbox.setFont(self._labelfont)
groupWidgets[param] = checkbox
if descr: # check if there is a param of the same name too
buttons.setToolTip(descr) if group in params:
# yes: create a widget for this as well
labelstr, buttons = self._makeEntry(param, initValues[param].value, nolabel=True, checkbox=checkbox, invert=True)
checkbox.setText(labelstr)
# add to Layout (yes: ignore the label!)
self.paramGroupBox.layout().addWidget(checkbox, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
else:
self.paramGroupBox.layout().addWidget(checkbox, row, 0, 1, 2) # or .. 1, 2) ??
row += 1
# loop over all params and insert and connect
for param in paramsByGroup[param]:
if param == group:
continue
label, buttons = self._makeEntry(param, initValues[param].value, checkbox=checkbox, invert=False)
# add to Layout
self.paramGroupBox.layout().addWidget(label, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
row += 1
else:
# param is a 'normal' param: create a widget if it has no group or is named after a group (otherwise its created above)
props = self._node.getProperties(self._module, param)
if props.get('group', param) == param:
label, buttons = self._makeEntry(param, initValues[param].value)
# add to Layout
self.paramGroupBox.layout().addWidget(label, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
row += 1
def _makeEntry(self, param, initvalue, nolabel=False, checkbox=None, invert=False):
props = self._node.getProperties(self._module, param)
description = props.get('description', '')
unit = props.get('unit','')
if unit:
labelstr = '%s (%s):' % (param, unit)
else:
labelstr = '%s:' % (param,)
if checkbox and not invert:
labelstr = ' ' + labelstr
buttons = ParameterButtons(self._module, param, initvalue, props['readonly'])
buttons.setRequested.connect(self._set_Button_pressed)
if description:
buttons.setToolTip(description)
if nolabel:
label = labelstr
else:
label = QLabel(labelstr)
label.setFont(self._labelfont)
if checkbox:
def stateChanged(newstate, buttons=buttons, label=None if nolabel else label, invert=invert):
if (newstate and not invert) or (invert and not newstate):
buttons.show()
if label:
label.show()
else:
buttons.hide()
if label:
label.hide()
# set initial state
stateChanged(0)
# connect
checkbox.stateChanged.connect(stateChanged)
self._paramWidgets[param] = (label, buttons)
return label, buttons
self.paramGroupBox.layout().addWidget(label, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
self._paramWidgets[param] = (label, buttons)
row += 1
def _set_Button_pressed(self, module, parameter, target): def _set_Button_pressed(self, module, parameter, target):
sig = (module, parameter, target) sig = (module, parameter, target)

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>230</width> <width>238</width>
<height>121</height> <height>121</height>
</rect> </rect>
</property> </property>
@ -55,7 +55,7 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QGroupBox" name="propertyGroupBox"> <widget class="QGroupBox" name="propertyGroupBox">
<property name="title"> <property name="title">
<string>Parameters:</string> <string>Properties</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin"> <property name="leftMargin">