Module sverchok.core.handlers
Expand source code
import bpy
from bpy.app.handlers import persistent
import sverchok
from sverchok import old_nodes
from sverchok import data_structure
import sverchok.core.events as ev
import sverchok.core.tasks as ts
from sverchok.core.event_system import handle_event
from sverchok.core.socket_data import clear_all_socket_cache
from sverchok.ui import bgl_callback_nodeview, bgl_callback_3dview
from sverchok.utils.handle_blender_data import BlTrees
from sverchok.utils.sv_logging import catch_log_error, TextBufferHandler, sv_logger
import sverchok.settings as settings
_state = {'frame': None}
def sverchok_trees():
for ng in bpy.data.node_groups:
if ng.bl_idname == 'SverchCustomTreeType':
yield ng
def has_frame_changed(scene):
last_frame = _state['frame']
_state['frame'] = scene.frame_current
return not last_frame == scene.frame_current
#
# app.handlers.undo_post and app.handlers.undo_pre are necessary to help remove stale
# draw callbacks (bgl / gpu / blf). F.ex the rightlick menu item "attach viewer draw"
# will invoke a number of commands as one event, if you undo that event (ctrl+z) then
# there is never a point where the node can ask "am i connected to anything, do i need
# to stop drawing?". When the Undo event causes a node to be removed, its node.free function
# is not called. (maybe it should be...)
#
# If at any time the undo system is fixed and does call "node.free()" when a node is removed
# after ctrl+z. then these two functions and handlers are no longer needed.
@persistent
def sv_handler_undo_pre(scene):
from sverchok.core import undo_handler_node_count
for ng in sverchok_trees():
undo_handler_node_count['sv_groups'] += len(ng.nodes)
@persistent
def sv_handler_undo_post(scene):
# It also can be called during work of Blender operators - https://developer.blender.org/T89546
# this function appears to be hoisted into an environment that does not have the same locals()
# hence this dict must be imported. (jan 2019)
from sverchok.core import undo_handler_node_count
num_to_test_against = 0
for ng in sverchok_trees():
num_to_test_against += len(ng.nodes)
if undo_handler_node_count['sv_groups'] != num_to_test_against:
sv_logger.debug('looks like a node was removed, cleaning')
sv_clean(scene)
undo_handler_node_count['sv_groups'] = 0
handle_event(ev.UndoEvent())
@persistent
def sv_update_handler(scene):
"""
Update sverchok node groups on frame change events.
Jump from one frame to another: has_frame_changed=True, is_animation_playing=False
Scrubbing variant 1: has_frame_changed=True, is_animation_playing=True
Scrubbing variant 2(stop): has_frame_changed=True, is_animation_playing=False
Scrubbing variant 3: has_frame_changed=False, is_animation_playing=True
Scrubbing variant 4(stop): has_frame_changed=False, is_animation_playing=False
Playing animation: has_frame_changed=True, is_animation_playing=True
Playing animation(stop): has_frame_changed=False, is_animation_playing=False
"""
if bpy.context.screen is None: # rendering
is_playing = False # should be False to update UI
is_frame_changed = True
else:
is_playing = bpy.context.screen.is_animation_playing
is_frame_changed = has_frame_changed(scene)
# print(f"Frame changed: {is_frame_changed}, Animation is playing: {is_playing}")
for ng in sverchok_trees(): # Comparatively small overhead with 200 trees in a file
with catch_log_error():
ng.process_ani(is_frame_changed, is_playing)
@persistent
def sv_scene_change_handler(scene):
"""
On depsgraph update (pre)
"""
# When the Play Animation is on this trigger is executed once. Such event
# should be suppressed because it repeats animation trigger. When Play
# animation is on and user changes something in the scene this trigger is
# only called if frame rate is equal to maximum.
if bpy.context.screen.is_animation_playing:
return
# scene handler can be triggered even when new node or its property
# is changed what causes redundant updates. Such events are created first,
# so it's possible to check them in the tasks object.
# also be aware that updates generate scene events by their selves and
# they have mechanism to avoid them
if ts.tasks:
return
for ng in BlTrees().sv_main_trees:
ng.scene_update()
@persistent
def sv_clean(scene):
"""
Cleanup callbacks, clean dicts.
"""
bgl_callback_nodeview.callback_disable_all()
bgl_callback_3dview.callback_disable_all()
data_structure.sv_Vars = {}
data_structure.temp_handle = {}
@persistent
def sv_pre_load(scene):
"""
This method is called whenever new file is opening
THe update order is next:
1. pre_load handler
2. update methods of trees in a file
3. post_load handler
4. evaluate trees from main tree handler
"""
clear_all_socket_cache()
sv_clean(scene)
handle_event(ev.FileEvent())
@persistent
def sv_post_load(scene):
"""
Upgrade nodes, apply preferences and do an update.
THe update order is next:
1. pre_load handler
2. update methods of trees in a file
3. post_load handler
4. evaluate trees from main tree handler
post_load handler is also called when Blender is first ran
The method should initialize Sverchok parts which are required by loaded tree
"""
TextBufferHandler.add_to_main_logger()
from sverchok import node_tree, settings
# ensure current nodeview view scale / location parameters reflect users' system settings
node_tree.SverchCustomTree.update_gl_scale_info(None, "sv_post_load")
# register and mark old and dependent nodes
with catch_log_error():
if any(not n.is_registered_node_type() for ng in BlTrees().sv_trees for n in ng.nodes):
old_nodes.register_all()
old_nodes.mark_all()
with catch_log_error():
settings.apply_theme_if_necessary()
# when a file is opened as a startup file update method of its trees is not called (Blender inconsistency??)
for tree in BlTrees().sv_main_trees:
tree.update()
def set_frame_change(mode):
post = bpy.app.handlers.frame_change_post
pre = bpy.app.handlers.frame_change_pre
# remove all
if sv_update_handler in post:
post.remove(sv_update_handler)
if sv_update_handler in pre:
pre.remove(sv_update_handler)
# apply the right one
if mode == "POST":
post.append(sv_update_handler)
elif mode == "PRE":
pre.append(sv_update_handler)
def update_frame_change_mode():
from sverchok import settings
mode = settings.get_param("frame_change_mode", "POST")
set_frame_change(mode)
@persistent
def save_pre_handler(scene):
addon = bpy.context.preferences.addons.get(sverchok.__name__)
prefs = addon.preferences
if prefs.log_to_buffer_clean:
for handler in sv_logger.handlers:
if hasattr(handler, 'clear'):
handler.clear()
handler_dict = {
'undo_pre': sv_handler_undo_pre,
'undo_post': sv_handler_undo_post,
'load_pre': sv_pre_load,
'load_post': sv_post_load,
'depsgraph_update_pre': sv_scene_change_handler,
'save_pre': save_pre_handler,
}
@persistent
def call_user_functions_on_post_load_event(scene):
for function in data_structure.post_load_call.registered_functions:
function()
def app_handler_ops(append=None, remove=None):
""" append or remove multiple items to specific bpy.app.handlers """
(operation, handler_dict) = ('append', append) if append else ('remove', remove)
for handler_name, handler_function in handler_dict.items():
handler = getattr(bpy.app.handlers, handler_name)
getattr(handler, operation)(handler_function)
settings.set_frame_change = set_frame_change
def register():
app_handler_ops(append=handler_dict)
update_frame_change_mode()
bpy.app.handlers.load_post.append(call_user_functions_on_post_load_event)
def unregister():
app_handler_ops(remove=handler_dict)
set_frame_change(None)
bpy.app.handlers.load_post.remove(call_user_functions_on_post_load_event)
Functions
def app_handler_ops(append=None, remove=None)
-
append or remove multiple items to specific bpy.app.handlers
Expand source code
def app_handler_ops(append=None, remove=None): """ append or remove multiple items to specific bpy.app.handlers """ (operation, handler_dict) = ('append', append) if append else ('remove', remove) for handler_name, handler_function in handler_dict.items(): handler = getattr(bpy.app.handlers, handler_name) getattr(handler, operation)(handler_function)
def call_user_functions_on_post_load_event(scene)
-
Expand source code
@persistent def call_user_functions_on_post_load_event(scene): for function in data_structure.post_load_call.registered_functions: function()
def has_frame_changed(scene)
-
Expand source code
def has_frame_changed(scene): last_frame = _state['frame'] _state['frame'] = scene.frame_current return not last_frame == scene.frame_current
def register()
-
Expand source code
def register(): app_handler_ops(append=handler_dict) update_frame_change_mode() bpy.app.handlers.load_post.append(call_user_functions_on_post_load_event)
def save_pre_handler(scene)
-
Expand source code
@persistent def save_pre_handler(scene): addon = bpy.context.preferences.addons.get(sverchok.__name__) prefs = addon.preferences if prefs.log_to_buffer_clean: for handler in sv_logger.handlers: if hasattr(handler, 'clear'): handler.clear()
def set_frame_change(mode)
-
Expand source code
def set_frame_change(mode): post = bpy.app.handlers.frame_change_post pre = bpy.app.handlers.frame_change_pre # remove all if sv_update_handler in post: post.remove(sv_update_handler) if sv_update_handler in pre: pre.remove(sv_update_handler) # apply the right one if mode == "POST": post.append(sv_update_handler) elif mode == "PRE": pre.append(sv_update_handler)
def sv_clean(scene)
-
Cleanup callbacks, clean dicts.
Expand source code
@persistent def sv_clean(scene): """ Cleanup callbacks, clean dicts. """ bgl_callback_nodeview.callback_disable_all() bgl_callback_3dview.callback_disable_all() data_structure.sv_Vars = {} data_structure.temp_handle = {}
def sv_handler_undo_post(scene)
-
Expand source code
@persistent def sv_handler_undo_post(scene): # It also can be called during work of Blender operators - https://developer.blender.org/T89546 # this function appears to be hoisted into an environment that does not have the same locals() # hence this dict must be imported. (jan 2019) from sverchok.core import undo_handler_node_count num_to_test_against = 0 for ng in sverchok_trees(): num_to_test_against += len(ng.nodes) if undo_handler_node_count['sv_groups'] != num_to_test_against: sv_logger.debug('looks like a node was removed, cleaning') sv_clean(scene) undo_handler_node_count['sv_groups'] = 0 handle_event(ev.UndoEvent())
def sv_handler_undo_pre(scene)
-
Expand source code
@persistent def sv_handler_undo_pre(scene): from sverchok.core import undo_handler_node_count for ng in sverchok_trees(): undo_handler_node_count['sv_groups'] += len(ng.nodes)
def sv_post_load(scene)
-
Upgrade nodes, apply preferences and do an update. THe update order is next: 1. pre_load handler 2. update methods of trees in a file 3. post_load handler 4. evaluate trees from main tree handler post_load handler is also called when Blender is first ran The method should initialize Sverchok parts which are required by loaded tree
Expand source code
@persistent def sv_post_load(scene): """ Upgrade nodes, apply preferences and do an update. THe update order is next: 1. pre_load handler 2. update methods of trees in a file 3. post_load handler 4. evaluate trees from main tree handler post_load handler is also called when Blender is first ran The method should initialize Sverchok parts which are required by loaded tree """ TextBufferHandler.add_to_main_logger() from sverchok import node_tree, settings # ensure current nodeview view scale / location parameters reflect users' system settings node_tree.SverchCustomTree.update_gl_scale_info(None, "sv_post_load") # register and mark old and dependent nodes with catch_log_error(): if any(not n.is_registered_node_type() for ng in BlTrees().sv_trees for n in ng.nodes): old_nodes.register_all() old_nodes.mark_all() with catch_log_error(): settings.apply_theme_if_necessary() # when a file is opened as a startup file update method of its trees is not called (Blender inconsistency??) for tree in BlTrees().sv_main_trees: tree.update()
def sv_pre_load(scene)
-
This method is called whenever new file is opening THe update order is next: 1. pre_load handler 2. update methods of trees in a file 3. post_load handler 4. evaluate trees from main tree handler
Expand source code
@persistent def sv_pre_load(scene): """ This method is called whenever new file is opening THe update order is next: 1. pre_load handler 2. update methods of trees in a file 3. post_load handler 4. evaluate trees from main tree handler """ clear_all_socket_cache() sv_clean(scene) handle_event(ev.FileEvent())
def sv_scene_change_handler(scene)
-
On depsgraph update (pre)
Expand source code
@persistent def sv_scene_change_handler(scene): """ On depsgraph update (pre) """ # When the Play Animation is on this trigger is executed once. Such event # should be suppressed because it repeats animation trigger. When Play # animation is on and user changes something in the scene this trigger is # only called if frame rate is equal to maximum. if bpy.context.screen.is_animation_playing: return # scene handler can be triggered even when new node or its property # is changed what causes redundant updates. Such events are created first, # so it's possible to check them in the tasks object. # also be aware that updates generate scene events by their selves and # they have mechanism to avoid them if ts.tasks: return for ng in BlTrees().sv_main_trees: ng.scene_update()
def sv_update_handler(scene)
-
Update sverchok node groups on frame change events. Jump from one frame to another: has_frame_changed=True, is_animation_playing=False Scrubbing variant 1: has_frame_changed=True, is_animation_playing=True Scrubbing variant 2(stop): has_frame_changed=True, is_animation_playing=False Scrubbing variant 3: has_frame_changed=False, is_animation_playing=True Scrubbing variant 4(stop): has_frame_changed=False, is_animation_playing=False Playing animation: has_frame_changed=True, is_animation_playing=True Playing animation(stop): has_frame_changed=False, is_animation_playing=False
Expand source code
@persistent def sv_update_handler(scene): """ Update sverchok node groups on frame change events. Jump from one frame to another: has_frame_changed=True, is_animation_playing=False Scrubbing variant 1: has_frame_changed=True, is_animation_playing=True Scrubbing variant 2(stop): has_frame_changed=True, is_animation_playing=False Scrubbing variant 3: has_frame_changed=False, is_animation_playing=True Scrubbing variant 4(stop): has_frame_changed=False, is_animation_playing=False Playing animation: has_frame_changed=True, is_animation_playing=True Playing animation(stop): has_frame_changed=False, is_animation_playing=False """ if bpy.context.screen is None: # rendering is_playing = False # should be False to update UI is_frame_changed = True else: is_playing = bpy.context.screen.is_animation_playing is_frame_changed = has_frame_changed(scene) # print(f"Frame changed: {is_frame_changed}, Animation is playing: {is_playing}") for ng in sverchok_trees(): # Comparatively small overhead with 200 trees in a file with catch_log_error(): ng.process_ani(is_frame_changed, is_playing)
def sverchok_trees()
-
Expand source code
def sverchok_trees(): for ng in bpy.data.node_groups: if ng.bl_idname == 'SverchCustomTreeType': yield ng
def unregister()
-
Expand source code
def unregister(): app_handler_ops(remove=handler_dict) set_frame_change(None) bpy.app.handlers.load_post.remove(call_user_functions_on_post_load_event)
def update_frame_change_mode()
-
Expand source code
def update_frame_change_mode(): from sverchok import settings mode = settings.get_param("frame_change_mode", "POST") set_frame_change(mode)