********************
Developing the Scene
********************
As it is an important part of the Karabo GUI, we expect that the Scene will
require new widgets/elements and other changes as time goes by. The following
should be considered guidelines for accomplishing that in a way which doesn't
cause undue pain to existing users of the GUI.
Basic Architecture
==================
The scene code is generally split into two parts: model and view. The model
code lives in ``karabo.common.scenemodel`` and is responsible for the
representation of scene data and reading/writing those data to/from files. It
is built using the `Traits library `_, which
you should familiarize yourself with if you plan to work on the scene. The view
code for the scene lives in ``karabogui.sceneview``. It is built using Qt and
makes use of the model objects.
.. note::
This separation of model and view has an important benefit: You don't *need*
a GUI to work with scenes. Having a well defined and **independent** data
model means that power users can script the creation/modification of scene
files.
Data Model
==========
The job of ``karabo.common.scenemodel`` is to describe the data of a scene.
GUI code does not belong here. For that, see `The View`_ below.
Scene File Versioning
---------------------
Every scene file, starting with version 2, contains a ``krb:version`` attribute
in the root SVG element which gives the version of the file. If the attribute
is missing, a file is assumed to be version 1.
The general philosophy of versioning in the scene file format is that old data
must *always* be readable. If a file is then modified and saved, it will be
written using the *latest version* of the file format. To accomplish this, the
scene data model code will necessarily accumulate reader code over time, but
will only ever know how to write out the current version of the file format.
The Scene File Format
---------------------
Version 1 of the scene file format is described in detail here:
:ref:`scene-file-version-one`. In general, the file format is SVG and
individual elements in a scene file are recognized by their corresponding
reader functions in the Karabo library.
Version 2 of the scene file format is basically the same as version 1, but
some elements have minor changes which are incompatible with the old reader
implementations. These changes should be in agreement with the guidelines
enumerated below.
Version 3 (and later) does not yet exist at the time of this writing. The
condition for it to exist would be if an existing element (or elements) need(s)
a new reader. As soon as the format of any element changes enough that it can't
be handled cleanly by the current reader, then a new reader (for that element)
and new file version is required.
Making Changes to the Scene File format
---------------------------------------
If you wish to add new data to the scene file format, or change the format of
data which is already there, you should take note of the following:
* If adding a new element, create a class which inherits ``BaseSceneObjectData``
(a *model class*).
* Create a reader function for the class. Register the reader with the
``register_scene_reader`` decorator. Make sure you pass
``version=`` (where ```` is the value of
``SCENE_FILE_VERSION``) to the decorator. Don't pass ``SCENE_FILE_VERSION``
because this value will change over time and you want to pin your reader to
a single version.
* Create a writer function for the class. Register the writer with the
``register_scene_writer`` decorator.
* **Add unit tests which cover all the new code that you added**. Try to cover
edge cases that you can think of.
* Starting Karabo 2.10, the scene reader and writer functions would only
expect one parameter, which is the XML element (``element``). The reader and
writer entrypoint function (``read_func``/``write_func``) is not passed
anymore. The functions ``read_element`` and ``write_element`` can be used
if there is a need to handle children elements.
* If *lightly* modifying an existing model class, you can make small changes to
the reader function
* **Leave the** ``SCENE_FILE_VERSION`` **constant alone**.
* Make whatever changes are necessary to the model class.
* Update the other reader function(s) for the model if needed
* Update unit tests carefully.
* If modifying an existing model class in a way which requires a new reader
function:
* Increment the ``SCENE_FILE_VERSION`` constant first.
* Make whatever changes are necessary to the model class.
* Create a **new reader function** and register it with a version equal to the
new value of ``SCENE_FILE_VERSION``. Remember not to use
``SCENE_FILE_VERSION`` here. Use its value.
* Update the old reader function(s), **but only if NOT doing so would cause
an exception when instantiating the model class**.
* The overall aim is to construct the latest version of the model class from
*any* version of the file data.
* Update unit tests carefully.
.. note::
Removing data from the file format is always safe. Old files which contain the
data will continue to be readable, because the reader can simply ignore the
data.
.. note::
Similarly, adding new widgets to the file format is also safe, as long as the
addition is orthogonal to existing data in the format. As of version 2.2(-ish)
the ``UnknownWidgetDataModel`` catches new widgets which do not have a reader
registered.
Unit Tests
----------
The scene model code, by virtue of being independent from the view code, has
very extensive test coverage. You should strive to maintain this when making
changes. It is intended as a defensive measure against introducing breaking
changes to users. Unfortunately, it's not automatic, and it requires a bit of
discipline on the part of developers working on the scene.
**This is very important**. Good unit test coverage of the scene file model
code is the main defense against user hostile bugs encountered when loading
scenes.
The View
========
The job of the subpackage ``karabogui.sceneview`` is to create a visual
representation of the data in scene model objects *and* give a way to
manipulate that data.
Adding a New Widget
-------------------
If you haven't added the data for your widget to the scene model yet, you
should first do that before proceeding with the view portion. Once your new
widget has a data model class associated with it, you can make it appear in the
scene by doing the following:
* Create a ``BaseBindingController`` class (or classes) which will be shown in
the scene.
* Make sure your controller class has a ``model`` trait which is an ``Instance``
of whatever your scene model class is.
* Register your controller class with the ``register_binding_controller``
decorator.
* Add unit tests for your controller class.
* Test in the GUI.
.. note::
A Developer's Checklist is documented in :ref:`gui-widget-checklist`
.. note::
If your new scene object **does NOT** need to interact with device properties
you should take a look at ``karabogui.sceneview.widget``. Adding things to
the scene view isn't *always* dealing with properties.
Adding a New Shape
------------------
Creating a new shape ``karabogui.sceneview.shapes`` is a bit easier, due to
the fact that shapes are not maintaining backwards compatibility with other
parts of the GUI code base. That said, you should still begin by creating a
data model class for your shape.
* Create a ``BaseShape`` class which will be shown in the scene
* Import your shape and model classes in ``karabogui.sceneview.builder``
and add them to the ``_SHAPE_CLASSES`` dictionary.
* Test in the GUI