How to get mouse position in draw event (before modal or invoke)?

Let say, I want to make a Gizmo, that will react with mouse position like Loop Cut tool does. So these events are perfect for me:

class MyGizmo(bpy.types.Gizmo):
    def draw(self, context):
        print("draw")

    def draw_select(self, context, select_id=0):
        print("draw select")

But to get mouse position, typically the Modal event is used, which have a event property with mouse position. But I want to get mouse position before the Invoke. So maybe I can get mouse position differently?

Hey

We can abuse an operator invoke or modal to get to bpy.types.event

I strongly believe we should have an access to something like bpy.context.window.event or similar from the API. i see no reasons it’s not accessible from the first place

3 Likes

it could swear that it used to be available through context.window back in the pre 2.7 days, but I can’t find any evidence of that so I might have just dreamed it.

Can’t get your hack unfortunately, have this error:

RuntimeError: Calling operator "bpy.ops.my_operator" error, can't modify blend data in this state (drawing/rendering)

But thanks for your attempt…

You want to do stuffs while blender is rendering? is that correct?

I want to draw in the scene some geometry, while the mouse is moving. Like Loop cut tool does.

as far as I know, the loop cut tool isn’t using a gizmo, it’s using a GL draw handler.

The basic idea behind the draw handlers would be- in a modal operator, you build your GL batch, and then the handler draws it during the next refresh (it doesn’t happen in the same frame). since you have access to an event during modal(), you’ll have the mouse position.

Yep, but how to run it in modal when a tool is selected? This is the only problem.

Gizmo group activates when the tool is selected, but where is no access to event data.
Operator should be launched, so it shall not activate until you press anywhere to invoke operator

You can’t just call operator from pool() or setup() in gizmoGroup you will get this error.

I think there is some ugly way to setup the timer to trigger the operator, but it looks very bad

I think you need to use the test_select() method of the gizmo, one of the parameters is the region-relative mouse coordinates:
https://docs.blender.org/api/current/bpy.types.Gizmo.html#bpy.types.Gizmo.test_select

This is what the preselect gizmo of the PolyBuild tool uses to highlight elements as the mouse moves around:

  1. https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c#L269
  2. https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c#L78

The alternative is doing it like the knife tool does (as @testure said above), which is a modal operator that adds a draw handler, you get the mouse coordinates inside modal() from the event parameter, keep them in a variable that’s an attribute of some object whose reference is sent to the draw handler as parameter, where you can read them back from the variable and draw stuff based on them.

Note how in that test_select() implementation they still need to “cheat” and grab the current window event (what @BD3D is talking about) so they can read what modifier keys are pressed, so yeah, exposing that CTX_wm_window(C)->eventstate member (pointing to the latest event) would be useful to Python add-on devs.

Alternatively, extending test_select() to have the event parameter like modal() does.

Tried test_select, but this event didn’t execute. Draw select and draw works, but not test_select. Maybe it should be setup properly…

1 Like