Difference between revisions of "Slicer3:EventBroker"
(Replacing page with '<big>'''Note:''' We are migrating this content to the slicer.org domain - <font color="orange">The newer page is [http://www.slicer.org/slicerWiki/index.php/Slicer3:EventBroker...') |
m (→Issues) |
||
(4 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
− | + | == Currently == | |
+ | The basic idea of the EventBroker is to avoid the following kind of code that shows up in GUIs and other classes that depend on events generated by other classes: | ||
+ | |||
+ | 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 (all data and methods are private) | ||
+ | |||
+ | * 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 | ||
+ | |||
+ | == EventBroker Solution == | ||
+ | |||
+ | The EventBroker is a singleton, available as: | ||
+ | |||
+ | vtkEventBroker *broker = vtkEventBroker::GetInstance(); | ||
+ | |||
+ | with the broker, you can make a call like the following: | ||
+ | |||
+ | broker->AddObservation(node, vtkCommand::ModifiedEvent, this, callbackCommand); | ||
+ | |||
+ | where ''node'' is the subject of the observation. | ||
+ | |||
+ | The broker does the following: | ||
+ | |||
+ | * adds DeleteEvent observers to both ''node'' and ''this'' so it can remove the observation automatically when either side is destroyed | ||
+ | |||
+ | * keeps an introspectable list of all observers it knows about | ||
+ | |||
+ | * has an option to keep a log of all event invocations for debugging and performance analysis | ||
+ | |||
+ | * has an option to turn off all event invocations | ||
+ | |||
+ | * has an ''asynchronous'' option to queue all event invocations and invoke them later (off by default) | ||
+ | |||
+ | * has option to collapse redundant events in the queue | ||
+ | |||
+ | The broker is also available for use from tcl. For convenience in slicer there's a global variable for the broker so you can use a code snippet like: | ||
+ | |||
+ | $::slicer3::Broker AddObservation $node ModifiedEvent {puts "the node was modified"} | ||
+ | |||
+ | Another nice feature is that you can use a numerical value for the event argument, which works around the limitation in VTK's native wrapping of event types. So, if you look in the header for a widget or object and know the custom event number, you can use it as an argument: | ||
+ | |||
+ | $::slicer3::Broker AddObservation $node 1 {puts "got an AnyEvent notification from the node"} | ||
+ | |||
+ | Future options include: | ||
+ | |||
+ | * add a timer to log the amount of time taken to process each event | ||
+ | |||
+ | * specify that some observations must be handled synchronously (e.g. for progress events that shouldn't be collapsed) | ||
+ | |||
+ | See the file [http://www.na-mic.org/Slicer/Documentation/Slicer3/html/classvtkEventBroker.html Libs/MRML/vtkEventBroker.h] for more information on logging options and introspection features. | ||
+ | |||
+ | ==References== | ||
+ | |||
+ | The following reference provided insight on requirements and design precedents. | ||
+ | |||
+ | * [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 if the appropriate extension were installed: | ||
+ | |||
+ | === Simple Dependency === | ||
+ | <pre> | ||
+ | <graphviz border='frame' format='svg'> | ||
+ | digraph G { | ||
+ | vtkImageViewer -> vtkImageEllipsoidSource[ label = ModifiedEvent ];} | ||
+ | </graphviz> | ||
+ | </pre> | ||
+ | |||
+ | === Subset of Slicer Dependencies === | ||
+ | <pre> | ||
+ | <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> | ||
+ | </pre> | ||
+ | |||
+ | == Issues == | ||
+ | |||
+ | * how to work with the vtkObserverManager? | ||
+ | * Discussion of Jan 27 2012: | ||
+ | <pre> [...] the problem wasn't that the Event Broker was used instead of going through vtkObserverManager, but that the vtkObject::AddObserver() was used with the callback command of the observer manager (that was later deleted which made the client data of the callback dirty when the event was fired). | ||
+ | |||
+ | We should eventually review the entire "observation" mechanism, there are currently 3 ways of observing vtkObjects: | ||
+ | - via the vtkObserverManager API (please everybody use that one for now) | ||
+ | - via the EventBroker directly | ||
+ | - via vtkObject::AddObserver directly | ||
+ | |||
+ | All three methods are valid but inconsistent with each other. In addition, it is still not user friendly to observe MRML nodes (using static functions as callback... how to synchronize a MRML node with a VTK widget in a displayable manager ? how to observe with Python...). | ||
+ | </pre> | ||
+ | * Bugs related to observers: | ||
+ | ** Volume Rendering ([http://na-mic.org/Mantis/view.php?id=1572 1572], [http://na-mic.org/Mantis/view.php?id=1744 1744]) | ||
+ | *** -> Use AddObserver with vtkObserverManager callback that is deleted before an observed event is fired, crash when trying to dereference the deleted observer. | ||
+ | ** Python ([http://na-mic.org/Mantis/view.php?id=1656 1656]) | ||
+ | *** -> PyObject of the observer is deleted before an observed event is fired. crash in the callback | ||
+ | * Current design notes | ||
+ | ** For logics, vtk[SetAnd]ObserveMRMLNode[Events]Macro can only observe MRML nodes (see vtkMRMLAbstractLogic::MRMLNodesCallback) | ||
+ | ** For QObjects listening to vtkObjects, destruction must unlink observations (modules must call setMRMLScene(0) in destructor? ) |
Latest revision as of 17:31, 29 February 2012
Home < Slicer3:EventBrokerContents
Currently
The basic idea of the EventBroker is to avoid the following kind of code that shows up in GUIs and other classes that depend on events generated by other classes:
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 (all data and methods are private)
- 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
EventBroker Solution
The EventBroker is a singleton, available as:
vtkEventBroker *broker = vtkEventBroker::GetInstance();
with the broker, you can make a call like the following:
broker->AddObservation(node, vtkCommand::ModifiedEvent, this, callbackCommand);
where node is the subject of the observation.
The broker does the following:
- adds DeleteEvent observers to both node and this so it can remove the observation automatically when either side is destroyed
- keeps an introspectable list of all observers it knows about
- has an option to keep a log of all event invocations for debugging and performance analysis
- has an option to turn off all event invocations
- has an asynchronous option to queue all event invocations and invoke them later (off by default)
- has option to collapse redundant events in the queue
The broker is also available for use from tcl. For convenience in slicer there's a global variable for the broker so you can use a code snippet like:
$::slicer3::Broker AddObservation $node ModifiedEvent {puts "the node was modified"}
Another nice feature is that you can use a numerical value for the event argument, which works around the limitation in VTK's native wrapping of event types. So, if you look in the header for a widget or object and know the custom event number, you can use it as an argument:
$::slicer3::Broker AddObservation $node 1 {puts "got an AnyEvent notification from the node"}
Future options include:
- add a timer to log the amount of time taken to process each event
- specify that some observations must be handled synchronously (e.g. for progress events that shouldn't be collapsed)
See the file Libs/MRML/vtkEventBroker.h for more information on logging options and introspection features.
References
The following reference provided insight on requirements and design precedents.
- Java Message Service (JMS) API
- Wikipedia definition of Observer Pattern
- A C++ implementation
- Another C++ implementation
- The Qt implementation
Dependency Graphs
EventBroker code should have the option to put out log files that are compatible with graphviz .dot file format. This can be rendered with a variety of programs, or even pasted directly in the wiki if the appropriate extension were installed:
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?
- Discussion of Jan 27 2012:
[...] the problem wasn't that the Event Broker was used instead of going through vtkObserverManager, but that the vtkObject::AddObserver() was used with the callback command of the observer manager (that was later deleted which made the client data of the callback dirty when the event was fired). We should eventually review the entire "observation" mechanism, there are currently 3 ways of observing vtkObjects: - via the vtkObserverManager API (please everybody use that one for now) - via the EventBroker directly - via vtkObject::AddObserver directly All three methods are valid but inconsistent with each other. In addition, it is still not user friendly to observe MRML nodes (using static functions as callback... how to synchronize a MRML node with a VTK widget in a displayable manager ? how to observe with Python...).
- Bugs related to observers:
- Current design notes
- For logics, vtk[SetAnd]ObserveMRMLNode[Events]Macro can only observe MRML nodes (see vtkMRMLAbstractLogic::MRMLNodesCallback)
- For QObjects listening to vtkObjects, destruction must unlink observations (modules must call setMRMLScene(0) in destructor? )