diff --git a/etc/cryo.cfg b/etc/cryo.cfg
index 3975843..ab4467c 100644
--- a/etc/cryo.cfg
+++ b/etc/cryo.cfg
@@ -8,7 +8,7 @@ description = short description
[interface tcp]
interface=tcp
bindto=0.0.0.0
-bindport=10769
+bindport=10767
# protocol to use for this interface
framing=eol
encoding=demo
@@ -39,3 +39,8 @@ timeout=900
# some (non-default) parameter properties
pollinterval.export=False
+# some parameter grouping
+p.group='pid'
+i.group='pid'
+d.group='pid'
+
diff --git a/secop/client/baseclient.py b/secop/client/baseclient.py
index 78add40..5b7a397 100644
--- a/secop/client/baseclient.py
+++ b/secop/client/baseclient.py
@@ -469,8 +469,11 @@ class Client(object):
def getParameters(self, module):
return self.describing_data['modules'][module]['parameters'].keys()
+ def getModuleProperties(self, module):
+ return self.describing_data['modules'][module]['properties']
+
def getModuleBaseClass(self, module):
- return self.describing_data['modules'][module]['interfaceclass']
+ return self.getModuleProperties(module)['interface']
def getCommands(self, module):
return self.describing_data['modules'][module]['commands'].keys()
diff --git a/secop/devices/cryo.py b/secop/devices/cryo.py
index caf2c37..42d58b1 100644
--- a/secop/devices/cryo.py
+++ b/secop/devices/cryo.py
@@ -65,15 +65,15 @@ class Cryostat(CryoBase):
maxpower=PARAM("Maximum heater power",
validator=nonnegative, default=1, unit="W",
readonly=False,
- group='heater',
+ group='heater_settings',
),
heater=PARAM("current heater setting",
validator=floatrange(0, 100), default=0, unit="%",
- group='heater',
+ group='heater_settings',
),
heaterpower=PARAM("current heater power",
validator=nonnegative, default=0, unit="W",
- group='heater',
+ group='heater_settings',
),
target=PARAM("target temperature",
validator=nonnegative, default=0, unit="K",
@@ -110,17 +110,17 @@ class Cryostat(CryoBase):
tolerance=PARAM("temperature range for stability checking",
validator=floatrange(0, 100), default=0.1, unit='K',
readonly=False,
- group='window',
+ group='stability',
),
window=PARAM("time window for stability checking",
validator=floatrange(1, 900), default=30, unit='s',
readonly=False,
- group='window',
+ group='stability',
),
timeout=PARAM("max waiting time for stabilisation check",
validator=floatrange(1, 36000), default=900, unit='s',
readonly=False,
- group='window',
+ group='stability',
),
)
CMDS = dict(
diff --git a/secop/gui/mainwindow.py b/secop/gui/mainwindow.py
index 16807af..5d3ae95 100644
--- a/secop/gui/mainwindow.py
+++ b/secop/gui/mainwindow.py
@@ -33,9 +33,9 @@ from secop.client.baseclient import Client as SECNode
import sys
ITEM_TYPE_NODE = QTreeWidgetItem.UserType + 1
-ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 2
-ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 3
-
+ITEM_TYPE_GROUP = QTreeWidgetItem.UserType + 2
+ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 3
+ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 4
class QSECNode(SECNode, QObject):
@@ -111,6 +111,8 @@ class MainWindow(QMainWindow):
def on_treeWidget_currentItemChanged(self, current, previous):
if current.type() == ITEM_TYPE_NODE:
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:
self._displayModule(current.parent().text(0), current.text(0))
elif current.type() == ITEM_TYPE_PARAMETER:
diff --git a/secop/gui/modulectrl.py b/secop/gui/modulectrl.py
index c35cc1d..399b99b 100644
--- a/secop/gui/modulectrl.py
+++ b/secop/gui/modulectrl.py
@@ -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 secop.gui.util import loadUi
@@ -55,6 +55,38 @@ class ParameterButtons(QWidget):
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):
def __init__(self, node, module, parent=None):
@@ -65,50 +97,128 @@ class ModuleCtrl(QWidget):
self._lastclick = None
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._initModuleWidgets()
self._node.newData.connect(self._updateValue)
+
def _initModuleWidgets(self):
initValues = self._node.queryCache(self._module)
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)
+ group = props.get('group',None)
+ if group is not None:
+ allGroups.add(group)
+ paramsByGroup.setdefault(group, []).append(param)
- buttons = ParameterButtons(self._module, param,
- initValues[param].value,
- props['readonly'])
+ groupWidgets = {} # groupname -> CheckBoxWidget for (un)folding
- # buttons.setRequested.connect(self._node.setParameter)
- buttons.setRequested.connect(self._set_Button_pressed)
+ # create and insert widgets into our QGridLayout
+ 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:
- buttons.setToolTip(descr)
+ # check if there is a param of the same name too
+ 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):
sig = (module, parameter, target)
diff --git a/secop/gui/ui/paramview.ui b/secop/gui/ui/paramview.ui
index bbe9976..5645b3a 100644
--- a/secop/gui/ui/paramview.ui
+++ b/secop/gui/ui/paramview.ui
@@ -6,7 +6,7 @@
0
0
- 230
+ 238
121
@@ -55,7 +55,7 @@
-
- Parameters:
+ Properties