frappy/secop/gui/modulectrl.py
Enrico Faulhaber b26b64032e fixups
Change-Id: Ib0c6fddd38f594d735feadce31e4809e7a4c5d44
2017-09-11 11:27:52 +02:00

348 lines
12 KiB
Python

# -*- coding: utf-8 -*-
# *****************************************************************************
# Copyright (c) 2015-2016 by the authors, see LICENSE
#
# This program 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 2 of the License, or (at your option) any later
# version.
#
# This program 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
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Alexander Lenz <alexander.lenz@frm2.tum.de>
#
# *****************************************************************************
from __future__ import print_function
from PyQt4.QtGui import QWidget, QLabel, QPushButton as QButton, QLineEdit, QMessageBox, QCheckBox, QSizePolicy, QDialog
from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal
from secop.gui.util import loadUi
from secop.gui.params import ParameterView
def showCommandResultDialog(command, args, result, extras=''):
m = QMessageBox()
if not args:
args = ''
m.setText('calling: %s(%s)\nyielded: %s\nqualifiers: %s' %
(command, args, result, extras))
m.exec_()
def showErrorDialog(error):
m = QMessageBox()
m.setText('Error %r' % error)
m.exec_()
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 CommandArgumentsDialog(QDialog):
def __init__(self, commandname, argtypes, parent=None):
super(CommandArgumentsDialog, self).__init__(parent)
# XXX: fill in apropriate widgets + OK/Cancel
def exec_(self):
print('CommandArgumentsDialog result is', super(
CommandArgumentsDialog, self).exec_())
return None # XXX: if there were arguments, return them after validation or None for 'Cancel'
class CommandButton(QButton):
def __init__(self, cmdname, cmdinfo, cb, parent=None):
super(CommandButton, self).__init__(parent)
self._cmdname = cmdname
self._argintypes = cmdinfo['arguments'] # list of datatypes
self.resulttype = cmdinfo['resulttype']
self._cb = cb # callback function for exection
self.setText(cmdname)
if cmdinfo['description']:
self.setToolTip(cmdinfo['description'])
self.pressed.connect(self.on_pushButton_pressed)
def on_pushButton_pressed(self):
self.setEnabled(False)
if self._argintypes or 1:
args = CommandArgumentsDialog(self._cmdname, self._argintypes)
if args: # not 'Cancel' clicked
print('############# %s', args)
self._cb(self._cmdname, args)
else:
# no need for arguments
self._cb(self._cmdname, None)
self.setEnabled(True)
class ModuleCtrl(QWidget):
def __init__(self, node, module, parent=None):
super(ModuleCtrl, self).__init__(parent)
loadUi(self, 'modulectrl.ui')
self._node = node
self._module = module
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 _execCommand(self, command, args=None):
if args: # try to validate input
# XXX: check datatypes with their validators?
import ast
try:
args = ast.literal_eval(args)
except Exception as e:
return showErrorDialog(e)
if not args:
args = tuple()
result, qualifiers = self._node.execCommand(
self._module, command, *args)
showCommandResultDialog(command, args, result, qualifiers)
def _initModuleWidgets(self):
initValues = self._node.queryCache(self._module)
row = 0
# ignore groupings for commands (for now)
commands = self._node.getCommands(self._module)
# keep a reference or the widgets are detroyed to soon.
self.cmdWidgets = cmdWidgets = {}
# create and insert widgets into our QGridLayout
for command in sorted(commands):
# XXX: fetch and use correct datatypes here!
w = CommandButton(command, commands[command], self._execCommand)
cmdWidgets[command] = w
self.commandGroupBox.layout().addWidget(w, 0, row)
row += 1
row = 0
# 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)
# enforce reading initial value if not already in cache
if param not in initValues:
self._node.getParameter(self._module, param)
# groupname -> CheckBoxWidget for (un)folding
self._groupWidgets = groupWidgets = {}
# 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
# check if there is a param of the same name too
if group in params:
datatype = self._node.getProperties(
self._module, group).get(
'datatype', None)
# yes: create a widget for this as well
labelstr, buttons = self._makeEntry(
param, initValues[param].value, datatype=datatype, 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
if param_ not in initValues:
initval = None
print("Warning: %r not in initValues!" % param_)
else:
initval = initValues[param_].value
datatype = self._node.getProperties(
self._module, param_).get(
'datatype', None)
label, buttons = self._makeEntry(
param_, initval, 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:
datatype = self._node.getProperties(
self._module, param).get(
'datatype', None)
label, buttons = self._makeEntry(
param, initValues[param].value, datatype=datatype)
# add to Layout
self.paramGroupBox.layout().addWidget(label, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
row += 1
# also populate properties
self._propWidgets = {}
props = self._node.getModuleProperties(self._module)
row = 0
for prop in sorted(props):
label = QLabel(prop + ':')
label.setFont(self._labelfont)
label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
# make 'display' label
view = QLabel(str(props[prop]))
view.setFont(self.font())
view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
view.setWordWrap(True)
self.propertyGroupBox.layout().addWidget(label, row, 0)
self.propertyGroupBox.layout().addWidget(view, row, 1)
row += 1
self._propWidgets[prop] = (label, view)
def _makeEntry(
self,
param,
initvalue,
datatype=None,
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 = ParameterView(
self._module,
param,
datatype=datatype,
initvalue=initvalue,
readonly=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
def _set_Button_pressed(self, module, parameter, target):
sig = (module, parameter, target)
if self._lastclick == sig:
return
self._lastclick = sig
try:
self._node.setParameter(module, parameter, target)
except Exception as e:
QMessageBox.warning(self.parent(), 'Operation failed', str(e))
def _updateValue(self, module, parameter, value):
if module != self._module:
return
self._paramWidgets[parameter][1].updateValue(str(value[0]))