Page MenuHome

Python API: callback_utils

Authored by Jacques Lucke (JacquesLucke) on Nov 22 2018, 5:53 PM.



Depends on:

The depsgraph_update_pre/post handlers are useful when one is monitoring specific properties in Blender.
To fully replace the behavior of the scene_update_post/pre handlers we need more though.

This patch proposes a new bpy_extras.callback_utils module with two new functions (whose name can still change).

  • set_interval(function, interval): This registers a callback that should be called every interval seconds. When the function returns True, it will run again in the next interval. Otherwise it will be unregistered automatically.
  • run_in_main_thread: Sometimes it is useful to run a separate thread in Blender (e.g. for a simple server or for Python debugging). Currently there is no reliable way to change any Blender data from a separate thread. Using this utility function, addon developers can register a callback that will be executed one time, when Blender is in the right state.

Internally this behavior is implemented using a new _always handler that should maybe not be part of the official API. The behavior of it can still be archieved by registering a function with zero interval. For simplicity I think it is a good idea to implement this functionality in Python. The always_handler function tries to be as fast as possible in the common case.

Diff Detail

rB Blender
callback_utils (branched from blender2.8)
Build Status
Buildable 2525
Build 2525: arc lint + arc unit

Event Timeline

I think we should at least have a depsgraph_update_pre / depsgraph_update_post before we add this, so always doesn't get abused for the purpose of detecting changes.

The code for that could be in the same place as scene_update_pre / scene_update_post was, just only running the callback if DEG_id_type_any_updated() returns true.

ok, will add those two handlers in a separate diff.

On top of the always handler we can also implement another callback type. It could be used like this:


internally it does this:

callbacks = queue.Queue()

def run_in_main_thread(function):

def execute_queued_functions(_):
    while not callbacks.empty():
        function = execution_queue.get_nowait()

This could be used to encourage a best practice for this use case.
The execute_queued_functions callback can also only be added when the first callback is queued to eliminate any overhead when no addon uses it.

  • set_interval and run_in_main_thread

We could make it so that the always handler is not part of the "official" api.
Instead people should use these two new utility functions.

Jacques Lucke (JacquesLucke) retitled this revision from Python API: to Python API: callback_utils.Nov 23 2018, 11:38 AM
Jacques Lucke (JacquesLucke) edited the summary of this revision. (Show Details)

I think this is a good solution to help addons do the right thing.

We could make it so that the always handler is not part of the "official" api.
Instead people should use these two new utility functions.

Agreed. Is it automatically hidden in the API docs by naming it _always with the underscore? The description could mention that this is for internal use, and that callback_utils should be used instead.

It looks good to me, but wait for review from @Campbell Barton (campbellbarton).


Blenders -> Blender's

This revision is now accepted and ready to land.Nov 23 2018, 12:30 PM
  • update api docs
  • improve api docs

This example could show using depsgraph.updates to iterate over the list of changed datablocks, and depsgraph.id_type_updated() to figure out if there were any changes for a specific datablock type (mainly to detect potential removal of datablocks).

  • another example for monitoring properties
Campbell Barton (campbellbarton) requested changes to this revision.Nov 26 2018, 1:34 AM

Would rather not have always handler, it encourages bad practice and will be used by addons even if we define it as internal-use-only.

If someone really wants they can define a timer, see: D3990 that executed immediately, at least in this case the API encourages the developer to consider a time when executing the next event is needed (instead of all the time).

This revision now requires changes to proceed.Nov 26 2018, 1:34 AM