Closed mariocaptain closed 3 years ago
Hi, there is a "Door Output" widget in the Sardana repo that has a similar goal: https://github.com/sardana-org/sardana/blob/develop/src/sardana/taurus/qt/qtgui/extra_macroexecutor/dooroutput.py
The Door TANGO device has several attributes (e.g. output, info, warning, error, debug) of type DevString and format Spectrum. This widget (Qt.QPlainTextEdit) connects to the Door device, and based on which attribute is getting data from it appends it with different color
Yes, I think that that is the closest thing.
Other than that, implementing your own taurus-ified QPlainTextEdit
should not be hard at all. The following is a minimal example:
class MyWidget(Qt.QPlainTextEdit, TaurusBaseComponent):
def handleEvent(self, evt_src, evt_type, evt_value):
try:
self.appendPlainText(evt_value.rvalue)
except Exception as e:
print(e)
Have a look at this example for how to taurus-ify a Qt widget: https://gitlab.com/alba-synchrotron/controls-section/pythoncourse-intermediate/-/blob/master/exercises/cheat/taurusexercise2.py
Dear @guifrecuni and @cpascual ,
It's wonderful that you offer not only one but two approaches. After studying both, I see that while the Door Output example is very interesting to know, the taurus-ify approach would result in less code being written. And being taurus-ish is a plus too :)
So here is what I have done, almost successful. The taurus-ified class:
class VerboseConsole(Qt.QPlainTextEdit, TaurusBaseComponent):
def __init__(self, parent=None):
# call the parent class init
Qt.QPlainTextEdit.__init__(self, parent=None)
TaurusBaseComponent.__init__(self, "VerboseConsole")
def handleEvent(self, evt_src, evt_type, evt_value):
try:
self.appendPlainText(evt_value.rvalue)
except Exception as e:
self.appendPlainText("Exception:" + str(e))
In the device server that emits the verbose message:
verbose = attribute(dtype=str, access=AttrWriteType.READ)
self.set_change_event("verbose", True, False)
To test, in this device server I made a thread that runs continuously:
def update_loop(self):
while True:
self._verbose = "This is a test message 1"
self.push_change_event('verbose', self._verbose)
time.sleep(1)
self._verbose = "This is a test message 2"
self.push_change_event('verbose', self._verbose)
time.sleep(1)
Works like a charm, except that the output on the VerboseConsole is:
This is a test message 1
This is a test message 1
This is a test message 2
This is a test message 2
This seems like there are 2 events for each push. How should I filter these two events? Thanks
In general, if you see duplicated events, check:
The second situation may happen if e.g. the server is actually emitting the event when you change it and then again when you push it.
Another possibility for "2" is that either tango's own (server-side) polling mechanism or taurus polling mechanism (client-side polling) is active for this attribute.
Some things you can do to check:
evt_src, evt_type, evt_value
in your handle_event
and check if the evt_type changes:evt_value.time
, you can see if the timestamp for the two events is the same or notimport tango
import time
d = tango.DeviceProxy("your/device/name")
et = tango.EventType
cb = tango.utils.EventCallback()
for t in [et.CHANGE_EVENT, et.PERIODIC_EVENT, et.DATA_READY_EVENT]: # try with less/more types
d.subscribe_event("verbose", t, cb)
time.sleep(100)
Hi, here you have a complete working example. Run the server in a console and then the client in another:
(thanks @reszelaz and @guifrecuni for their hints on the self.set_change_event
usage)
import datetime
from tango.server import Device, command, attribute
class Dummy(Device):
"""
A dummy server that changes its "verbose" attr when the
push command is executed
"""
def init_device(self):
self._verbose = "---"
# declare that events will be pushed for the "verbose" attribute
self.set_change_event("verbose", True, False)
@attribute(dtype=str)
def verbose(self):
return self._verbose
@command()
def push(self):
self._verbose = datetime.datetime.now().isoformat()
self.push_change_event("verbose", self._verbose)
if __name__ == "__main__":
# register the server in the DB (only needed once)
import tango
dev_info = tango.DbDevInfo()
dev_info.server = "Dummy/test"
dev_info._class = "Dummy"
dev_info.name = "test/dummy/1"
db = tango.Database()
db.add_device(dev_info)
# run the server
Dummy.run_server()
import taurus
from taurus.qt.qtgui.application import TaurusApplication
from taurus.qt.qtgui.base import TaurusBaseComponent
from taurus.external.qt import Qt
class MyWidget(Qt.QPlainTextEdit, TaurusBaseComponent):
"""Minimal logging widget"""
def handleEvent(self, evt_src, evt_type, evt_value):
try:
self.appendPlainText(evt_value.rvalue)
except Exception as e:
print(e)
if __name__ == "__main__":
import sys
app = TaurusApplication(cmd_line_parser=None)
# launch the logging widget
w = MyWidget()
w.setModel("test/dummy/1/verbose")
w.show()
# call the push command of the device every 500ms
d = taurus.Device("test/dummy/1")
t = Qt.QTimer()
t.timeout.connect(d.push)
t.start(500)
sys.exit(app.exec_())
Dear @cpascual ,
Thanks so much for the time you put in writing this working example. I have learnt many things from it.
I run it and it works without the duplication of verbose messages. By studying it closely, I notice that the major difference between this example and my almost-working-code above (where events got triggered twice) is that in your "Minimal logging widget" you don't call the init functions of the 2 base classes.
So I remove those from my above code and it works as expected (i.e. no more duplication).
So the understanding here is that inheriting both classes that way results in what seems to me to be two identical event listeners. A good lesson learnt.
I have learnt a lot from the Taurus community in general and from you particularly. Thanks so much for the kind efforts, and please don't let the boat sink!
don't call the init functions of the 2 base classes.
Oh, right! That is it! The behaviour when calling the two parent classes has changed with the python and PyQt versions. In fact we have discussed it before somewhere (I forgot).
Some time ago (e.g. with python2.6, PyQt4), I recommended doing it exactly as you did (calling the 2 parents explicitly) but with PyQt5 and pyhton3 I tend to use super
instead.
Hi,
I am writing a device server (which will have just 1 device instance) for the business logic of my system.
This device server thus will control all the devices in the system via Device Proxies.
As this device server runs to operate the automation logic of the system, I want it to be able to send out verbose message to a Taurus Gui.
The approach I am thinking is to declare a verbose_str attribute in this server, and whenever I want the Taurus client to get the new verbose message, this sever will update this str variable and push a data ready event for the client to know and get the string.
My question is is there any taurus widget suited for this purpose, I mean some that simply associates itself to an attribute or the like and amends its content like a text editor? Or what is best suited for this?
P.S. Verbose messages with custom colors would be great, but not a must
Thanks!