frappy/frappy/gui/moduleoverview.py
Markus Zolliker da15df076a fetched mlz version
- before some chamges in the gerrit pipline

Change-Id: I33eb2d75f83345a7039d0fb709e66defefb1c3e0
2023-05-02 15:25:11 +02:00

211 lines
7.6 KiB
Python

# -*- coding: utf-8 -*-
# *****************************************************************************
# Copyright (c) 2015-2023 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 Zaft <a.zaft@fz-juelich.de>
#
# *****************************************************************************
from frappy.datatypes import StatusType
from frappy.gui.qt import QIcon, Qt, QTreeWidget, QTreeWidgetItem, pyqtSignal
class ParamItem(QTreeWidgetItem):
def __init__(self, node, module, param):
super().__init__()
self.module = module
self.param = param
self.setText(0, param)
class ModuleItem(QTreeWidgetItem):
display = {'status': 0, 'value': 1, 'target': 2, 'status/text': 3}
def __init__(self, node, module):
super().__init__()
ModuleItem._loadicons()
self.module = module
self.param = None
#self._expanded = False
#self._params = {}
parameters = node.getParameters(module)
self._hasTarget = 'target' in parameters
#if self._hasTarget:
# self.setFlags(self.flags() | Qt.ItemIsEditable)
if 'status' not in parameters:
self.setIcon(self.display['status'], ModuleItem.icons['clear'])
self.setText(0, self.module)
# initial read
cache = node.queryCache(self.module)
for param, val in cache.items():
self.valueChanged(param, val)
self.params = []
for param in parameters:
self.params.append(ParamItem(node, module, param))
@classmethod
def _loadicons(cls):
if hasattr(cls, 'icons'):
return
cls.icons = {
'disabled': QIcon(':/leds/gray'),
'idle': QIcon(':/leds/green'),
'warn':QIcon(':/leds/orange'),
'error': QIcon(':/leds/red'),
'busy': QIcon(':/leds/yellow'),
'unknown': QIcon(':/leds/unknown'),
'clear': QIcon(':/leds/clear'),
}
@classmethod
def statusIcon(cls, statuscode):
if statuscode == 0:
return ModuleItem.icons['disabled']
if statuscode in range(100, 200):
return ModuleItem.icons['idle']
if statuscode in range(200, 300):
return ModuleItem.icons['warn']
if statuscode in range(300, 400):
return ModuleItem.icons['busy']
if statuscode in range(400, 500):
return ModuleItem.icons['error']
return ModuleItem.icons['clear']
def valueChanged(self, parameter, value):
if parameter not in self.display:
return
if parameter == 'status':
if value.readerror:
self.setIcon(self.display[parameter], ModuleItem.statusIcon(StatusType.ERROR))
self.setText(self.display['status/text'], str(value.readerror))
else:
self.setIcon(self.display[parameter], ModuleItem.statusIcon(value.value[0].value))
self.setText(self.display['status/text'], value.value[1])
else:
self.setText(self.display[parameter], value.formatted())
def disconnected(self):
self.setIcon(self.display['status'], ModuleItem.icons['unknown'])
def setClearIcon(self):
self.setIcon(self.display['status'], ModuleItem.icons['clear'])
def hasTarget(self):
return self._hasTarget
def _rebuildAdvanced(self, advanced):
if advanced:
self.addChildren(self.params)
else:
for p in self.params:
self.removeChild(p)
class ModuleOverview(QTreeWidget):
# current module/param, prev module/param
itemChanged = pyqtSignal(str, str)
def __init__(self, node, parent=None):
super().__init__(parent)
self._node = node
self._modules = {}
#self.setHeaderHidden(True)
#self.setChildIndicatorPolicy(QTreeWidgetItem.DontShowIndicator)
self.setRootIsDecorated(False)
self.setExpandsOnDoubleClick(False)
self.setColumnCount(4)
header = self.headerItem()
header.setText(0, 'Name')
header.setText(1, 'Value')
header.setText(2, 'Target')
header.setText(3, 'Status')
for module in sorted(self._node.modules):
mod = ModuleItem(self._node, module)
self._modules[module] = mod
self.addTopLevelItem(mod)
self._resizeColumns()
self.itemExpanded.connect(self._resizeColumns)
self.itemCollapsed.connect(self._resizeColumns)
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
# self.customContextMenuRequested.connect(self._contextMenu)
self._node.newData.connect(self._updateValue)
self.currentItemChanged.connect(self.handleCurrentItemChanged)
#self.itemDoubleClicked.connect(self.handleDoubleClick)
# def handleDoubleClick(self, item, column):
# if item.hasTarget() and column == 2:
# self.editItem(item, column)
def mouseReleaseEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
if self.itemAt(event.pos()) is None:
self.clearTreeSelection()
return super().mouseReleaseEvent(event)
def handleCurrentItemChanged(self, current, previous):
self.itemChanged.emit(current.module, current.param or '')
def setToDisconnected(self):
for module in self._modules.values():
module.disconnected()
def setToReconnected(self):
"""set status after connection is reestablished.
If the node-description has changed during the reconnection, the nodewidget
this overview is a part of gets replaced. However, the reconnect-event
gets through before the descriptionChanged-event. So if a module is no
longer present we return early in order to avoid KeyErrors on node.modules
For the case of additional modules or changed module contents we do not care.
"""
nodemods = self._node.modules.keys()
for mname, module in self._modules.items():
if mname not in nodemods:
return # description changed and we will be replaced, return early
cache = self._node.queryCache(mname)
if not 'status' in cache:
module.setClearIcon()
continue
module.valueChanged('status', cache['status'])
def _updateValue(self, module, parameter, value):
self._modules[module].valueChanged(parameter, value)
def _rebuildAdvanced(self, advanced):
self.setRootIsDecorated(advanced)
for module in self._modules.values():
module._rebuildAdvanced(advanced)
self._resizeColumns()
def _resizeColumns(self):
for i in range(self.columnCount()):
self.resizeColumnToContents(i)
def clearTreeSelection(self):
selected = self.selectedItems()
if not selected:
return
self.clearSelection()
self.itemChanged.emit('', '')
self.last_was_clear = True