Add greeter tab to UI

* adds a tab on startup that shows last connected secnodes
* last nodes saved with qsettings

Change-Id: I2b663a408dc46bd0a0135e723b55d5ef3661bec8
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30524
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Alexander Zaft <a.zaft@fz-juelich.de>
This commit is contained in:
Alexander Zaft 2023-02-27 12:41:58 +01:00 committed by Markus Zolliker
parent 4c577cf83d
commit f1ea85bfa7
7 changed files with 1419 additions and 1077 deletions

View File

@ -24,7 +24,7 @@
import frappy.client import frappy.client
from frappy.gui.qt import QInputDialog, QMainWindow, QMessageBox, QObject, \ from frappy.gui.qt import QInputDialog, QMainWindow, QMessageBox, QObject, \
QTreeWidgetItem, pyqtSignal, pyqtSlot QTreeWidgetItem, pyqtSignal, pyqtSlot, QWidget, QSettings
from frappy.gui.util import Value, Colors, loadUi from frappy.gui.util import Value, Colors, loadUi
from frappy.lib import formatExtendedTraceback from frappy.lib import formatExtendedTraceback
from frappy.gui.logwindow import LogWindow from frappy.gui.logwindow import LogWindow
@ -115,7 +115,39 @@ class QSECNode(QObject):
self.unhandledMsg.emit('%s %s %r' % (action, specifier, data)) self.unhandledMsg.emit('%s %s %r' % (action, specifier, data))
class Greeter(QWidget):
recentClearBtn = pyqtSignal()
addnodes = pyqtSignal(list)
def __init__(self, parent=None):
super().__init__(parent)
loadUi(self, 'greeter.ui')
self.loadRecent()
def loadRecent(self):
self.recentNodes.clear()
settings = QSettings()
recent = settings.value('recent', [])
for host in recent:
self.recentNodes.addItem(host)
@pyqtSlot()
def on_ClearButton_clicked(self):
self.recentClearBtn.emit()
@pyqtSlot()
def on_connectRecentButton_clicked(self):
selected = self.recentNodes.selectedItems()
hosts = [item.text() for item in selected]
self.addnodes.emit(hosts)
@pyqtSlot()
def on_AddSECNodeButton_clicked(self):
self.addnodes.emit([self.secnodeEdit.text()
or self.secnodeEdit.placeholderText()])
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
recentNodesChanged = pyqtSignal()
def __init__(self, hosts, logger, parent=None): def __init__(self, hosts, logger, parent=None):
super().__init__(parent) super().__init__(parent)
@ -147,6 +179,13 @@ class MainWindow(QMainWindow):
print(formatExtendedTraceback()) print(formatExtendedTraceback())
self.log.error('error in addNode: %r', e) self.log.error('error in addNode: %r', e)
if not self._nodes:
greeter = Greeter(self)
greeter.addnodes.connect(self._addNodes)
greeter.recentClearBtn.connect(self.on_actionClear_triggered)
self.recentNodesChanged.connect(greeter.loadRecent)
self.tab.addPanel(greeter, 'Welcome')
@pyqtSlot() @pyqtSlot()
def on_actionAdd_SEC_node_triggered(self): def on_actionAdd_SEC_node_triggered(self):
host, ok = QInputDialog.getText(self, 'Add SEC node', host, ok = QInputDialog.getText(self, 'Add SEC node',
@ -174,8 +213,17 @@ class MainWindow(QMainWindow):
# if level in ['user', 'admin', 'expert']: # if level in ['user', 'admin', 'expert']:
# print('visibility Level now:', level) # print('visibility Level now:', level)
def _addNode(self, host): def _addNodes(self, hosts):
for host in hosts:
try:
self.log.info('Trying to connect to %s', host)
self._addNode(host)
except Exception as e:
self.log.error('error in addNode: %r', e)
QMessageBox.critical(self.parent(),
'Connecting to %s failed!' % host, str(e))
def _addNode(self, host):
# create client # create client
node = QSECNode(host, self.log, parent=self) node = QSECNode(host, self.log, parent=self)
nodename = node.nodename nodename = node.nodename
@ -185,12 +233,32 @@ class MainWindow(QMainWindow):
self.tab.addTab(nodeWidget, node.equipmentId) self.tab.addTab(nodeWidget, node.equipmentId)
self._nodeWidgets[nodename] = nodeWidget self._nodeWidgets[nodename] = nodeWidget
self.tab.setCurrentWidget(nodeWidget) self.tab.setCurrentWidget(nodeWidget)
# add to recent nodes
settings = QSettings()
recent = settings.value('recent', [])
if host in recent:
recent.remove(host)
recent.insert(0, host)
settings.setValue('recent', recent)
self.recentNodesChanged.emit()
return nodename return nodename
def on_actionClear_triggered(self):
"""clears recent SECNode menu"""
settings = QSettings()
settings.remove('recent')
self.recentNodesChanged.emit()
def _handleTabClose(self, index): def _handleTabClose(self, index):
node = self.tab.widget(index).getSecNode() try:
# disconnect node from all events node = self.tab.widget(index).getSecNode()
self._nodes.pop(node.nodename) # disconnect node from all events
self._nodes.pop(node.nodename)
self.log.debug("Closing tab with node %s" % node.nodename)
except AttributeError:
# Closing the greeter
self.log.debug("Greeter Tab closed")
self.tab.removeTab(index) self.tab.removeTab(index)
def _rebuildAdvanced(self, advanced): def _rebuildAdvanced(self, advanced):

View File

@ -32,7 +32,7 @@ try:
from PyQt5 import uic from PyQt5 import uic
from PyQt5.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QSize, QPointF, \ from PyQt5.QtCore import Qt, QObject, pyqtSignal, pyqtSlot, QSize, QPointF, \
QRectF, QPoint, QByteArray, QEvent, QMimeData QRectF, QPoint, QByteArray, QEvent, QMimeData, QSettings
from PyQt5.QtGui import QFont, QTextCursor, QFontMetrics, QColor, QBrush, \ from PyQt5.QtGui import QFont, QTextCursor, QFontMetrics, QColor, QBrush, \
QPainter, QPolygonF, QPen, QIcon, QStandardItemModel, QStandardItem, \ QPainter, QPolygonF, QPen, QIcon, QStandardItemModel, QStandardItem, \
QPalette, QCursor, QDrag, QMouseEvent, QPixmap QPalette, QCursor, QDrag, QMouseEvent, QPixmap

File diff suppressed because it is too large Load Diff

224
frappy/gui/ui/greeter.ui Normal file
View File

@ -0,0 +1,224 @@
<?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>691</width>
<height>621</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout" rowstretch="2,5,2" columnstretch="2,6,2">
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Frappy&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<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:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Welcome to Frappy's graphical SECoP client&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="margin">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1" alignment="Qt::AlignBottom">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Recent SECNodes</string>
</property>
</widget>
</item>
<item row="0" column="2" alignment="Qt::AlignRight">
<widget class="QToolButton" name="ClearButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Clear all recent connections&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../resources/frappy-gui.qrc">
<normaloff>:/icons/trash</normaloff>:/icons/trash</iconset>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QPushButton" name="connectRecentButton">
<property name="text">
<string>Connect to selected</string>
</property>
<property name="icon">
<iconset resource="../../../resources/frappy-gui.qrc">
<normaloff>:/icons/connect</normaloff>:/icons/connect</iconset>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QListWidget" name="recentNodes">
<property name="toolTip">
<string>Select one or more recent Nodes to Connect to</string>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<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 alignment="Qt::AlignBottom">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Connect to SECNode</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="0,0">
<item row="0" column="1">
<widget class="QPushButton" name="AddSECNodeButton">
<property name="text">
<string>Connect</string>
</property>
<property name="icon">
<iconset resource="../../../resources/frappy-gui.qrc">
<normaloff>:/icons/connect</normaloff>:/icons/connect</iconset>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLineEdit" name="secnodeEdit">
<property name="placeholderText">
<string>localhost:10767</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="1">
<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="1">
<spacer name="verticalSpacer_3">
<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="1" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>recentNodes</tabstop>
<tabstop>connectRecentButton</tabstop>
<tabstop>secnodeEdit</tabstop>
<tabstop>AddSECNodeButton</tabstop>
<tabstop>ClearButton</tabstop>
</tabstops>
<resources>
<include location="../../../resources/frappy-gui.qrc"/>
</resources>
<connections>
<connection>
<sender>secnodeEdit</sender>
<signal>returnPressed()</signal>
<receiver>AddSECNodeButton</receiver>
<slot>animateClick()</slot>
<hints>
<hint type="sourcelabel">
<x>261</x>
<y>456</y>
</hint>
<hint type="destinationlabel">
<x>462</x>
<y>456</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -32,7 +32,6 @@ uipath = path.dirname(__file__)
def loadUi(widget, uiname, subdir='ui'): def loadUi(widget, uiname, subdir='ui'):
uic.loadUi(path.join(uipath, subdir, uiname), widget) uic.loadUi(path.join(uipath, subdir, uiname), widget)
class Value: class Value:
def __init__(self, value, timestamp=None, readerror=None): def __init__(self, value, timestamp=None, readerror=None):
self.value = value self.value = value

View File

@ -11,6 +11,7 @@
</qresource> </qresource>
<qresource prefix="icons"> <qresource prefix="icons">
<file alias="stop">icons/cross-circle.png</file> <file alias="stop">icons/cross-circle.png</file>
<file alias="trash">icons/bin.png</file>
<file alias="plot-add">icons/system-monitor--plus.png</file> <file alias="plot-add">icons/system-monitor--plus.png</file>
<file alias="submit">icons/arrow-turn-180.png</file> <file alias="submit">icons/arrow-turn-180.png</file>
<file alias="connect">icons/plug--plus.png</file> <file alias="connect">icons/plug--plus.png</file>

BIN
resources/icons/bin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B