7
$\begingroup$

My add-on needs to do some clean-up action before it is ended. This is done in the unregister() function in __init__.py when the user unchecks the add-on in Blender's preferences dialog and works fine in this case. But if the user just closes Blender the unregister() function seems not to be called. How could I achieve this or force Blender to do some other action before closing? There is no handler list for the event "Blender is closing" in bpy.app.handlers as far as I can see.

$\endgroup$
  • $\begingroup$ There is indeed no handler for that event, nor is unregister called (only on actively unticking an add-on). This leaves you behind with one more option: Python's native deconstructor __del__. It might be called as Blender terminates. $\endgroup$ – CodeManX Jan 13 '15 at 10:23
6
$\begingroup$

Yes, this is possible, You can use Python's atexit module.

This can be used exactly as you would writing regular Python code (no Blender specifics are required.).

eg:

import atexit

def my_cleanup_code():
    print("cleaning up!")

atexit.register(my_cleanup_code)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Still works with current blender 2.82. No external dependencies needed, it's stable and works exactly as advertised. $\endgroup$ – squarespiral Apr 2 at 17:47
  • $\begingroup$ Note: The Blender objects are already deleted at the time the function is executed. If you have some property (of a custom class) that is not automatically deleted by Blender (in my case it was something like self.batch_cached = batch_for_shader(...)), make sure you call atexit.register(...) with a function using directly the reference to the to property (here: self.batch_cached), since something like bpy.types.Object.my_custom_object is no longer available. $\endgroup$ – Mr.Epic Fail Aug 1 at 11:29
5
$\begingroup$

No, the destructors are neither called when Blender terminates. But I think I have a workaround:

I have a timer running ("Operator Modal Timer" from the template list), and its cancel() function is called by Blender when terminating! To distinguish the various callers of the cancel() function I count the elements in the stack trace, given by traceback.format_stack(). I figured out the following:

  • 1 element in the traceback list: cancel() is called because Blender is terminated.
  • 3 elements in the traceback list: cancel() is called because a *.blend file is loaded.
  • 5 elements in the traceback list: cancel() is called because my "stop timer" menu button is pressed by the user.
  • 8 elements in the traceback list: cancel() is called because the add-on is unchecked in Blender's preferences dialog.

I am aware that counting on these values is very unsafe because they may change with each Blender version or even with the state Blender is in. But important for me is only the first case, and it seems to work for me that way - at least for the time being ...

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Great find! One downside: F8 is not possible while a modal operator is running, you will have to end it before a script reload. Regarding counting the stacktrace entries: you can catch the .blend load event with an app handler, a click on "stop timer" in your own operator and addon unticking with code in unregister() - so actually no need to count at all - or am I overlooking something? $\endgroup$ – CodeManX Jan 13 '15 at 15:31
  • $\begingroup$ You are not quite right: Yes, the cases with 3, 5 or 8 elements in the traceback list do not matter (I have the other event handlers you propose already). But my handler for "Blender closing" must only be called in the case with 1 element in the list, not in the other cases; therefore I have to check this - and hope this value will not change with future versions. With the F8 problem I have to live; my "stop timer" button allows to stop the timer. $\endgroup$ – frisee Jan 16 '15 at 11:43
  • $\begingroup$ I see, but if you can detect all 3 other events, can't you infer that all other events must be "Blender shutdown"? $\endgroup$ – CodeManX Jan 16 '15 at 11:47
0
$\begingroup$

I think the better option is use some handler event...

http://www.blender.org/api/blender_python_api_2_73_2/bpy.app.handlers.html?highlight=bpy.app.handlers#bpy.app.handlers.version_update

maybe you can "load" a new blender file before blender is closing.. then you can use the

bpy.app.handlers.load_post
on loading a new blend file (after)

bpy.app.handlers.load_pre
on loading a new blend file (before) 

there is the moment to do something before or after blender change to another blender file :D

best

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.