.. _custom-widgets: ================== Custom Widgets ================== There are several approaches to developing and customizing a GUI with Taurus. The easiest approach (which may not even require programming in python) is to :ref:`create a Taurus GUI using the TaurusGUI framework` , populating it with panels created from existing Taurus or 3rd party widgets and attaching models to provide functionality. This is the most common (and recommended) approach, considering that many Taurus widgets can be further configured from the GUI itself and these configurations can be automatically restored, allowing for a level of customization that is sufficient for many applications. Sometimes however, one may need to customize the widgets at a lower level (e.g., for accessing properties that are not accessible via the GUI, or for grouping several existing widgets together, etc). This can be done by using the Taurus widgets just as one would use any other Qt widget (either using the :ref:`Qt Designer` or in a purely programmatic way). Finally, in some cases neither Taurus nor other third party modules provide the required functionality, and a new widget needs to be created. If such widget requires to interact with a control system or other data sources supported via :ref:`Taurus model objects`, the recommended approach is to create a widget that inherits both from a :class:`QWidget` (or a `QWidget`-derived class) and from the :class:`taurus.qt.qtgui.base.TaurusBaseComponent` mixin class (or one of its derived mixin classes from :mod:`taurus.qt.qtgui.base`). These Taurus mixin classes provide several APIs that are expected from Taurus widgets, such as: - model support API - configuration API - logger API - formatter API For this reason, this is sometimes informally called "*Taurus-ifying* a pure Qt class". The following is a simple example of creating a Taurus "power-meter" widget that displays the value of its attached attribute model as a bar (like e.g. in an equalizer). For this we are going to compose a :class:`QProgressBar` with a :class:`taurus.qt.qtgui.base.TaurusBaseComponent` mixin class: .. literalinclude:: ./examples/powermeter.py :language: python :linenos: :emphasize-lines: 6, 10-11, 19-24 As you can see, the mixin class provides all the taurus fucntionality regarding setting and subscribing to models, and all one needs to do is to implement the ``handleEvent`` method that will be called whenever the attached taurus model is updated. .. note:: if you create a generic enough widget which could be useful for other people, consider contributing it to Taurus, either to be included directly in the official taurus module or to be distributed as a :ref:`Taurus plugin`. .. tip:: we recommend to try to use the highest level approach compatible with your requirements, and limit the customization to the smallest possible portion of code. For example: consider that you need a GUI that includes a "virtual gamepad" widget to control a robot arm. Since such "gamepad" is not provided by Taurus, we recommend that you implement *only* the "gamepad" widget (maybe using the Designer to put together several :class:`QPushButtons` within a :class:`TaurusWidget`) in a custom module and then use that widget within a panel in a TaurusGUI (as opposed to implementing the whole GUI with the Designer). In this way you improve the re-usability of your widget *and* you profit from the built-in mechanisms of the Taurus GUIs such as handling of perspectives, saving-restoring of settings, etc .. _multi-model: Multi-model support: model-composer ----------------------------------- Before Taurus TEP20_ (implemented in Taurus 5.1) :class:`taurus.qt.qtgui.base.TaurusBaseComponent` and its derived classes only provided support for a single model to be associated with the QWidget / QObject. Because of this, many taurus widgets that required to be attached to more than one model had to implement the multi-model support in their own specific (and sometimes inconsistent) ways. With the introduction of TEP20_, the taurus base classes support multiple models. As an example, consider the following modification of the above "PowerMeter" class adding support for a second model consisting on an attribute that provides a color name that controls the background color of the bar: .. literalinclude:: ./examples/powermeter2.py :language: python :linenos: :emphasize-lines: 13-14,25,27-28,39 The relevant differences of the PowerMeter2 class with respect to the previous single-model version have been highlighted in the above code snippet: essentially one just needs to define the supported model keys in the ``.modelKeys`` class method and then handle the different possible sources of the events received in ``handleEvent``. Note that the first key in ``modelKeys`` is to be used as the default when not explicitly passed to the model API methods. The multi-model API also facilitates the implementation of widgets that operate on lists of models, by using the special constant ``MLIST`` defined in :mod:`taurus.qt.qtgui.base` and also accessible as ``TaurusBaseComponent.MLIST``. For example the following code implements a very simple widget that logs events received from an arbitrary list of attributes: .. literalinclude:: ./examples/eventlogger.py :language: python :linenos: The multi-model API treats the ``MLIST`` in a special way: when calling ``setModel`` with ``key=MLIST``, the ``model`` argument is expected to be a *sequence* of model names; new model keys are automatically added to the widget's ``modelList`` attribute and the corresponding models are attached using those keys. The new keys are of the form ``(MLIST, i)`` where ``i`` is the index of the corresponding model name in the model sequence. The new models can be accessed individually with the standard multi-model API using the generated model keys. Another typical pattern that can be implemented with the ``MLIST`` support is the model delegates container, where the widget does not handle the events by itself but instead it dynamically creates other taurus subwidgets (e.g. when the model is set) and then delegates the handling of events to those subwidgets (similar to what :class:`taurus.qt.qtgui.panel.TaurusForm` does). The following example shows a simplistic implementation of a form widget that shows the model name and its value for each model attached to it: .. literalinclude:: ./examples/simpleform.py :language: python :linenos: Note that, contrary to previous examples, this form does not re-implement the ``handleEvent`` method (i.e. it ignores the events from its models) but instead it calls ``setModel`` on its subwidgets, letting them handle their respective models' events. .. _TEP20: http://taurus-scada.org/tep/?TEP20.md