import bpy # https://blenderartists.org/t/how-to-execute-operator-once-when-keymap-is-held-down/1166009 class GPTB_OT_temp_cutter(bpy.types.Operator): bl_idname = "wm.temp_cutter" bl_label = "Temporary cutter" bl_description = "Temporary cutter during press in GP mode" bl_options = {'REGISTER'}#, 'UNDO' avoid register undo step _is_running = False# block subsequent 'PRESS' events bpy.types.Scene.tmp_cutter_org_mode = bpy.props.StringProperty( name="temp cutter previous mode", description="Use to store mode used before cutter", default="") # original_mode = None def execute(self, context): print('exe so cute') bpy.ops.wm.tool_set_by_id(name='builtin.cutter') return {'FINISHED'} def invoke(self, context, event): if event.value == 'RELEASE': __class__._is_running = False # if self.original_mode: # bpy.ops.wm.tool_set_by_id(name = self.original_mode) if context.scene.tmp_cutter_org_mode: bpy.ops.wm.tool_set_by_id(name = context.scene.tmp_cutter_org_mode) # self.original_mode = None # return 'CANCELLED' unless the code is important, # this prevents updating the view layer unecessarily return {'CANCELLED'} elif event.value == 'PRESS': if not self._is_running: # self.original_mode = bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False).idname context.scene.tmp_cutter_org_mode = bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False).idname __class__._is_running = True return self.execute(context) return {'CANCELLED'} class GPTB_OT_sticky_cutter(bpy.types.Operator): bl_idname = "wm.sticky_cutter" bl_label = "Sticky cutter" bl_description = "Sticky cutter tool" bl_options = {'REGISTER'}#, 'UNDO' def execute(self, context): # toggle code # if self.original_mode == 'builtin.cutter':#if on cutter, return to draw # bpy.ops.wm.tool_set_by_id(name='builtin_brush.Draw') return {'FINISHED'} def modal(self, context, event): if event.type == self.key and event.value == 'PRESS': return {'RUNNING_MODAL'} elif event.type == self.key and event.value == 'RELEASE': if self.timeout: # use release code in here bpy.ops.wm.tool_set_by_id(name=self.original_mode) print("released") return {'FINISHED'} wm = context.window_manager wm.event_timer_remove(self.handler) # return {'FINISHED'} return self.execute(context) elif event.type == 'TIMER': self.timeout = True wm = context.window_manager wm.event_timer_remove(self.handler) if self.timeout: pass # print("repeating holding down") # use holding down code in here # bpy.ops.wm.tool_set_by_id(name='builtin.cutter')#builtin.cutter cursor return {'PASS_THROUGH'} def invoke(self, context, event): self.key = '' #get key from keymap wm = bpy.context.window_manager #addons : #wm.keyconfigs.addon.keymaps.items() for cat, keymap in wm.keyconfigs.user.keymaps.items():#user set for k in keymap.keymap_items: if k.idname == 'wm.sticky_cutter': self.key = k.type if not self.key: self.report({'ERROR'}, 'Could not found dedicated key in user keymap for "wm.sticky_cutter"') return {'CANCELLED'} self.original_mode = bpy.context.workspace.tools.from_space_view3d_mode(bpy.context.mode, create=False).idname if event.value == 'PRESS': self.timeout = False wm = context.window_manager wm.modal_handler_add(self) bpy.ops.wm.tool_set_by_id(name='builtin.cutter') self.handler = wm.event_timer_add( time_step=0.2, window=context.window) return {'RUNNING_MODAL'} return {'CANCELLED'} ## keymaps ''' tmp_cutter_addon_keymaps = [] def register_keymaps(): # pref = get_addon_prefs() # if not pref.temp_cutter_use_shortcut: # return addon = bpy.context.window_manager.keyconfigs.addon try: km = bpy.context.window_manager.keyconfigs.addon.keymaps["3D View"]# Grease Pencil except Exception as e: km = addon.keymaps.new(name = "3D View", space_type = "VIEW_3D") #3D View pass ops_id = 'wm.temp_cutter'# 'wm.sticky_cutter' if ops_id not in km.keymap_items: ## keymap to operator cam space (in grease pencil mode only ?) km = addon.keymaps.new(name='3D View', space_type='VIEW_3D')#EMPTY #Grease Pencil #3D View # use 'ANY' to map both 'PRESS' and 'RELEASE' to the operator # then use the operator's invoke to deal with each articulation kmi = km.keymap_items.new(ops_id, type="T", value="ANY")#, alt=pref.use_alt, ctrl=pref.use_ctrl, shift=pref.use_shift, any=False) tmp_cutter_addon_keymaps.append(km) def unregister_keymaps(): # wm = bpy.context.window_manager for km in tmp_cutter_addon_keymaps: for kmi in km.keymap_items: km.keymap_items.remove(kmi) # wm.keyconfigs.addon.keymaps.remove(km)#dont use new km field... tmp_cutter_addon_keymaps.clear() # del tmp_cutter_addon_keymaps[:] ''' def register(): if not bpy.app.background: bpy.utils.register_class(GPTB_OT_temp_cutter) bpy.utils.register_class(GPTB_OT_sticky_cutter) # register_keymaps() def unregister(): if not bpy.app.background: # unregister_keymaps() bpy.utils.unregister_class(GPTB_OT_temp_cutter) bpy.utils.unregister_class(GPTB_OT_sticky_cutter) if __name__ == "__main__": register()