Operator for 2D button gizmo is not called

I have a simple GIZMO_GT_button_2d that should invoke an operator when clicked upon but this is not happening. I can call the operator directly with bpy.ops, but nothing happens when I click on the little gizmo

My operator

class CustomOperator(bpy.types.Operator):
    bl_idname = "run.forest"
    bl_label = "Run Forest"
    bl_description = "Run, Forest, Run!"

    def __init__(self):
        print("Start")

    def __del__(self):
        print("End")

    @classmethod
    def poll(cls, context):
        #return context.area.ui_type == 'VIEW_3D':
        return True

    def invoke(self, context, event):
        print("Start")
        return {'FINISHED'}

    def execute(self, context):
        print("Execute")
        return {'FINISHED'}

And then I build my gizmo like this

class CustomGizmos(bpy.types.GizmoGroup):
    bl_idname = "CustomGizmos"
    bl_label = "Custom gizmos"
    bl_space_type = "VIEW_3D"
    bl_region_type = "WINDOW"
    bl_options = {'PERSISTENT', 'SCALE'}
    
    @classmethod
    def setup_keymap(cls, keyconfig):
        keymap = bpy.context.window_manager.keyconfigs.addon.keymaps.new('run_forest')
        keymap.keymap_items.new('run.forest', 'P', 'PRESS')
        return keymap
    
    @classmethod
    def poll(cls, context):
        return True
    
    def draw_prepare(self, context):
        r_xpos = context.region.width - 30
        for gizmo in self.gizmos:
            gizmo.matrix_basis[0][3] = r_xpos
            gizmo.matrix_basis[1][3] = 48
            r_xpos += 55

    def setup(self, context):
        mpr = self.gizmos.new("GIZMO_GT_button_2d")
        mpr.bl_idname = "RunForestButton"
        mpr.icon = 'PLAY'
        mpr.draw_options = {'BACKDROP', 'OUTLINE'}
        mpr.target_set_operator('run.forest')
        mpr.alpha = 1.0
        mpr.color = 0.5, 0.5, 0.5
        mpr.color_highlight = 1.0, 1.0, 1.0
        mpr.alpha_highlight = 0.3
        mpr.scale_basis = (80 * 0.35) / 2
        mpr.use_tooltip = True
        mpr.show_drag = False
        mpr.use_draw_value = True
        
register_class(CustomOperator)
register_class(CustomGizmos)

#bpy.ops.run.forest()

I can see the operator’s description as a tooltip when I hover the gizmo, but clicking it doesn’t do anything. I also tried with invoke. The keyboard shortcut that I am trying to assign to the same operator doesn’t call it, either. Not even the constructor is giving me an output.

Apparently, this is the problem. I still can’t figure out why

In general, when I need to adjust a keymap, I will register those changes at the same time as I’m registering the operator that the keymap will call, whereas in your example you are defining the keymap from within the Gizmo.

As you noticed, that caused issues, I’ve edited your script to do what I think you want it to do, I removed 3 functions that weren’t necessary, and added the syntax for adjusting the keymap at the time that the other classes are registered.

import bpy
from bpy.utils import register_class

class CustomOperator(bpy.types.Operator):
    bl_idname = "run.forest"
    bl_label = "Run Forest"
    bl_description = "Run, Forest, Run!"

    @classmethod
    def poll(cls, context):
        return context.area.ui_type == 'VIEW_3D'

    def invoke(self, context, event):
        print('The gizmo works!')
        return {'FINISHED'}

    def execute(self, context):
        print("Execute")
        return {'FINISHED'}
    
class CustomGizmos(bpy.types.GizmoGroup):
    bl_idname = "CustomGizmos"
    bl_label = "Custom gizmos"
    bl_space_type = "VIEW_3D"
    bl_region_type = "WINDOW"
    bl_options = {'PERSISTENT', 'SCALE'}
    
    @classmethod
    def poll(cls, context):
        return True
    
    def draw_prepare(self, context):
        r_xpos = context.region.width - 30
        for gizmo in self.gizmos:
            gizmo.matrix_basis[0][3] = r_xpos
            gizmo.matrix_basis[1][3] = 48
            r_xpos += 55

    def setup(self, context):
        mpr = self.gizmos.new("GIZMO_GT_button_2d")
        mpr.bl_idname = "RunForestButton"
        mpr.icon = 'PLAY'
        mpr.draw_options = {'BACKDROP', 'OUTLINE'}
        mpr.target_set_operator('run.forest')
        mpr.alpha = 1.0
        mpr.color = 0.5, 0.5, 0.5
        mpr.color_highlight = 1.0, 1.0, 1.0
        mpr.alpha_highlight = 0.3
        mpr.scale_basis = (80 * 0.35) / 2
        mpr.use_tooltip = True
        mpr.show_drag = False
        mpr.use_draw_value = True
        
register_class(CustomOperator)
register_class(CustomGizmos)

# Registering keymap changes, remove them in your unregister function
wm = bpy.context.window_manager
kc = wm.keyconfigs.user

km = kc.keymaps['3D View']
kmi = km.keymap_items.new(
    "run.forest",
    'P',
    'PRESS'
)
kmi.active = True

Thanks. I guess I used def setup_keymap because I saw it in somebody else’s script. I don’t know if/how it worked for them, though. Indeed, separating that part did the trick