I would like to perform an operation immediately upon the completion of a grease pencil drawing session (for an add-on). How can I detect when a grease pencil session has completed? That is, when the user releases the left mouse button after drawing a single curve? I am running modal, but the grease pencil operator seems to eat the LMB release event, as nothing gets passed through to my event detection.
Inside the general def modal(self, context, event), printing event.type and event.value catches no event on the mouse release at the end of the grease pencil. So it seems that the grease pencil itself is not passing these events through. But Blender is obviously aware of them (for example, the “draw” button is grayed out until the click release), so there must be some way of catching this event.
Yeah, the event is “consumed”, but can you make Blender call another operator if you bind one to the keys/mouse buttons that are used when releasing the GP?
I’m not sure I understand your idea. I am trying to avoid any extra key strokes, as my users have to draw a very large number of lines and they have explicitly requested that a certain action take place immediately upon grease pencil release, without having to take another action. I am not able to detect the grease pencil click release in standard modal mode, so binding anything to LMB-release doesn’t help because the event is not detected.
But there must be some event or flag in Blender that signals the end of the grease pencil stroke that can be detected somehow: the only change I can find is the existence of a new grease pencil stroke object, but I don’t know how to turn this existence into a detectable event.
Define callbacks or listeners to be notified when data is changed
What I meant was to try to set up a hotkey to capture the release event after a GP stroke, and bind it to some operator…
import bpy
import time
from datetime import datetime
class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"
def execute(self, context):
timestamp = datetime.strftime(datetime.now(), "%H:%M:%S.%f")[:-3]
self.report({'INFO'}, "GP end {}".format(timestamp))
return {'FINISHED'}
def register():
bpy.utils.register_class(SimpleOperator)
def unregister():
bpy.utils.unregister_class(SimpleOperator)
if __name__ == "__main__":
register()
but it seems to be fully consumed… Left Mouse + D doesn’t trigger it if I make a hotkey for the same combination. If I remove the key modifier D, then it is only captured if no stroke is make
So not looking good.
Instead of trying to “catch” the event, a better approach is to create a Macro Operator that chains together the Grease Pencil operator and any other operator(s) that you may wish to run after it. At least in the internal C-API, this is rather straightforward, and is simply a matter of defining a new macro operator and then attaching each of the operators you wish to call to it (along with the necessary parameters for getting those to do what you need). Examples of tools built this way include the Extrude and Duplicate tools, where a normal operator is used for the data creation part, and the transform operator gets called afterwards to operate on the newly created geometry.
I’m not too familiar with the Python API, but either one of the following solutions may work:
Create a macro operator somehow
Create a new operator that calls the required operators one by one. You’ll probably want to stick these things in the “invoke()” method (so that the Grease Pencil interactive drawing part will work)
That’s a good idea! Macros can be defined in Python, too. You would need to let your addon “replace” the keybindings however. AFAIK keyconfigs.addons takes precedence over keyconfigs.default, so that you shouldn’t have to actually replace the default keybindings.
With the syntax I am trying, the later functions do not wait for the grease pencil drawing to complete before being called, but I’m just making this up as I go along.
Q1: Is it possible to call a macro while remaining inside a modal session?
I have also discovered that the poll() function of active operators (eg the class for my modal operator which is activated from a panel button) does get called on the grease pencil click release. Unfortunately, it is not possible to modify data from the poll function: “RuntimeError: Calling operator “bpy.ops.gpencil.convert” error, can’t modify blend data in this state (drawing/rendering)”.
Q2: Is there a hack to be able to modify data when an event is detected from an operator’s poll() function, when the operator itself is not called?
PS - Thanks for the suggestion Albertofx, but my users really want to be able to have an action performed immediately on click release without having to hit any other keys, because they are drawing a large number of curves, so I am hoping there is still a way.
def modal(self, context, event):
bpy.ops.gpencil.draw('INVOKE_DEFAULT',mode ="DRAW")
if event.type == 'LEFTMOUSE':
if event.value == 'PRESS':
#do this
elif event.value == 'RELEASE':
#do this
If you figure how to fit this in for the grease pencil, like have the RELEASE event work after drawing out a stroke, please share. I’m developing an add-on and i need the same idea. Thanks.