Event Display Support
Offline Documentation: [Offline Category] [FAQ] [Howto] [Reference] [Manual] |
Offline Documentation |
This article is part of the Offline Documentation |
Other pages... |
Offline Category |
See also... |
Normally nuwa.py runs in batch mode but there are two methods for supporting an interactive user-controlled loop. They are provided either by Quanjing or an "External Loop" Python module.
Quanjing
Quanjing is a Daya Bay specific version of LHCb's Panoramix. It provides a flexible GUI that specified entirely in XML files. It has native support for visualizing the geometry and implements a "next event" button. It is 3D based on OpenInventor and requires OpenScientist. It does not (any longer) integrate with ROOT TCanvas.
To run in this mode one does:
nuwa.py -V [...usual arguments...]
It is tricky to mix other display mechanisms (eg TCanvas) with Quanjing. Read on for a simpler, but more basic support.
External Loop
There is support built in to nuwa.py for delegating the main loop to a Python module, called here an External Loop Module (ELM). This internal loop can run the GUI loop for some event display or it can be used to loop over data files directly while having NuWa services available.
This is done via the "-E" option with an argument naming the module:
nuwa.py -E External.Loop [...usual arguments...]
There are some examples in the ExternalLoop package. For example:
nuwa.py -E ExternalLoop.Gui [...]
provides an example of a "next event" button and drawing on a TCanvas in a separate window.
Here is a real example:
nuwa.py -E EventLooper.Gui -m EventLooper.PlotAlg --raw-load=both --no-history -n 10 ~/daq.NoTag.0006291.Physics.SAB-AD2.SFO-1._0001.data
This example creates a Tkinter GUI with a "next" button. Clicking on it runs the algorithm chain. In this example, it runs the alg defined in PlotAlg which will pop up a TCanvas (just showing a random gausian dist). You can also click the "Hist" button to launch another TCanvas that runs from the context of the Gui itself.
Writing an External Loop Module
A few rules must be followed to write your own ELM:
- Main entry
- Your module must implement a "
start(app)
" method.
The "app" is an instance of an AppMgr object, the same thing passed in to a Python algorithm. When the "start(app)
" method exists Gaudi shuts down and nuwa.py exits.
- Data navigation
- Use "
app.run(1)
" to advance to the next execution cycle ("event")
For other things "app"
can do, look into the definition of AppMgr in the GaudiPython.Bindings module.
- Honor command line
- the module should honor the general nuwa.py command line. In particular the total number of execution cycles requested should be honored. This can be accessed with:
from DybPython import nuwa nexecs = nuwa.opts.executions
- TES Access
- Access the event store via
evt = app.evtsvc() ho = evt['/Event/....']
The Problem With Loops
When one writes an event display one must be careful about "main loops". In particular any GUI controls and any display must run in its/their own main loop. Otherwise they would appear to freeze when Gaudi is processing an execution cycle.
And, don't look just to threads to solve the problem. Most code is not thread safe so care must be taken to not access data that wasn't created in ones own thread.
The GUI
If you implement an ELM that provides a GUI you must take steps if you want it to remain interactive while Gaudi is processing. The current example in EventLooper.Gui will freeze while Gaudi is processing the algorithms.
To avoid this the GUI must run in its own process or thread. And to do that one must take care to only share thread safe objects, eg via a shared Python Queue.
As an example, consider test_gui_sync.py and test_gui_async.py. The first provides a synchronous GUI. When you click "stack" you start a long running procedure. Until it returns the GUI can not process refreshes. Try to resize the GUI window or cover/expose the window and not it does not respond. The latter solves this by executing long running procedures in their own thread with a pair of queues to safely handle message passing between the threads.
In this latter case, it is possible to make the use of queues and threads almost invisible to the higher layer programmer by using the async.AsyncMethod to wrap the real object. Any of the real method calls are transformed into the exact same calls against async.AsyncMethod except for an additional "callable" objects passed as their first argument. The caller must provide a callable that does something useful with the return value of the real object. Then, the caller must provision a way to periodically check the queue for return objects and call them. In the example, this checking is done every 100 ms using the Tkinter "after()" method.
This is not particularly elegant as one only has one layer of callback callables. If one wants to do multiple things in response to an asynchronous return there is no way to easily do that. Also, it is awkward to manually drain the return queue periodically. A more proper solution is provided by Twisted Python with its defered and reactor objects.
ROOT TCanvas
It is possible to create a ROOT TCanvas either from the ELM or from a Python Algorithm. For doing simple "live updating" plots or displays, it is recommended to use an Algorithm. Be aware that ROOT cares about the names given to TCanvas objects. Two displays should use unique names for their canvases or take care to share the actual TCanvas object.
An example of such an algorithm is in EventLooper.PlotAlg
Using the test_gui_async.py and test_gui_sync.py examples described above, you see that ROOT is able to maintain its own event loop and needs no special consideration. (How is this done?)
Offline Software Documentation: [Offline Categories] [FAQ] [Offline Manual Category] |