4
$\begingroup$

I have a panel with a label inside the 3d viewport and I want to change the text of the label depending on if the mouse cursor is in the VIEW_3D area or not. I don't have an operator so I can't use invoke and using bpy.context.area.type from within the panel will always return VIEW_3D since that's the panels context.

This code doesn't work but is essentially what I'd like to be able to do:

import bpy


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Custom Panel"

    def draw(self, context):
        layout = self.layout

        if bpy.context.area.type == 'VIEW_3D':
            row = layout.row()
            row.label(text="Focused")
        else:
            row = layout.row()
            row.label(text="Panel not in focus")


def register():
    bpy.utils.register_class(HelloWorldPanel)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

Short video showing how I'd like it to work

From the context of the panel inside the 3d viewport, is there any way to tell if the mouse is outside the VIEW_3D area? I'd like to change a label if the cursor is in the 3d viewport.

$\endgroup$
5
  • 3
    $\begingroup$ Can you define what you mean by "active"? $\endgroup$ Jan 20, 2022 at 17:34
  • $\begingroup$ When a panel is draw the context area is always the one it's drawn in. Do you mean you want to know when the user hovers their mouse over different parts of the UI ? $\endgroup$
    – Gorgious
    Jan 20, 2022 at 19:13
  • 1
    $\begingroup$ Does this answer your question? How to get mouse position in a window with Python? $\endgroup$
    – pyCod3R
    Jan 20, 2022 at 19:56
  • $\begingroup$ @pyCod3R ISTM that isn't the answer the OP was looking for, because they needed the extra knowledge that their solution required access to the mouse position. $\endgroup$ Jan 20, 2022 at 21:09
  • $\begingroup$ @MarkusvonBroady @Gorgious sorry for the confusion, I edited the post for clarity. What I meant was for the panel to show a label depending on if the mouse cursor is inside or outside the VIEW_3D area $\endgroup$
    – CybranM
    Jan 21, 2022 at 13:09

1 Answer 1

2
$\begingroup$

If you define "active" as "cursor is in the area", then you need to have an actual test for where the mouse is. Here's a demonstration of an operator that figures out if the mouse is in the 3D Viewport. You need to modify it so that it puts the value somewhere appropriate to your needs rather than just reporting it. You then need to invoke it and use that value in your if test. The panel is only present as a demonstration, and you don't need it.

import bpy

def is_in_view3d_area(x,y):
    for area in bpy.context.screen.areas:
        if area.type == "VIEW_3D":
            x_in_range = x >= area.x and x <= area.x + area.width
            y_in_range = y >= area.y and y <= area.y + area.height
            return x_in_range and y_in_range
    return False

class SimpleMouseOperator(bpy.types.Operator):
    bl_idname = "wm.mouse_position"
    bl_label = "Mouse location"

    x : bpy.props.IntProperty()
    y : bpy.props.IntProperty()

    def execute(self, context):
        # rather then printing, use the report function,
        # this way the message appears in the header,
        in_range = is_in_view3d_area(self.x, self.y)
        self.report({'INFO'}, "Mouse coords are %d %d" % (self.x, self.y))
        self.report({'INFO'}, f"{in_range}")
        return {'FINISHED'}

    def invoke(self, context, event):
        self.x = event.mouse_x
        self.y = event.mouse_y
        return self.execute(context)

class MousePanel(bpy.types.Panel):
    bl_label = "Mouse"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        self.layout.operator("wm.mouse_position")

bpy.utils.register_class(SimpleMouseOperator)
bpy.utils.register_class(MousePanel)

bpy.ops.wm.mouse_position('INVOKE_DEFAULT')

The key take away is that the operator relies on the invoke function having an event with the mouse coordinates. is_in_view3d_area has to be called in the operator, since the 3D Viewport could have been resized since it was last called. It does assume you only have one 3D viewport. If you want to check any of several possible viewport areas than its logic has to change.

$\endgroup$
16
  • 5
    $\begingroup$ I'm sure you know this but I'll plug it anyways for completeness :) , in python you can chain comparison operators : x < y < z is the same as y > x and y < z docs.python.org/3/reference/expressions.html#comparisons $\endgroup$
    – Gorgious
    Jan 20, 2022 at 19:31
  • 1
    $\begingroup$ x : bpy.props.IntProperty(), y : bpy.props.IntProperty() $\endgroup$
    – pyCod3R
    Jan 20, 2022 at 21:05
  • 1
    $\begingroup$ @pyCod3R good spot. fixed. $\endgroup$ Jan 20, 2022 at 21:06
  • 1
    $\begingroup$ is_in_view3d_area(context, x, y); for area in context.screen.areas: $\endgroup$
    – pyCod3R
    Jan 20, 2022 at 21:12
  • 1
    $\begingroup$ @MarkusvonBroady If you are hanging out with a friend at a party, why would you go outside, call your friend and invite him again? $\endgroup$
    – pyCod3R
    Jan 21, 2022 at 7:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .