frappy/frappy/gui/moduleoverview.py
Markus Zolliker 9bf187758b central point for status codes
Put status code definitions into frappy.datatypes.StatusType.
frappy.datatypes is anyway imported in servers and clients,
so this is a better place than frappy.modules.

Change-Id: I81dfc8a066f598fbd20854ed1a13b937b7facc8c
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30703
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
2023-04-21 16:16:40 +02:00

177 lines
6.0 KiB
Python

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):
for mname, module in self._modules.items():
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