gizmo_tweak() does not return 'FINISHED' when invoked on gizmo #66843

Closed
opened 2019-07-13 05:47:46 +02:00 by andy van dalsem · 6 comments

System Information
Operating system: Windows 10
Graphics card:

Blender Version
Broken: 2.80, 648e8a1f1d, master, 2019-06-28
Worked: 2.80, d30f72dfd8, master, 2019-06-19

Short description of error
bpy.ops.gizmogroup.gizmo_tweak() does not reliably return 'FINISHED' when invoked during a modal operator if bpy.context.view_layer.objects.active is set or modified in any way. Prior to 648e8a1f1d, running the following script in a modal operator (which invoked gizmo_tweak() on a MOUSEMOVE event) would return 'FINISHED' and would proceed to activate the gizmo, manipulating the object.
Now the same script, used in the same exact way returns {‘PASS_THROUGH’, ‘CANCELLED’}.

Exact steps for others to reproduce the error

  1. Execute the following script:
import bpy
from bpy.props import IntProperty

def in_threshold(x_pos_1, x_pos_2, y_pos_1, y_pos_2, thresh):
     
    return ((x_pos_1 - thresh) <= x_pos_2 <= (x_pos_1 + thresh) and
           (y_pos_1 - thresh) <= y_pos_2 <= (y_pos_1 + thresh))
           
class GizmoOperator(bpy.types.Operator):
    """Gizmo Tweak Example"""
    bl_idname = "object.gizmo_operator"
    bl_label = "Gizmo Tweak Operator"

    first_mouse_x: IntProperty()
    first_mouse_y: IntProperty()
    tweak_thresh = bpy.context.preferences.inputs.drag_threshold_mouse
    exit = False
    count = 0

    def modal(self, context, event):

        if self.exit:
            return {'FINISHED'}
        
        elif event.type == 'LEFTMOUSE':
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            return {'CANCELLED'}
        
        # Wait for mouse to move beyond 'tweak threshold"
        elif not in_threshold(self.first_mouse_x, event.mouse_x, 
                            self.first_mouse_y, event.mouse_y, 
                            self.tweak_thresh):
                           
            - Gizmo_Tweak will return 'PASS_THROUGH', 'CANCELLED' the first time
            - and 'FINISHED' the second time.
            if event.type == 'MOUSEMOVE':
                self.count += 1
                try_gizmo = bpy.ops.gizmogroup.gizmo_tweak('INVOKE_DEFAULT')
                #print(try_gizmo)
                print("TRY #:", self.count, 'FINISHED' in try_gizmo)
                if 'FINISHED' in try_gizmo:
                    self.exit = True
                
            return {'PASS_THROUGH'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        self.tweak_thresh = bpy.context.preferences.inputs.drag_threshold_mouse
        
        if context.object:
            self.first_mouse_x = event.mouse_x
            self.first_mouse_y = event.mouse_y

            active = bpy.context.active_object
            select = bpy.context.selected_objects

            # Modify the selection in some way
            for o in select:
                o.select_set(True)
                view_layer = bpy.context.view_layer
                
                    - Setting the active object is causing the problem
                    - If you comment out the following active object assignment, gizmo_tweak() correctly returns 'FINISHED' again

                view_layer.objects.active = active

            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "No active object, could not finish")
            return {'CANCELLED'}


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


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


if __name__ == "__main__":
    register()
  1. Assign operator to a keymap (3D View Tool: Move to Mouse>Right>Press):

The operator is object.gizmo_operator
For this purpose just put it under 3D View Tool: Move> Mouse> Right> Press. Then, disable the Move operator
image.png

  1. Open the system console:

  2. Make the Active Tool the Move Tool. Now right-click>drag on the gizmo of a selected object:

This is what should print in the console:

TRY #: 1 False
TRY #: 2 True

gizmo_tweak() is returning {‘PASS_THROUGH’, ‘CANCELLED’} on the first pass of modal operation, then returning 'FINISHED' the second time through.

I built Blender myself and tried it in debug. The first pass which returns {‘PASS_THROUGH’, ‘CANCELLED’} raised the following exception:

>	blender.exe!issue_debug_notification(const wchar_t * const message) Line 28	C++

 	blender.exe!__acrt_report_runtime_error(const wchar_t * message) Line 154	C++
 	blender.exe!abort() Line 61	C++
 	blender.exe!gizmo_tweak_invoke(bContext * C, wmOperator * op, const wmEvent * event) Line 593	C
 	blender.exe!wm_operator_invoke(bContext * C, wmOperatorType * ot, wmEvent * event, PointerRNA * properties, ReportList * reports, const bool poll_only, bool use_last_properties) Line 1438	C
 	blender.exe!wm_operator_call_internal(bContext * C, wmOperatorType * ot, PointerRNA * properties, ReportList * reports, const short context, const bool poll_only, wmEvent * event) Line 1685	C
 	blender.exe!WM_operator_call_py(bContext * C, wmOperatorType * ot, short context, PointerRNA * properties, ReportList * reports, const bool is_undo) Line 1785	C
 	blender.exe!pyop_call(_object * UNUSED_self, _object * args) Line 267	C
 	[External Code]	
 	blender.exe!bpy_class_call(bContext * C, PointerRNA * ptr, FunctionRNA * func, ParameterList * parms) Line 8295	C
 	blender.exe!rna_operator_modal_cb(bContext * C, wmOperator * op, const wmEvent * event) Line 1378	C
 	blender.exe!wm_handler_operator_call(bContext * C, ListBase * handlers, wmEventHandler * handler_base, wmEvent * event, PointerRNA * properties) Line 2186	C
 	blender.exe!wm_handlers_do_intern(bContext * C, wmEvent * event, ListBase * handlers) Line 2897	C
 	blender.exe!wm_handlers_do(bContext * C, wmEvent * event, ListBase * handlers) Line 2945	C
 	blender.exe!wm_event_do_handlers(bContext * C) Line 3307	C
 	blender.exe!WM_main(bContext * C) Line 420	C
 	blender.exe!main(int argc, const unsigned char * * UNUSED_argv_c) Line 502	C
 	[External Code]

If you comment out this line in the opersator script:

view_layer.objects.active = active

gizmo_tweak() will work on the first pass again.

I've tried setting active to None, making another scene object active, making the current active object not active then active again. It seems any attempt to set objects.active in any way before running modal causes gizmo_tweak() not to work on the first pass.

Lastly, if you invoke gizmo_tweak() immediately after assigning an active object before running modal, gizmo_tweak() will return 'FINISHED' as expected.

If what I'm seeing is intended (just confused because it worked fine before) is there a workaround that will allow active object assignment without causing gizmo_tweak() throw an exception?

Thanks!

**System Information** Operating system: Windows 10 Graphics card: **Blender Version** Broken: 2.80, 648e8a1f1d4a, master, 2019-06-28 Worked: 2.80, d30f72dfd8ac, master, 2019-06-19 **Short description of error** bpy.ops.gizmogroup.gizmo_tweak() does not reliably return 'FINISHED' when invoked during a modal operator if bpy.context.view_layer.objects.active is set or modified in any way. Prior to 648e8a1f1d4a, running the following script in a modal operator (which invoked gizmo_tweak() on a MOUSEMOVE event) would return 'FINISHED' and would proceed to activate the gizmo, manipulating the object. Now the same script, used in the same exact way returns {‘PASS_THROUGH’, ‘CANCELLED’}. **Exact steps for others to reproduce the error** 1. Execute the following script: ``` import bpy from bpy.props import IntProperty def in_threshold(x_pos_1, x_pos_2, y_pos_1, y_pos_2, thresh): return ((x_pos_1 - thresh) <= x_pos_2 <= (x_pos_1 + thresh) and (y_pos_1 - thresh) <= y_pos_2 <= (y_pos_1 + thresh)) class GizmoOperator(bpy.types.Operator): """Gizmo Tweak Example""" bl_idname = "object.gizmo_operator" bl_label = "Gizmo Tweak Operator" first_mouse_x: IntProperty() first_mouse_y: IntProperty() tweak_thresh = bpy.context.preferences.inputs.drag_threshold_mouse exit = False count = 0 def modal(self, context, event): if self.exit: return {'FINISHED'} elif event.type == 'LEFTMOUSE': return {'FINISHED'} elif event.type in {'RIGHTMOUSE', 'ESC'}: return {'CANCELLED'} # Wait for mouse to move beyond 'tweak threshold" elif not in_threshold(self.first_mouse_x, event.mouse_x, self.first_mouse_y, event.mouse_y, self.tweak_thresh): - Gizmo_Tweak will return 'PASS_THROUGH', 'CANCELLED' the first time - and 'FINISHED' the second time. if event.type == 'MOUSEMOVE': self.count += 1 try_gizmo = bpy.ops.gizmogroup.gizmo_tweak('INVOKE_DEFAULT') #print(try_gizmo) print("TRY #:", self.count, 'FINISHED' in try_gizmo) if 'FINISHED' in try_gizmo: self.exit = True return {'PASS_THROUGH'} return {'RUNNING_MODAL'} def invoke(self, context, event): self.tweak_thresh = bpy.context.preferences.inputs.drag_threshold_mouse if context.object: self.first_mouse_x = event.mouse_x self.first_mouse_y = event.mouse_y active = bpy.context.active_object select = bpy.context.selected_objects # Modify the selection in some way for o in select: o.select_set(True) view_layer = bpy.context.view_layer - Setting the active object is causing the problem - If you comment out the following active object assignment, gizmo_tweak() correctly returns 'FINISHED' again view_layer.objects.active = active context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} else: self.report({'WARNING'}, "No active object, could not finish") return {'CANCELLED'} def register(): bpy.utils.register_class(GizmoOperator) def unregister(): bpy.utils.unregister_class(GizmoOperator) if __name__ == "__main__": register() ``` 2. Assign operator to a keymap (3D View Tool: Move to Mouse>Right>Press): The operator is **object.gizmo_operator** For this purpose just put it under 3D View Tool: Move> Mouse> Right> Press. Then, disable the Move operator ![image.png](https://archive.blender.org/developer/F7604532/image.png) 3. Open the system console: 4. Make the Active Tool the Move Tool. Now right-click>drag on the gizmo of a selected object: This is what should print in the console: ``` TRY #: 1 False TRY #: 2 True ``` gizmo_tweak() is returning {‘PASS_THROUGH’, ‘CANCELLED’} on the first pass of modal operation, then returning 'FINISHED' the second time through. I built Blender myself and tried it in debug. The first pass which returns {‘PASS_THROUGH’, ‘CANCELLED’} raised the following exception: ``` > blender.exe!issue_debug_notification(const wchar_t * const message) Line 28 C++ blender.exe!__acrt_report_runtime_error(const wchar_t * message) Line 154 C++ blender.exe!abort() Line 61 C++ blender.exe!gizmo_tweak_invoke(bContext * C, wmOperator * op, const wmEvent * event) Line 593 C blender.exe!wm_operator_invoke(bContext * C, wmOperatorType * ot, wmEvent * event, PointerRNA * properties, ReportList * reports, const bool poll_only, bool use_last_properties) Line 1438 C blender.exe!wm_operator_call_internal(bContext * C, wmOperatorType * ot, PointerRNA * properties, ReportList * reports, const short context, const bool poll_only, wmEvent * event) Line 1685 C blender.exe!WM_operator_call_py(bContext * C, wmOperatorType * ot, short context, PointerRNA * properties, ReportList * reports, const bool is_undo) Line 1785 C blender.exe!pyop_call(_object * UNUSED_self, _object * args) Line 267 C [External Code] blender.exe!bpy_class_call(bContext * C, PointerRNA * ptr, FunctionRNA * func, ParameterList * parms) Line 8295 C blender.exe!rna_operator_modal_cb(bContext * C, wmOperator * op, const wmEvent * event) Line 1378 C blender.exe!wm_handler_operator_call(bContext * C, ListBase * handlers, wmEventHandler * handler_base, wmEvent * event, PointerRNA * properties) Line 2186 C blender.exe!wm_handlers_do_intern(bContext * C, wmEvent * event, ListBase * handlers) Line 2897 C blender.exe!wm_handlers_do(bContext * C, wmEvent * event, ListBase * handlers) Line 2945 C blender.exe!wm_event_do_handlers(bContext * C) Line 3307 C blender.exe!WM_main(bContext * C) Line 420 C blender.exe!main(int argc, const unsigned char * * UNUSED_argv_c) Line 502 C [External Code] ``` If you comment out this line in the opersator script: ``` view_layer.objects.active = active ``` gizmo_tweak() will work on the first pass again. I've tried setting active to None, making another scene object active, making the current active object not active then active again. It seems any attempt to set objects.active in any way before running modal causes gizmo_tweak() not to work on the first pass. Lastly, if you invoke gizmo_tweak() immediately after assigning an active object *before* running modal, gizmo_tweak() will return 'FINISHED' as expected. If what I'm seeing is intended (just confused because it worked fine before) is there a workaround that will allow active object assignment without causing gizmo_tweak() throw an exception? Thanks!

Added subscriber: @andyvandalsem

Added subscriber: @andyvandalsem
Campbell Barton self-assigned this 2019-08-12 20:52:25 +02:00

Added subscriber: @mano-wii

Added subscriber: @mano-wii

Changed status from 'Needs Triage' to: 'Confirmed'

Changed status from 'Needs Triage' to: 'Confirmed'

This report is too time consuming for us to track down. It would be better if it was simplified.
But since @ideasman42 has claimed this task I will consider it as confirmed.

I could not reproduce this problem using the selection keymap with the LMB.
But I had this assert when assign the operator keymapitem to LMB instead RMB:

BLI_assert failed: ...\source\blender\windowmanager\gizmo\intern\wm_gizmo_group.c:577, gizmo_tweak_invoke(), at '0'
This report is too time consuming for us to track down. It would be better if it was simplified. But since @ideasman42 has claimed this task I will consider it as confirmed. I could not reproduce this problem using the selection keymap with the LMB. But I had this assert when assign the operator keymapitem to LMB instead RMB: ``` BLI_assert failed: ...\source\blender\windowmanager\gizmo\intern\wm_gizmo_group.c:577, gizmo_tweak_invoke(), at '0' ```

Changed status from 'Confirmed' to: 'Archived'

Changed status from 'Confirmed' to: 'Archived'

I've investigated this and I don't believe this is a reasonable use case to support.

  • On operator invoke, setting the active object requires a refresh which temporarily looses the highlighted gizmo.
  • After invoke, the mouse move events that would activate the refreshed gizmo are sent to the operators modal handler.

In practice, this means you can't perform actions that reset the gizmo, if you're going to have an operator that starts a gizmo tweak operation.

Closing.

I've investigated this and I don't believe this is a reasonable use case to support. - On operator invoke, setting the active object requires a refresh which temporarily looses the highlighted gizmo. - After invoke, the mouse move events that would activate the refreshed gizmo are sent to the operators modal handler. In practice, this means you can't perform actions that reset the gizmo, if you're going to have an operator that starts a gizmo tweak operation. Closing.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#66843
No description provided.