# -*- coding: utf-8 -*-
"""
Simple basic GUI for the registermaster.
This GUI allows users to interact with the registermaster, send MQTT commands, and receive updates.
The class inherits from a base instrument GUI class and implements specific functionality for the registermaster.
Created on Mon Jan 3 09:19:17 2022
@author: C. Jung
TODO:
Bug: no bugs known, but in work.....
"""
import sys
import qdarkstyle
# import numpy as np
import os
import pathlib
import qtawesome as qta
from PyQt5 import QtWidgets
from pylab_ml.gui.instruments.base_instrument import Gui as Guibase
from pylab_ml.gui.instruments.base_instrument import load_ui
from pylab_ml.misc.registermaster import RegisterMaster
from ate_common.logger import LogLevel
# from pylab_ml.common.common import color
__author__ = "Zlin526F"
__credits__ = ["Zlin526F"]
__email__ = "Zlin526F@github"
__version__ = "0.0.3a"
[docs]
class Gui(Guibase):
"""Simple basic GUI for the Registermaster.
Inherited from base_instrument
status
"""
_states = {
"connect": ["connect", "color: rgb(0, 255, 0)"], # overwrite the definitions in base_instrument
"disconnect": ["disconnect", "color: rgb(255, 0, 0)"],
"on": ["running", "color: rgb(0, 255, 0)"],
"off": ["waiting for start...", "color: palette(HighlightedText)"],
}
[docs]
def __init__(self, parent=None, name="reg", parentwindow=None, channel=None):
"""
Initialize the GUI for the registermaster.
Parameters
----------
parent: QWidget, optional
The parent widget of the GUI.
name: str, optional
The name of the GUI instance.
parentwindow: QWidget, optional
The parent window of the GUI.
channel: int, optional
The channel number for the registermaster.
"""
super().__init__(grandparent=parent, name=name, parentwindow=parentwindow)
self.myframe = load_ui(self.gui.myframe, __file__)
# bgcolor = self.gui.palette().color(QtGui.QPalette.Background).name() # getRgb()
self.myadjustUI()
self.gui.closeEvent = self.close
self.parent = parent
self.mqtt_initlist = ["filename"]
self._filename = ""
self.regs = None
self.show_regs = []
[docs]
def myadjustUI(self):
"""Adjust the GUI elements for the registermaster."""
# set icons:
self.gui.runToolBar.setVisible(False)
# self.add_menuicon('onoff') # add existing icon and connection from the base-instrument
# create and connect Menue entries
self.gui.actionexcel = self.add_menue("open registermaster in excel", self.openexcel, False)
self.gui.actionshownames = self.add_menue("show names", self.showregdoc, True, True, True)
self.gui.actionshowbits = self.add_menue("show bits", self.showregdoc, True, True, True)
self.gui.actionshowval = self.add_menue("show value", self.showregdoc, True, True, True)
self.gui.actionshowdir = self.add_menue("show dir", self.showregdoc, True, True)
self.gui.actionshowres = self.add_menue("show reset values", self.showregdoc, True, True)
# set connection fom Gui-object to functions:
# self.myframe.Dsamples.valueChanged.connect(self.rangeSamples)
# self.myframe.Echname.editingFinished.connect(self.editchname)
self.mqttConnectWidgets() # connect all widget which start with MQTT_
geometry = self.gui.geometry()
wh = self.myframe.geometry()
self.gui.setWindowTitle(f' {os.path.basename(__file__).split(".")[0]} (Version: {__version__})')
self.myframe.setGeometry(geometry.x(), geometry.y(), wh.width() + 100, wh.height() + 250)
[docs]
def regstatus(self, msg):
"""Update the status label in the GUI with the given message."""
self.myframe.Lstatus.setText(msg)
self.myframe.Lstatus.setStyleSheet("color: rgb(255, 0, 0)")
# ======================================================
# attributes which connect to an extern call (mqtt-command)
[docs]
def mqttreceive(self, instName, msg):
"""
Common MQTT receive messages, get raw MQTT data for more information.
Parameters
----------
instName: str
The name of the instrument instance.
msg: dict
The MQTT message received.
"""
if super().mqttreceive(instName, msg):
return
# self.logger.log_message(LogLevel.Debug(), f"{instName}.mqttreceive: {msg} ") # e.q. msg={'type': 'set', 'cmd': 'TEST7.read', 'payload': 1608}
# if you change a widget with the mqtt command, you have to use the widget.blockSignals(True/False)
if "cmd" in tuple(msg.keys()) and "payload" in tuple(msg.keys()):
mycmd = msg["cmd"].split(".")
if len(mycmd) == 1 and mycmd[0].split('(')[0] in ["readreg", "writereg"]:
mycmd = mycmd[0].split('(')
adr = int(mycmd[1][:-1], 16)
print(f"read/writereg: {adr}, {mycmd}")
self.memoryframe(int(mycmd[1][:-1],16), msg["payload"])
elif len(mycmd) == 2 and mycmd[1] in ["read", "write"]: # special handling from mqtt commands
self.regstatus(" ")
if mycmd[0] in dir(self.regs):
self.registerframe(self.regs.__dict__[mycmd[0]], msg["payload"])
else:
self.regstatus(f"{mycmd[0]} not found in the registermaster")
else:
self.logger.log_message(LogLevel.Warning(), f"{instName} {msg['cmd']} not found -> do nothing")
[docs]
def registerframe(self, register, value):
"""
Update the register frame in the GUI with the given register and value.
Parameters
----------
register: Register
The register object to update.
value: int
The value to set for the register.
"""
# self.logger.log_message(LogLevel.Debug(), f" call registerframe with {register} {value}")
register._cache = value
myregister = None
myregisters = self.show_regs.copy()
hl = ""
hle = ""
for regs in myregisters:
if not regs.Bhold.isChecked() and regs.name != register._name:
regs.hide()
self.myframe.QVBregisters.removeWidget(regs)
regs.destroy()
regs.deleteLater()
self.show_regs.remove(regs)
elif regs.name == register._name:
myregister = regs
if myregister is None:
self.show_regs.append(QtWidgets.QGroupBox(self.myframe))
myregister = self.show_regs[-1]
load_ui(myregister, os.path.join(os.path.dirname(__file__), "register.ui")) # QBregister
myregister.Bhold.setIcon(qta.icon("fa5s.thumbtack", color="white", scale_factor=1.0, color_active="orange"))
myregister.Bhold.clicked.connect(lambda: self.toggleBhold(myregister.Bhold))
# self.myframe.Name.mousePressEvent = self.readreg(register._name)
self.myframe.QVBregisters.addWidget(myregister)
elif value != myregister.value:
hl = '<font color="orange">'
hle = "</font>"
self.showdoc(self.gui.actionshownames, myregister.Lbitname)
self.showdoc(self.gui.actionshowbits, myregister.Lbit)
self.showdoc(self.gui.actionshowval, myregister.Lvalue)
self.showdoc(self.gui.actionshowdir, myregister.Lrw)
self.showdoc(self.gui.actionshowres, myregister.Lres)
# <font color="orange"></font>
myregister.name = register._name
myregister.value = value
table = register.value_table
width = register._len_slices()
myregister.Name.setTitle(f"{register._name} = {value}(d) = 0x{value:0{width//4}x} = b{value:0{width}b}")
bits = bitname = " "
rw = "dir"
val = "val"
res = "res"
for index in table.index:
length = len(table["name"][index]) if len(table["name"][index]) > len(index) else len(index)
bits = f"{bits}{index:>{length}} "
bitname = f"{bitname}{table['name'][index]:>{length}} "
rw = f"{rw}{table['dir'][index]:>{length}} " if table["dir"][index] is not None else f"{rw} {'x':>{length-1}} "
val = f"{val}{table['dec'][index]:>{length}} "
# val = f"{val}{' ':>{length}}{hl}{table['dec'][index]}{hle} "
res = f"{res}{table['res'][index]:>{length}} "
myregister.Lbit.setText(bits)
myregister.Lbitname.setText(bitname)
myregister.Lrw.setText(rw)
myregister.Lvalue.setText(val)
myregister.Lres.setText(res)
myregister.Lbitname.setToolTip(register.__doc__)
# _len_slices()
[docs]
def memoryframe(self, adr, value):
"""
Update the memory frame in the GUI with the given address and value.
Parameters
----------
adr: int
The address of the memory location.
value: int
The value to set for the memory location.
"""
for name in self.regs.__dict__: # search for address, perhaps it is a register
register = self.regs.__dict__[name]
if hasattr(register, "_cpuaddr"):
if register._cpuaddr == adr:
print(f"found {name}")
self.regstatus(" ")
self.registerframe(self.regs.__dict__[name], value)
return
# it is memory
print('it is memory....')
# ======================================================================================
# definitions from complex mqtt commands, if you need only a connection between the mqtt-command and a widget, start the name of your widget with MQTT_
@property
def filename(self):
"""Get the current filename."""
return self._filename
@filename.setter
def filename(self, val):
self._filename = val
val = val.split("/")
index = 0
shortpath = ""
for name in val:
if name == "proot":
project = val[index + 1]
version = val[index + 2]
workarea = val[index + 4]
shortpath = f": {project}/{version}/{workarea}/.../"
index += 1
self.id = f": {shortpath}{val[-1]}"
self.gui.actionexcel.setEnabled(True)
try:
self.regs = RegisterMaster(filename=self._filename)
self.regs.init()
self.regs.reset_regs(default=0)
self.regstatus(" ")
except Exception as ex:
self.regstatus(f"could not load {self._filename}")
msg = f"registermaster.filename something is wrong :-( : {ex}"
self.logger.log_message(LogLevel.Error(), msg)
print(msg)
return
self.logger.log_message(LogLevel.Info(), f"registermaster.filename set to {self._filename}")
self.myframe.Lfilename.setText(self._filename)
print(f"registermaster.filename set to {self._filename}")
[docs]
def showdoc(self, action, label):
"""Show or hide the documentation for a given label based on the state of the corresponding action."""
if not action.isChecked():
label.hide()
else:
label.show()
# end extern calling attributes
# ======================================================================================
#
# connected GUI-function to buttons or menues
#
[docs]
def close(self, event=None):
"""Handle the close event for the GUI by publishing an 'off' command and then calling the base class's close method."""
super().close()
[docs]
def openexcel(self):
"""Open the current Excel file."""
filename = pathlib.Path(self.filename)
msg = f"start excel {filename}"
self.logger.log_message(LogLevel.Info(), msg)
os.system(msg)
[docs]
def toggleBhold(self, hold):
"""Toggle the state of the hold button and update its icon accordingly."""
if hold.isChecked():
hold.setIcon(qta.icon("fa5s.thumbtack", color="orange", scale_factor=1.0, color_active="white"))
else:
hold.setIcon(qta.icon("fa5s.thumbtack", color="white", scale_factor=1.0, color_active="orange"))
[docs]
def showregdoc(self):
"""Show or hide the documentation for all registers based on the state of the corresponding actions."""
if not hasattr(self, "show_regs"):
return
for myregister in self.show_regs:
self.showdoc(self.gui.actionshownames, myregister.Lbitname)
self.showdoc(self.gui.actionshowbits, myregister.Lbit)
self.showdoc(self.gui.actionshowval, myregister.Lvalue)
self.showdoc(self.gui.actionshowdir, myregister.Lrw)
self.showdoc(self.gui.actionshowres, myregister.Lres)
myregister.show()
[docs]
def readreg(self, regname):
"""Read the value of a register."""
self.logger.log_message(LogLevel.Error(), f" readreg {regname} ")
self.publish(f"{regname}.read")
if __name__ == "__main__":
# from pylab_ml.common.mqtt_client import mqtt_init
# broker = 127.0.0.1'
# message_client = 'ate/DT1604092/matrix'
# mqttclient = mqtt_init() # prepare mqtt
# mqttclient.init(None, message_client=message_client.split('/')[0]) # mqtt client connect to broker
# mqttclient.init()
app = QtWidgets.QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
window = Gui(app)
app.exec_()
# window.scope.anim.event_source.stop()