Merge "provide an mean to use commands in the gui"
This commit is contained in:
commit
0aa542f07c
@ -386,6 +386,10 @@ class Client(object):
|
||||
datatype = get_datatype(parameterData['datatype'])
|
||||
self.describing_data['modules'][module]['parameters'] \
|
||||
[parameter]['datatype'] = datatype
|
||||
for cmdname, cmdData in moduleData[
|
||||
'commands'].items():
|
||||
cmdData['arguments'] = map(get_datatype, cmdData['arguments'])
|
||||
cmdData['resulttype'] = get_datatype(cmdData['resulttype'])
|
||||
except Exception as exc:
|
||||
print(formatException(verbose=True))
|
||||
raise
|
||||
@ -548,9 +552,9 @@ class Client(object):
|
||||
def getCommands(self, module):
|
||||
return self.describing_data['modules'][module]['commands']
|
||||
|
||||
def execCommand(self, module, command, args=None):
|
||||
def execCommand(self, module, command, args):
|
||||
# ignore reply message + reply specifier, only return data
|
||||
return self._communicate('do', '%s:%s' % (module, command), self.encode_message(args) if args else None)[2]
|
||||
return self._communicate('do', '%s:%s' % (module, command), list(args) if args else None)[2]
|
||||
|
||||
def getProperties(self, module, parameter):
|
||||
return self.describing_data['modules'][module]['parameters'][parameter]
|
||||
|
@ -29,6 +29,46 @@ from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal
|
||||
from secop.gui.util import loadUi
|
||||
from secop.gui.params import ParameterView
|
||||
|
||||
from secop.datatypes import *
|
||||
|
||||
from PyQt4.QtGui import QDialog, QPushButton, QLabel, QApplication, QLineEdit,\
|
||||
QGroupBox, QSpinBox, QDoubleSpinBox, QComboBox, QCheckBox, QRadioButton, \
|
||||
QVBoxLayout, QGridLayout, QScrollArea, QFrame
|
||||
|
||||
from secop.gui.valuewidgets import get_widget
|
||||
|
||||
|
||||
class CommandDialog(QDialog):
|
||||
def __init__(self, cmdname, arglist, parent=None):
|
||||
super(CommandDialog, self).__init__(parent)
|
||||
loadUi(self, 'cmddialog.ui')
|
||||
|
||||
self.setWindowTitle('Arguments for %s' % cmdname)
|
||||
row = 0
|
||||
|
||||
self._labels = []
|
||||
self.widgets = []
|
||||
for row, dtype in enumerate(arglist):
|
||||
l = QLabel(repr(dtype))
|
||||
l.setWordWrap(True)
|
||||
w = get_widget(dtype, readonly=False)
|
||||
self.gridLayout.addWidget(l, row, 0)
|
||||
self.gridLayout.addWidget(w, row, 1)
|
||||
self._labels.append(l)
|
||||
self.widgets.append(w)
|
||||
|
||||
self.gridLayout.setRowStretch(len(arglist), 1)
|
||||
self.setModal(True)
|
||||
self.resize(self.sizeHint())
|
||||
|
||||
def get_value(self):
|
||||
return [w.get_value() for w in self.widgets]
|
||||
|
||||
def exec_(self):
|
||||
if super(CommandDialog, self).exec_():
|
||||
return self.get_value()
|
||||
|
||||
|
||||
|
||||
def showCommandResultDialog(command, args, result, extras=''):
|
||||
m = QMessageBox()
|
||||
@ -69,7 +109,6 @@ class ParameterGroup(QWidget):
|
||||
self._row += 1
|
||||
|
||||
def on_toggle_clicked(self):
|
||||
print("ParameterGroup.on_toggle_clicked")
|
||||
if self.paramGroupBox.isChecked():
|
||||
for w in self._widgets:
|
||||
w.show()
|
||||
@ -78,19 +117,6 @@ class ParameterGroup(QWidget):
|
||||
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):
|
||||
@ -108,10 +134,10 @@ class CommandButton(QButton):
|
||||
|
||||
def on_pushButton_pressed(self):
|
||||
self.setEnabled(False)
|
||||
if self._argintypes or 1:
|
||||
args = CommandArgumentsDialog(self._cmdname, self._argintypes)
|
||||
if self._argintypes:
|
||||
dlg = CommandDialog(self._cmdname, self._argintypes)
|
||||
args = dlg.exec_()
|
||||
if args: # not 'Cancel' clicked
|
||||
print('############# %s', args)
|
||||
self._cb(self._cmdname, args)
|
||||
else:
|
||||
# no need for arguments
|
||||
@ -140,17 +166,10 @@ class ModuleCtrl(QWidget):
|
||||
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)
|
||||
self._module, command, args)
|
||||
showCommandResultDialog(command, args, result, qualifiers)
|
||||
|
||||
def _initModuleWidgets(self):
|
||||
|
@ -138,7 +138,18 @@ class NodeCtrl(QWidget):
|
||||
else:
|
||||
interfaces = modprops['interfaces']
|
||||
description = modprops['description']
|
||||
unit = self._node.getProperties(modname, 'value').get('unit', '')
|
||||
|
||||
# fallback: allow (now) invalid 'Driveable'
|
||||
if 'Drivable' in interfaces or 'Driveable' in interfaces:
|
||||
widget = DrivableWidget(self._node, modname, self)
|
||||
unit = self._node.getProperties(modname, 'value').get('unit', '')
|
||||
elif 'Readable' in interfaces:
|
||||
widget = ReadableWidget(self._node, modname, self)
|
||||
unit = self._node.getProperties(modname, 'value').get('unit', '')
|
||||
else:
|
||||
widget = QLabel('Unsupported Interfaceclass %r' % interfaces)
|
||||
unit = ''
|
||||
|
||||
|
||||
if unit:
|
||||
labelstr = '%s (%s):' % (modname, unit)
|
||||
@ -147,14 +158,6 @@ class NodeCtrl(QWidget):
|
||||
label = QLabel(labelstr)
|
||||
label.setFont(labelfont)
|
||||
|
||||
# fallback: allow (now) invalid 'Driveable'
|
||||
if 'Drivable' in interfaces or 'Driveable' in interfaces:
|
||||
widget = DrivableWidget(self._node, modname, self)
|
||||
elif 'Readable' in interfaces:
|
||||
widget = ReadableWidget(self._node, modname, self)
|
||||
else:
|
||||
widget = QLabel('Unsupported Interfaceclasses %r' % interfaces)
|
||||
|
||||
if description:
|
||||
widget.setToolTip(description)
|
||||
|
||||
|
88
secop/gui/ui/cmddialog.ui
Normal file
88
secop/gui/ui/cmddialog.ui
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>135</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>242</width>
|
||||
<height>76</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
264
secop/gui/valuewidgets.py
Normal file
264
secop/gui/valuewidgets.py
Normal file
@ -0,0 +1,264 @@
|
||||
# -*- 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:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
from secop.datatypes import *
|
||||
|
||||
from PyQt4.QtGui import QDialog, QPushButton, QLabel, QApplication, QLineEdit,\
|
||||
QGroupBox, QSpinBox, QDoubleSpinBox, QComboBox, QCheckBox, QRadioButton, \
|
||||
QVBoxLayout, QGridLayout, QScrollArea, QFrame
|
||||
|
||||
from secop.gui.util import loadUi
|
||||
|
||||
|
||||
# XXX: implement live validators !!!!
|
||||
|
||||
class StringWidget(QLineEdit):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(StringWidget, self).__init__(parent)
|
||||
self.datatype = datatype
|
||||
if readonly:
|
||||
self.setEnabled(False)
|
||||
|
||||
def get_value(self):
|
||||
res = self.text()
|
||||
return self.datatype.validate(res)
|
||||
|
||||
def set_value(self, value):
|
||||
self.setText(value)
|
||||
|
||||
|
||||
class BlobWidget(StringWidget):
|
||||
# XXX: make an editable hex-table ?
|
||||
pass
|
||||
|
||||
# or derive from widget and switch between combobox and radiobuttons?
|
||||
class EnumWidget(QComboBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(EnumWidget, self).__init__(parent)
|
||||
self.datatype = datatype
|
||||
|
||||
self._map = {}
|
||||
self._revmap = {}
|
||||
for idx, (val, name) in enumerate(datatype.entries.items()):
|
||||
self._map[idx] = (name, val)
|
||||
self._revmap[name] = idx
|
||||
self._revmap[val] = idx
|
||||
self.addItem(name, val)
|
||||
# XXX: fill Combobox from datatype
|
||||
|
||||
def get_value(self):
|
||||
# XXX: return integer corresponding to the selected item
|
||||
return self._map[self.currentIndex()][1]
|
||||
|
||||
def set_value(self, value):
|
||||
self.setCurrentIndex(self._revmap[value])
|
||||
|
||||
|
||||
class BoolWidget(QCheckBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(BoolWidget, self).__init__(parent)
|
||||
self.datatype = datatype
|
||||
if readonly:
|
||||
self.setEnabled(False)
|
||||
|
||||
def get_value(self):
|
||||
return self.isChecked()
|
||||
|
||||
def set_value(self, value):
|
||||
self.setChecked(bool(value))
|
||||
|
||||
|
||||
class IntWidget(QSpinBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(IntWidget, self).__init__(parent)
|
||||
self.datatype = datatype
|
||||
if readonly:
|
||||
self.setEnabled(False)
|
||||
self.setMaximum(datatype.max)
|
||||
self.setMinimum(datatype.min)
|
||||
|
||||
def get_value(self):
|
||||
return int(self.value())
|
||||
|
||||
def set_value(self, value):
|
||||
self.setValue(int(value))
|
||||
|
||||
|
||||
class FloatWidget(QDoubleSpinBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(FloatWidget, self).__init__(parent)
|
||||
self.datatype = datatype
|
||||
if readonly:
|
||||
self.setEnabled(False)
|
||||
self.setMaximum(datatype.max)
|
||||
self.setMinimum(datatype.min)
|
||||
self.setDecimals(12)
|
||||
|
||||
def get_value(self):
|
||||
return float(self.value())
|
||||
|
||||
def set_value(self, value):
|
||||
self.setValue(float(value))
|
||||
|
||||
|
||||
class TupleWidget(QFrame):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(TupleWidget, self).__init__(parent)
|
||||
|
||||
self.datatypes = datatype.subtypes
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
self.subwidgets = []
|
||||
for t in self.datatypes:
|
||||
w = get_widget(t, readonly=readonly, parent=self)
|
||||
w.show()
|
||||
self.layout.addWidget(w)
|
||||
self.subwidgets.append(w)
|
||||
self.setLayout(self.layout)
|
||||
self.show()
|
||||
self.update()
|
||||
|
||||
def get_value(self):
|
||||
return [v.validate(w.get_value()) for w,v in zip(self.subwidgets, self.datatypes)]
|
||||
|
||||
def set_value(self, value):
|
||||
for w, v in zip(self.subwidgets, value):
|
||||
w.set_value(value)
|
||||
|
||||
|
||||
class StructWidget(QGroupBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(StructWidget, self).__init__(parent)
|
||||
|
||||
self.layout = QGridLayout()
|
||||
self.subwidgets = {}
|
||||
self.datatypes = []
|
||||
self._labels = []
|
||||
for idx, name in enumerate(sorted(datatype.named_subtypes)):
|
||||
dt = datatype.named_subtypes[name]
|
||||
w = get_widget(dt, readonly=readonly, parent=self)
|
||||
l = QLabel(name)
|
||||
self.layout.addWidget(l, idx, 0)
|
||||
self.layout.addWidget(w, idx, 1)
|
||||
self._labels.append(l)
|
||||
self.subwidgets[name] = (w, dt)
|
||||
self.datatypes.append(dt)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def get_value(self):
|
||||
res = {}
|
||||
for name, entry in self.subwidgets.items():
|
||||
w, dt = entry
|
||||
res[name] = dt.validate(w.get_value())
|
||||
return res
|
||||
|
||||
def set_value(self, value):
|
||||
for k, v in value.items():
|
||||
entry = self.subwidgets[k]
|
||||
w, dt = entry
|
||||
w.set_value(dt.validate(v))
|
||||
|
||||
|
||||
class ArrayWidget(QGroupBox):
|
||||
def __init__(self, datatype, readonly=False, parent=None):
|
||||
super(ArrayWidget, self).__init__(parent)
|
||||
self.datatype = datatype.subtype
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
self.subwidgets = []
|
||||
for i in range(datatype.maxsize):
|
||||
w = get_widget(self.datatype, readonly=readonly, parent=self)
|
||||
self.layout.addWidget(w)
|
||||
self.subwidgets.append(w)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def get_value(self):
|
||||
return [self.datatype.validate(w.get_value()) for w in self.subwidgets]
|
||||
|
||||
def set_value(self, values):
|
||||
for w, v in zip(self.subwidgets, values):
|
||||
w.set_value(v)
|
||||
|
||||
|
||||
|
||||
def get_widget(datatype, readonly=False, parent=None):
|
||||
return {FloatRange: FloatWidget,
|
||||
IntRange: IntWidget,
|
||||
StringType: StringWidget,
|
||||
BLOBType: BlobWidget,
|
||||
EnumType: EnumWidget,
|
||||
BoolType: BoolWidget,
|
||||
TupleOf: TupleWidget,
|
||||
StructOf: StructWidget,
|
||||
ArrayOf: ArrayWidget,
|
||||
}.get(datatype.__class__)(datatype, readonly, parent)
|
||||
|
||||
|
||||
class msg(QDialog):
|
||||
def __init__(self, stuff, parent=None):
|
||||
super(msg, self).__init__(parent)
|
||||
loadUi(self, 'cmddialog.ui')
|
||||
print(dir(self))
|
||||
self.setWindowTitle('Please enter the arguments for calling command "blubb()"')
|
||||
row = 0
|
||||
# self.gridLayout.addWidget(QLabel('String'), row, 0); self.gridLayout.addWidget(StringWidget(StringType()), row, 1); row += 1
|
||||
# self.gridLayout.addWidget(QLabel('Blob'), row, 0); self.gridLayout.addWidget(BlobWidget(BLOBType()), row, 1); row += 1
|
||||
# self.gridLayout.addWidget(QLabel('Enum'), row, 0); self.gridLayout.addWidget(EnumWidget(EnumType(a=1,b=9)), row, 1); row += 1
|
||||
# self.gridLayout.addWidget(QLabel('Bool'), row, 0); self.gridLayout.addWidget(BoolWidget(BoolType()), row, 1); row += 1
|
||||
# self.gridLayout.addWidget(QLabel('int'), row, 0); self.gridLayout.addWidget(IntWidget(IntRange(0,9)), row, 1); row += 1
|
||||
# self.gridLayout.addWidget(QLabel('float'), row, 0); self.gridLayout.addWidget(FloatWidget(FloatRange(-9,9)), row, 1); row += 1
|
||||
|
||||
#self.gridLayout.addWidget(QLabel('tuple'), row, 0);
|
||||
#dt = TupleOf(BoolType(), EnumType(a=2,b=3))
|
||||
#w = TupleWidget(dt)
|
||||
#self.gridLayout.addWidget(w, row, 1)
|
||||
#row+=1
|
||||
|
||||
#self.gridLayout.addWidget(QLabel('array'), row, 0);
|
||||
#dt = ArrayOf(IntRange(0,10), 10)
|
||||
#w = ArrayWidget(dt)
|
||||
#self.gridLayout.addWidget(w, row, 1)
|
||||
#row+=1
|
||||
|
||||
self.gridLayout.addWidget(QLabel('struct'), row, 0);
|
||||
dt = StructOf(i=IntRange(0,10), f=FloatRange(), b=BoolType())
|
||||
w = StructWidget(dt)
|
||||
self.gridLayout.addWidget(w, row, 1)
|
||||
row+=1
|
||||
|
||||
self.gridLayout.addWidget(QLabel('stuff'), row, 0, 1, 0); row += 1 # at pos (0,0) span 2 cols, 1 row
|
||||
self.gridLayout.setRowStretch(row, 1)
|
||||
self.setModal(True)
|
||||
|
||||
def accept(self):
|
||||
print('accepted')
|
||||
super(msg, self).accept()
|
||||
|
||||
def reject(self):
|
||||
print('rejected')
|
||||
super(msg, self).reject()
|
||||
|
||||
def done(self, how):
|
||||
print('done(%r)' % how)
|
||||
return super(msg, self).done(how)
|
||||
|
Loading…
x
Reference in New Issue
Block a user