I've been creating an add-on for Blender but discovered a problem. For my add-on, I need to be able to get drivers to update in realtime while a Modal Operator is running. Since the add-on code is very long, I've created a demonstration script given below:
import bpy
from bpy.props import (PointerProperty,
FloatProperty
)
from bpy.types import (Panel,
Menu,
Operator,
PropertyGroup,
)
class SerialProperties(PropertyGroup):
example_float: FloatProperty(
name="User Input Float",
description="Float to send to new item",
default = 0.0
)
class ModalOperator(bpy.types.Operator):
bl_idname = "object.modal_operator"
bl_label = "Simple Modal Operator"
def __init__(self):
print("Start")
def __del__(self):
print("End")
def execute(self, context):
context.object.location.x = self.value / 100.0
return {'FINISHED'}
def modal(self, context, event):
if event.type == 'MOUSEMOVE': # Apply
self.value = event.mouse_x
context.scene.serialtool.example_float = event.mouse_x
self.execute(context)
elif event.type == 'LEFTMOUSE': # Confirm
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel
context.object.location.x = self.init_loc_x
context.scene.serialtool.example_float = self.init_loc_x
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
self.init_loc_x = context.object.location.x
self.value = event.mouse_x
self.execute(context)
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
class SERIAL_PT_MainPanel(Panel):
bl_label = "Serial Panel"
bl_idname = "SERIAL_PT_MainPanel"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Serial" #You can replace serial in this line with any other word you want
#bl_context = "objectmode"
def draw(self, context):
layout = self.layout
scene = context.scene
serialtool = scene.serialtool
layout.prop(serialtool, "example_float")
layout.operator("object.modal_operator")
classes = ( #list all your classes here so you don't have to register them individually later
ModalOperator,
SerialProperties,
SERIAL_PT_MainPanel, #Order matters here
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.serialtool = PointerProperty(type=SerialProperties)
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
del bpy.types.Scene.serialtool
# test call
#bpy.ops.object.modal_operator('INVOKE_DEFAULT')
if __name__ == "__main__":
register()
For this demo, I'm using the custom property created by the script as the variable in a Driver used to drive the y Location of the Default cube in the default Blend file. However, the driver doesn't update correctly when the Modal Operator is running, as seen in the picture below:
As seen, the driven property (which for the demo is the y location of the cube) doesn't update at all, despite the driving property (the custom property created by the add-on) changing correctly. It stays at the same value it was before the Operator was run. I have no idea why this would be. When the custom property is changed manually (meaning through direct user input to the property itself), the driver functions normally. So the question: Why isn't the Modal Operator Updating the Driver?