From BlenderWiki

Jump to: navigation, search

Introduction to Notifiers and Listeners

Notifiers and listeners have been introduced in Blender 2.5 as a way of cleanly keeping Blender (in particular the UI) up to date. After manipulating data, the rest of Blender needs to be notified and updated to account for these changes, so the proper redraws, statistics updates, caches, etc. will be executed.


Motivation

In Blender 2.4, this was handled the opposite way. After changes, individual tools, functions, buttons would manually force redrawing the screen internally, with functions such as allqueue(REDRAWVIEW3D); to manually refresh the 3D View for example.

However this is not a system that is extensible. When adding a spacetype, tools have to be updated for this new spacetype, because there is no system for it to get notified on changes. Similarly python scripts cannot track data changes well, and the ones that do try this have to resort to polling.

As a solution, we can turn around this system and instead of tools queuing redraws, they can say which data they changed to notify areas, regions, preview renders, and anything interested in tracking data changes, and these can then decide themselves if they want to update.


How it works

Rather than guessing what else in Blender needs to be updated for each tool or property change, instead we just send a notifier to tell 'what happened'. These notifiers get put in a queue, which the various editors in Blender (3d view, graph editor etc) have listeners, which can then examine these notifications, and decide if they want to react to it or not.

It's important to note that notifiers are optional hints only - just because you send a notifier doesn't necessarily mean anything is going to happen. This is by design, it prevents superfluous redraws/updates/etc happening if they're not needed.

Notifier: A description of "what happened"
Listener: Checks for specific notifiers, and updates Blender's state accordingly

Notifier flow.png

Inside a Notifier

Notifier struct.png

Notifiers are quite simple, mainly containing categorisation information in varying levels of specific detail.

Sending a notifier usually consists of combining together a Category tag, Data tag, Subtype tag, and Action tag. Most notifiers don't use all of these at once, how specific a notifier needs to be is something that's dependent on individual circumstances. Category, Data and Subtype usually describe the type of thing that was changed, Action describes how it was changed. For example:

 WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, ima);

In this example, an NC_IMAGE Category notifier is being sent, with an Action of NA_EDITED (as opposed to NA_ADDED, NA_REMOVED, etc).

The full list of notifier types is available in source/blender/windowmanager/WM_types.h.

If there are no notifiers available that suit your purposes, you can add a new one, but if unsure, best to check with other developers.

The other info contained in a notifier is an optional 'reference' pointer to a datablock. The reference pointer is optional and not guaranteed to be valid, but it's there for comparison, to make better decisions about how to react to a notifier. In the above example, an Image Editor listener can use the attached 'ima' pointer to check if it's the image that's currently being displayed, and only bother updating if it is.

Usually, it's a good idea to not be too general, so that listeners don't react unnecessarily - it's generally good to have at least Category and Data, and perhaps an Action.


Sending Notifiers

Notifiers are usually sent by the RNA system, or directly, via operators or other functions. Within Blender you can use WM_event_add_notifier() to send a notifier from places like inside operators where there's access to context, eg:

 WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, ima);

Notifiers can also be sent automatically when RNA properties are updated. In the code that defines RNA properties (source/blender/makesrna/intern/rna_*.c), RNA_property_update() can be used to set notifiers associated with a property, eg:

 RNA_def_property_update(prop, NC_OBJECT|ND_TRANSFORM, "rna_Object_update");

Here, whenever the 'prop' property is updated, an NC_OBJECT Category notifier will be sent with ND_TRANSFORM data. As well as this, it's also calling another update function "rna_Object_update" to do non-notifier related things such as dependency graph updates.

Listeners

Listeners are often in the editor space definition files (space_*.c). Listeners can be on an Area level, or Region level - it's better to keep it specific to regions where possible, for efficiency. For example, the graph editor may need to redraw the main curve region on a change, but not the channels region.

 static void action_main_area_listener(ARegion *ar, wmNotifier *note)
   {    
       /* context changes */
       switch(note->category) {
               case NC_SCENE:
               switch(note->data) {
                       case ND_FRAME:
                       case ND_MARKERS:
                               ED_region_tag_redraw(ar);
                               break;
                       }
               break;
       }
   }

The example above is listening in the main region of the action editor. If it hears a ND_FRAME or ND_MARKERS notifier in the NC_SCENE category, it will redraw the region.

Potentially in the future, it could be possible to define Python listeners (as a replacement for the old scriptlinks), which can react when they hear certain notifiers. This is another reason why it's important for notifiers to describe "what happened" well, so that new future listeners can get accurate information to work with.

Hints

If an RNA property is sending a notifier, but Blender is not updating properly, often the correct solution is to edit the relevant listener to listen out for that notifier, rather than changing the original notifier.

Remember: notifiers are there not to force redraws or updates, but to notify what happened. It's the job of the listener to actually take action.


More Info