|
|
(10 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
− | == Currently ==
| + | <big>'''Note:''' We are migrating this content to the slicer.org domain - <font color="orange">The newer page is [https://www.slicer.org/wiki/Slicer3:EventBroker here]</font></big> |
− | The basic idea of the EventBroker is that currently we have a lot of this kind of code in GUIs:
| |
− | | |
− | node->AddObserver(vtkCommand::ModifiedEvent, callbackCommand)
| |
− | | |
− | == Problems ==
| |
− | | |
− | The problems with this:
| |
− | | |
− | * node 'owns' the observer, but the callbackCommand is opaque so it doesn't know anything about what will happen when the event is invoked
| |
− | | |
− | * the GUI needs to explicitly remove the observer before it is destroyed
| |
− | | |
− | * node is not introspectable; you cannot get a list of observers on the node
| |
− | | |
− | * there's no easy way to know what side effects will happen for any Set call (either a priori or experimentally).
| |
− | | |
− | * there's no way to collapse events or disable them
| |
− | | |
− | == Goals for Solution == | |
− | | |
− | So the EventBroker would be a singleton, perhaps owned by the MRML Scene that would look something like:
| |
− | | |
− | broker->AddObservation(node, vtkCommand::ModifiedEvent, this, callbackCommand);
| |
− | | |
− | The broker would do the following: | |
− | | |
− | * add DeleteEvent observers to both node and this so it can remove the observer automatically if either side is destroyed
| |
− | | |
− | * keep an introspectable list of all observers it knows about
| |
− | | |
− | * have an option to keep a log of all event invocations for debugging and performance analysis
| |
− | | |
− | * have an option to turn off all event invocations
| |
− | | |
− | * have an option to queue all event invocations and invoke them later
| |
− | | |
− | * have methods to collapse redundant events in the queue
| |
− | | |
− | * perhaps have method to pass event invocations from a processing thread to the main GUI thread?
| |
− | | |
− | * the callbackCommand could be avoided if the vtkObject had a virtual method like this:
| |
− | | |
− | virtual void HandleEvent( vtkObject *caller, unsigned long event, void *clientData, void *callData );
| |
− | | |
− | * vtkEventBroker can have a class static GetInstance() method returning pointer to the global broker (like the way vtkSlicerApplication is done).
| |
− | | |
− | Additional possible extensions:
| |
− | | |
− | * rather than maintaining a distinct queue, the broker could queue events into the GUI event queue
| |
− | | |
− | * the event queue could be protected by a mutex lock so that multiple threads can access the MRML scene in parallel but only the GUI thread talks to the display
| |
− | | |
− | * add a timer to log the amount of time taken to process each event
| |
− | | |
− | ==References==
| |
− | | |
− | * [http://java.sun.com/products/jms/javadoc-102a/index.html Java Message Service (JMS) API]
| |
− | * [http://en.wikipedia.org/wiki/Observer_pattern Wikipedia definition of Observer Pattern]
| |
− | * [http://xlobject.sourceforge.net/ A C++ implementation]
| |
− | * [http://sigslot.sourceforge.net/ Another C++ implementation]
| |
− | * [http://doc.trolltech.com/4.3/signalsandslots.html The Qt implementation]
| |
− | | |
− | ==Dependency Graphs==
| |
− | | |
− | EventBroker code should have the option to put out log files that are compatible with [http://www.graphviz.org graphviz] .dot file format. This can be rendered with a variety of programs, or even pasted directly in the wiki: | |
− | | |
− | === Simple Dependency ===
| |
− | <graphviz border='frame' format='svg'>
| |
− | digraph G {
| |
− | vtkImageViewer -> vtkImageEllipsoidSource[ label = ModifiedEvent ] | |
− | ;}
| |
− | </graphviz> | |
− | | |
− | === Subset of Slicer Dependencies ===
| |
− | <graphviz border='frame' format='svg'>
| |
− | strict digraph G {
| |
− | vtkMRMLModelDisplayNode -> vtkMRMLModelDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLScalarVolumeDisplayNode -> vtkMRMLScalarVolumeDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLLabelMapVolumeDisplayNode -> vtkMRMLLabelMapVolumeDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLDiffusionWeightedVolumeDisplayNode -> vtkMRMLDiffusionWeightedVolumeDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLDiffusionTensorVolumeDisplayNode -> vtkMRMLDiffusionTensorVolumeDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLFiberBundleLineDisplayNode -> vtkMRMLFiberBundleLineDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLFiberBundleTubeDisplayNode -> vtkMRMLFiberBundleTubeDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLFiberBundleGlyphDisplayNode -> vtkMRMLFiberBundleGlyphDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLCameraNode -> vtkOpenGLCamera [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLUnstructuredGridDisplayNode -> vtkMRMLUnstructuredGridDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLCameraNode -> vtkOpenGLCamera [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkMRMLModelDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLCameraNode -> vtkOpenGLCamera [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelNode -> vtkPolyData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelNode -> vtkPolyData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkMRMLModelDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkImageData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelNode -> vtkPolyData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkMRMLModelDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkImageData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelNode -> vtkPolyData [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkMRMLModelDisplayNode [ label = "ModifiedEvent" ]
| |
− | ; vtkMRMLModelDisplayNode -> vtkImageData [ label = "ModifiedEvent" ]
| |
− | ;}
| |
− | </graphviz> | |
− | | |
− | == Issues ==
| |
− | | |
− | * how to work with the vtkObserverManager?
| |