Create custom Taurus widgets
Use taurus.qt.qtgui.base.TaurusBaseComponent (or one of its derived
mixin classes) when a custom Qt widget needs to interact directly with Taurus
models. For background on the widget model API and the available customization
levels, see Extending Taurus widgets.
Create a single-model widget
The following example creates a simple power-meter widget by combining a
QProgressBar with
taurus.qt.qtgui.base.TaurusBaseComponent:
1from taurus.external.qt import Qt
2from taurus.qt.qtgui.application import TaurusApplication
3from taurus.qt.qtgui.base import TaurusBaseComponent
4
5
6class PowerMeter(Qt.QProgressBar, TaurusBaseComponent):
7 """A Taurus-ified QProgressBar"""
8
9 # setFormat() defined by both TaurusBaseComponent and QProgressBar. Rename.
10 setFormat = TaurusBaseComponent.setFormat
11 setBarFormat = Qt.QProgressBar.setFormat
12
13 def __init__(self, parent=None, value_range=(0, 100)):
14 super(PowerMeter, self).__init__(parent=parent)
15 self.setOrientation(Qt.Qt.Vertical)
16 self.setRange(*value_range)
17 self.setTextVisible(False)
18
19 def handleEvent(self, evt_src, evt_type, evt_value):
20 """reimplemented from TaurusBaseComponent"""
21 try:
22 self.setValue(int(evt_value.rvalue.m))
23 except Exception as e:
24 self.info("Skipping event. Reason: %s", e)
25
26
27if __name__ == "__main__":
28 import sys
29
30 app = TaurusApplication()
31 w = PowerMeter()
32 w.setModel("eval:Q(60+20*rand())")
33 w.show()
34 sys.exit(app.exec_())
The mixin provides the Taurus functionality for setting and subscribing to a
model. The widget implements handleEvent, which is called whenever the
attached model is updated.
If the widget is generic enough to be useful to others, consider contributing it to Taurus or distributing it as a Taurus plugin.
Add support for multiple models
To attach several models to one widget, define the supported keys in
modelKeys and handle events according to their source. This version of the
power-meter adds a second model that supplies the background color:
1from taurus.external.qt import Qt
2from taurus.qt.qtgui.application import TaurusApplication
3from taurus.qt.qtgui.base import TaurusBaseComponent
4
5
6class PowerMeter2(Qt.QProgressBar, TaurusBaseComponent):
7 """A Taurus-ified QProgressBar with separate models for value and color"""
8
9 # setFormat() defined by both TaurusBaseComponent and QProgressBar. Rename.
10 setFormat = TaurusBaseComponent.setFormat
11 setBarFormat = Qt.QProgressBar.setFormat
12
13 modelKeys = ["power", "color"] # support 2 models (default key is "power")
14 _template = "QProgressBar::chunk {background: %s}" # stylesheet template
15
16 def __init__(self, parent=None, value_range=(0, 100)):
17 super(PowerMeter2, self).__init__(parent=parent)
18 self.setOrientation(Qt.Qt.Vertical)
19 self.setRange(*value_range)
20 self.setTextVisible(False)
21
22 def handleEvent(self, evt_src, evt_type, evt_value):
23 """reimplemented from TaurusBaseComponent"""
24 try:
25 if evt_src is self.getModelObj(key="power"):
26 self.setValue(int(evt_value.rvalue.m))
27 elif evt_src is self.getModelObj(key="color"):
28 self.setStyleSheet(self._template % evt_value.rvalue)
29 except Exception as e:
30 self.info("Skipping event. Reason: %s", e)
31
32
33if __name__ == "__main__":
34 import sys
35
36 app = TaurusApplication()
37 w = PowerMeter2()
38 w.setModel("eval:Q(60+20*rand())") # implicit use of key="power"
39 w.setModel("eval:['green','red','blue'][randint(3)]", key="color")
40 w.show()
41 sys.exit(app.exec_())
The first key returned by modelKeys is used as the default when a model API
method is called without an explicit key.
Handle a list of models
Use the special MLIST key when the widget must operate on an arbitrary list
of models. The following widget logs events received from a list of attributes:
1from datetime import datetime
2
3from taurus.core import TaurusEventType
4from taurus.external.qt import Qt
5from taurus.qt.qtgui.application import TaurusApplication
6from taurus.qt.qtgui.base import TaurusBaseComponent
7
8
9class EventLogger(Qt.QTextEdit, TaurusBaseComponent):
10 """A taurus-ified QTextEdit widget that logs events received
11 from an arbitrary list of taurus attributes
12 """
13
14 modelKeys = [TaurusBaseComponent.MLIST]
15
16 def __init__(self, parent=None):
17 super(EventLogger, self).__init__(parent=parent)
18 self.setMinimumWidth(800)
19
20 def handleEvent(self, evt_src, evt_type, evt_value):
21 """reimplemented from TaurusBaseComponent"""
22 line = "{}\t[{}]\t{}".format(
23 datetime.now(),
24 TaurusEventType.whatis(evt_type),
25 evt_src.getFullName(),
26 )
27 self.append(line)
28
29
30if __name__ == "__main__":
31 import sys
32
33 app = TaurusApplication()
34 w = EventLogger()
35 w.setModel(["eval:123", "tango:sys/tg_test/1/short_scalar", "eval:rand()"])
36 w.show()
37 sys.exit(app.exec_())
Calling setModel with key=MLIST expects a sequence of model names.
Taurus adds generated keys of the form (MLIST, i) to the widget’s model
list, and the corresponding models can then be accessed using the normal
multi-model API.
Delegate models to child widgets
A container widget does not need to process all model events itself. It can create Taurus child widgets and assign the models to them instead. The following simplified form creates a label and a value widget for every model:
1from taurus.external.qt import Qt
2from taurus.qt.qtgui.application import TaurusApplication
3from taurus.qt.qtgui.base import MLIST, TaurusBaseComponent
4from taurus.qt.qtgui.display import TaurusLabel
5
6
7class SimpleForm(Qt.QWidget, TaurusBaseComponent):
8 """A simple taurus form using the model list support from
9 TaurusBaseComponent.
10 """
11
12 modelKeys = [MLIST]
13
14 def __init__(self, parent=None):
15 super(SimpleForm, self).__init__(parent=parent)
16 self.setLayout(Qt.QFormLayout(self))
17
18 def setModel(self, model, *, key=MLIST):
19 """reimplemented from TaurusBaseComponent"""
20 TaurusBaseComponent.setModel(self, model, key=key)
21 _ly = self.layout()
22
23 if key is MLIST: # (re)create all rows
24 # remove existing rows
25 while _ly.rowCount():
26 _ly.removeRow(0)
27 # create new rows
28 for i, name in enumerate(model):
29 simple_name = self.getModelObj(key=(MLIST, i)).getSimpleName()
30 value_label = TaurusLabel()
31 value_label.setModel(name)
32 _ly.addRow(simple_name, value_label)
33 else: # update a single existing row
34 _, row = key # key must be of the form (MLIST, <i>)
35 name_label = _ly.itemAt(row, _ly.ItemRole.LabelRole).widget()
36 value_label = _ly.itemAt(row, _ly.ItemRole.FieldRole).widget()
37 name_label.setText(self.getModelObj(key=key).getSimpleName())
38 value_label.setModel(self.getModelName(key=key))
39
40
41if __name__ == "__main__":
42 import sys
43
44 app = TaurusApplication()
45 w = SimpleForm()
46 w.setModel(
47 [
48 "eval:foo=123;foo",
49 "eval:randint(99)",
50 "sys/tg_test/1/short_scalar",
51 "eval:randint(99)",
52 ]
53 )
54 w.show()
55 sys.exit(app.exec_())
In this example the container does not reimplement handleEvent. Instead,
it calls setModel on its child widgets and lets each child handle the events
from its own model.