Stub debug client gui.

Change-Id: Ib422c66bc36245e1fc3c450765d7555da5c8dda0
This commit is contained in:
Alexander Lenz 2016-12-05 16:20:55 +01:00
parent 68f73b5aa1
commit d442da0789
17 changed files with 935 additions and 10 deletions

2
.gitignore vendored
View File

@ -3,3 +3,5 @@ html/*
*.pyc
pid/*
# ide
.idea

53
bin/secop-gui Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env 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>
#
# *****************************************************************************
import sys
from os import path
# Add import path for inplace usage
sys.path.insert(0, path.abspath(path.join(path.dirname(__file__), '..')))
from secop.gui.mainwindow import MainWindow
from secop import loggers
from PyQt4.QtGui import QApplication
def main(argv=None):
if argv is None:
argv = sys.argv
loggers.initLogging('gui', 'debug')
app = QApplication(argv)
win = MainWindow()
win.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -0,0 +1,26 @@
# -*- 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>
#
# *****************************************************************************
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)

View File

@ -29,6 +29,7 @@ import threading
import Queue
from secop import loggers
from secop.validators import validator_from_str
from secop.lib import mkthread
from secop.lib.parsing import parse_time, format_time
from secop.protocol.encoding import ENCODERS
@ -137,7 +138,7 @@ class Client(object):
describing_data = {}
stopflag = False
def __init__(self, opts):
def __init__(self, opts, autoconnect=True):
self.log = loggers.log.getChild('client', True)
self._cache = dict()
if 'device' in opts:
@ -164,13 +165,15 @@ class Client(object):
# mapping the modulename to a dict mapping the parameter names to their values
# note: the module value is stored as the value of the parameter value
# of the module
self.cache = dict()
self._syncLock = threading.RLock()
self._thread = threading.Thread(target=self._run)
self._thread.daemon = True
self._thread.start()
if autoconnect:
self.startup()
def _run(self):
while not self.stopflag:
try:
@ -247,7 +250,7 @@ class Client(object):
self.log.warning("deprecated specifier %r" % spec)
spec = '%s:value' % spec
modname, pname = spec.split(':', 1)
self.cache.setdefault(modname, {})[pname] = Value(*data)
self._cache.setdefault(modname, {})[pname] = Value(*data)
if spec in self.callbacks:
for func in self.callbacks[spec]:
try:
@ -265,6 +268,22 @@ class Client(object):
run.add(func)
self.single_shots[spec].difference_update(run)
def _getDescribingModuleData(self, module):
return self.describingModulesData[module]
def _getDescribingParameterData(self, module, parameter):
return self._getDescribingModuleData(module)['parameters'][parameter]
def _issueDescribe(self):
_, self.equipment_id, self.describing_data = self.communicate(
'describe')
for module, moduleData in self.describing_data['modules'].items():
for parameter, parameterData in moduleData['parameters'].items():
validator = validator_from_str(parameterData['validator'])
self.describing_data['modules'][module]['parameters'] \
[parameter]['validator'] = validator
def register_callback(self, module, parameter, cb):
self.log.debug(
'registering callback %r for %s:%s' %
@ -343,14 +362,36 @@ class Client(object):
# XXX: further notification-callbacks needed ???
def startup(self, async=False):
_, self.equipment_id, self.describing_data = self.communicate(
'describe')
self._issueDescribe()
# always fill our cache
self.communicate('activate')
# deactivate updates if not wanted
if not async:
self.communicate('deactivate')
def queryCache(self, module, parameter=None):
result = self._cache.get(module, {})
if parameter is not None:
result = result[parameter]
return result
def setParameter(self, module, parameter, value):
validator = self._getDescribingParameterData(module,
parameter)['validator']
value = validator(value)
self.communicate('change', '%s:%s' % (module, parameter), value)
@property
def describingData(self):
return self.describing_data
@property
def describingModulesData(self):
return self.describingData['modules']
@property
def equipmentId(self):
return self.equipment_id

View File

@ -74,7 +74,8 @@ class PARAM(object):
readonly=self.readonly,
value=self.value,
timestamp=self.timestamp,
validator=repr(self.validator),
validator=str(self.validator) if not isinstance(
self.validator, type) else self.validator.__name__
)

0
secop/gui/__init__.py Normal file
View File

136
secop/gui/mainwindow.py Normal file
View File

@ -0,0 +1,136 @@
# -*- 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 PyQt4.QtGui import QMainWindow, QInputDialog, QTreeWidgetItem
from PyQt4.QtCore import pyqtSignature as qtsig, QObject, pyqtSignal
from secop.gui.util import loadUi
from secop.gui.nodectrl import NodeCtrl
from secop.gui.modulectrl import ModuleCtrl
from secop.client.baseclient import Client as SECNode
ITEM_TYPE_NODE = QTreeWidgetItem.UserType + 1
ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 2
ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 3
class QSECNode(SECNode, QObject):
newData = pyqtSignal(str, str, object) # module, parameter, data
def __init__(self, opts, autoconnect=False, parent=None):
SECNode.__init__(self, opts, autoconnect)
QObject.__init__(self, parent)
self.startup(True)
self._subscribeCallbacks()
def _subscribeCallbacks(self):
for module in self.modules:
self._subscribeModuleCallback(module)
def _subscribeModuleCallback(self, module):
for parameter in self.getParameters(module):
self._subscribeParameterCallback(module, parameter)
def _subscribeParameterCallback(self, module, parameter):
self.register_callback(module, parameter, self._newDataReceived)
def _newDataReceived(self, module, parameter, data):
self.newData.emit(module, parameter, data)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
loadUi(self, 'mainwindow.ui')
self.splitter.setStretchFactor(0, 1)
self.splitter.setStretchFactor(1, 70)
self._nodes = {}
self._nodeCtrls = {}
self._currentWidget = self.splitter.widget(1).layout().takeAt(0)
# add localhost if available
self._addNode('localhost')
@qtsig('')
def on_actionAdd_SEC_node_triggered(self):
host, ok = QInputDialog.getText(self, 'Add SEC node',
'Enter SEC node hostname:')
if not ok:
return
self._addNode(host)
def on_treeWidget_currentItemChanged(self, current, previous):
if current.type() == ITEM_TYPE_NODE:
self._displayNode(current.text(0))
elif current.type() == ITEM_TYPE_MODULE:
self._displayModule(current.parent().text(0), current.text(0))
def _addNode(self, host):
# create client
port = 10767
if ':' in host:
host, port = host.split(':', 1)
port = int(port)
node = QSECNode({'connectto':host, 'port':port}, parent=self)
host = '%s:%d' % (host, port)
self._nodes[host] = node
# fill tree
nodeItem = QTreeWidgetItem(None, [host], ITEM_TYPE_NODE)
for module in sorted(node.modules):
moduleItem = QTreeWidgetItem(nodeItem, [module], ITEM_TYPE_MODULE)
for param in sorted(node.getParameters(module)):
paramItem = QTreeWidgetItem(moduleItem, [param],
ITEM_TYPE_PARAMETER)
self.treeWidget.addTopLevelItem(nodeItem)
def _displayNode(self, node):
ctrl = self._nodeCtrls.get(node, None)
if ctrl is None:
ctrl = self._nodeCtrls[node] = NodeCtrl(self._nodes[node])
self._replaceCtrlWidget(ctrl)
def _displayModule(self, node, module):
self._replaceCtrlWidget(ModuleCtrl(self._nodes[node], module))
def _replaceCtrlWidget(self, new):
old = self.splitter.widget(1).layout().takeAt(0)
if old:
old.widget().hide()
self.splitter.widget(1).layout().addWidget(new)
new.show()
self._currentWidget = new

86
secop/gui/modulectrl.py Normal file
View File

@ -0,0 +1,86 @@
# -*- 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 PyQt4.QtGui import QWidget, QLabel
from PyQt4.QtCore import pyqtSignature as qtsig, Qt, pyqtSignal
from secop.gui.util import loadUi
class ParameterButtons(QWidget):
setRequested = pyqtSignal(str, str, str) # module, parameter, setpoint
def __init__(self, module, parameter, initval='', parent=None):
super(ParameterButtons, self).__init__(parent)
loadUi(self, 'parambuttons.ui')
self._module = module
self._parameter = parameter
self.currentLineEdit.setText(str(initval))
def on_setPushButton_clicked(self):
self.setRequested.emit(self._module, self._parameter,
self.setLineEdit.text())
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._paramWidgets = {} # widget cache do avoid garbage collection
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)):
label = QLabel(param + ':')
label.setFont(font)
buttons = ParameterButtons(self._module, param,
initValues[param].value)
buttons.setRequested.connect(self._node.setParameter)
self.paramGroupBox.layout().addWidget(label, row, 0)
self.paramGroupBox.layout().addWidget(buttons, row, 1)
self._paramWidgets[param] = (label, buttons)
row += 1
def _updateValue(self, module, parameter, value):
if module != self._module:
return
self._paramWidgets[parameter][1].currentLineEdit.setText(str(value[0]))

92
secop/gui/nodectrl.py Normal file
View File

@ -0,0 +1,92 @@
# -*- 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>
#
# *****************************************************************************
import pprint
from PyQt4.QtGui import QWidget, QTextCursor, QFont, QFontMetrics
from PyQt4.QtCore import pyqtSignature as qtsig, Qt
from secop.gui.util import loadUi
class NodeCtrl(QWidget):
def __init__(self, node, parent=None):
super(NodeCtrl, self).__init__(parent)
loadUi(self, 'nodectrl.ui')
self._node = node
self.contactPointLabel.setText(self._node.contactPoint)
self.equipmentIdLabel.setText(self._node.equipmentId)
self.protocolVersionLabel.setText(self._node.protocolVersion)
self._clearLog()
@qtsig('')
def on_sendPushButton_clicked(self):
msg = self.msgLineEdit.text().strip()
if not msg:
return
self._addLogEntry('<span style="font-weight:bold">Request:</span> '
'%s:' % msg, raw=True)
msg = msg.split(' ', 2)
reply = self._node.syncCommunicate(*msg)
self._addLogEntry(reply, newline=True, pretty=True)
@qtsig('')
def on_clearPushButton_clicked(self):
self._clearLog()
def _clearLog(self):
self.logTextBrowser.clear()
self._addLogEntry('SECoP Communication Shell')
self._addLogEntry('=========================')
self._addLogEntry('', newline=True)
def _addLogEntry(self, msg, newline=False, pretty=False, raw=False):
if pretty:
msg = pprint.pformat(msg, width=self._getLogWidth())
if not raw:
msg = '<pre>%s</pre>' % Qt.escape(str(msg)).replace('\n', '<br />')
content = ''
if self.logTextBrowser.toPlainText():
content = self.logTextBrowser.toHtml()
content += msg
if newline:
content += '<br />'
self.logTextBrowser.setHtml(content)
self.logTextBrowser.moveCursor(QTextCursor.End)
def _getLogWidth(self):
fontMetrics = QFontMetrics(QFont('Monospace'))
# calculate max avail characters by using an a (which is possible
# due to monospace)
result = self.logTextBrowser.width() / fontMetrics.width('a')
return result

118
secop/gui/ui/mainwindow.ui Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1228</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>secop-gui</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1228</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="separator"/>
<addaction name="actionAdd_SEC_node"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionAbout"/>
<addaction name="actionAbout_Qt"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionAdd_SEC_node"/>
</widget>
<action name="actionAdd_SEC_node">
<property name="text">
<string>Add SEC node</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionAbout">
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionAbout_Qt">
<property name="text">
<string>About Qt</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>230</width>
<height>121</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<italic>false</italic>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Module name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="moduleNameLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="paramGroupBox">
<property name="title">
<string>Parameters:</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="spacing">
<number>6</number>
</property>
</layout>
</widget>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

153
secop/gui/ui/nodectrl.ui Normal file
View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>652</width>
<height>490</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>652</width>
<height>490</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contact point:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="contactPointLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Equipment ID:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="equipmentIdLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Protocol version:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="protocolVersionLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QLineEdit" name="msgLineEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&gt;&gt;&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="sendPushButton">
<property name="text">
<string>Send</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="clearPushButton">
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QTextBrowser" name="logTextBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>msgLineEdit</sender>
<signal>returnPressed()</signal>
<receiver>sendPushButton</receiver>
<slot>animateClick()</slot>
<hints>
<hint type="sourcelabel">
<x>387</x>
<y>459</y>
</hint>
<hint type="destinationlabel">
<x>498</x>
<y>462</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>730</width>
<height>31</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Current: </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="currentLineEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Set: </string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="setLineEdit">
<property name="minimumSize">
<size>
<width>256</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="setPushButton">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

33
secop/gui/util.py Normal file
View File

@ -0,0 +1,33 @@
# -*- 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 os import path
from PyQt4 import uic
uipath = path.dirname(__file__)
def loadUi(widget, uiname, subdir='ui'):
uic.loadUi(path.join(uipath, subdir, uiname), widget)

View File

@ -61,6 +61,7 @@ def mkthread(func, *args, **kwds):
t.start()
return t
if __name__ == '__main__':
print "minimal testing: lib"
d = attrdict(a=1, b=2)

View File

@ -278,7 +278,7 @@ class Dispatcher(object):
def _getParamValue(self, modulename, pname):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchmoduleError(module=modulename)
raise NoSuchModuleError(module=modulename)
pobj = moduleobj.PARAMS.get(pname, None)
if pobj is None:

View File

@ -30,6 +30,7 @@
# if a validator does a mapping, it normally maps to the external representation (used for print/log/protocol/...)
# to get the internal representation (for the code), call method convert
class ProgrammingError(Exception):
pass
@ -156,7 +157,7 @@ class vector(object):
def __init__(self, *args):
self.validators = args
self.argstr = ', '.join([repr(e) for e in args])
self.argstr = ', '.join([validator_to_str(e) for e in args])
def __call__(self, args):
if len(args) != len(self.validators):
@ -173,7 +174,7 @@ class record(object):
def __init__(self, **kwds):
self.validators = args
self.argstr = ', '.join([repr(e) for e in kwds.items()])
self.argstr = ', '.join([validator_to_str(e) for e in kwds.items()])
def __call__(self, arg):
if len(args) != len(self.validators):
@ -190,7 +191,7 @@ class oneof(object):
def __init__(self, *args):
self.oneof = args
self.argstr = ', '.join([repr(e) for e in args])
self.argstr = ', '.join([validator_to_str(e) for e in args])
def __call__(self, arg):
for v in self.oneof:
@ -247,3 +248,12 @@ class enum(object):
def convert(self, arg):
return self.mapping.get(arg, arg)
def validator_to_str(validator):
return str(validator) if not isinstance(validator, type) \
else validator.__name__
def validator_from_str(validator_str):
return eval(validator_str)