Read only, immutable, frozen, unchanging, static… just not working!
tl;dr: Both the panel and the operator need access to any properties- using a
PointerProperty to a
PropertyGroup which holds the operator options / properties and is stored somewhere accessible (like
bpy.types.WindowManager) is a decent way of doing this (ref).
When I went to implement the panel to provide the options for the ‘split into words’ operator, I found that I could display the one option I had written for the operator, but it wouldn’t change:
As part of QTE, I am implementing a new feature: splitting a text strip with a sentence into individual words which appear over time.
It took me a while to figure out panels for setting custom keymap items, but it turns out those are handled just very slightly differently anyway. Everywhere I looked talked about setting properties when calling an operator, but not why I could show an option on a panel but not set it.
You know how when you find a bunch of results that are talking about something very close to what you want to do but not quite, and a bunch of the results are from 10 years ago and out of date, and you begin to doubt your sanity and lose your hair? Me too.
Finally, as I was in the process of writing a question on BlenderArtists, I stumbled upon a thread that asked pretty much exactly what I was about to:
These operator properties can be made visible to the user in the UI panel: [snip] However, they’re not editable here – they’re locked.
There’s some info about panels and instancing of operators and their properties that follows, which is worth a read.
Elsewhere in the thread, there’s a suggestion about how to define and share operator properties between the operator and the panel, using a
PropertyGroup and a
PointerProperty. The idea is: create a PropertyGroup class to store settings; then store a reference to that as a PointerProperty either within the WindowManager or the Scene.
That approach works:
Code might speak louder than explanations, so here’s a simplified example from the feature I am working on:
# For reading this, you may want to jump to register() first where the reference to the PropertyGroup is stored, then back to the operator and panel classes # Options / properties of the operator & panel - the PropertyGroup class AppearingWordsOptions(bpy.types.PropertyGroup): time_offset: bpy.props.FloatProperty( name="Time offset", default=1, min=0.0, soft_max=5, step=5, description="Time between words appearing", update=update_frames_from_time, ) frame_offset: bpy.props.IntProperty( name="Frame offset", default=0, min=0, soft_max=300, step=5, description="Frames between words appearing", update=update_time_from_frames, ) # The operator itself class SEQUENCER_OT_split_to_appearing_words(TextSequenceAction): # (parts snipped) def execute(self, context): prop_group = context.window_manager.appearing_text_options frame_offset = prop_group.frame_offset # (rest of operator) # The panel class SEQUENCER_PT_appearing_text(bpy.types.Panel): """Panel for appearing text""" bl_label = "Appearing Words" bl_space_type = "SEQUENCE_EDITOR" bl_region_type = "UI" def draw(self, context): """Draw the appearing text panel""" prop_group = context.window_manager.appearing_text_options layout = self.layout layout.operator("sequencer.split_to_appearing_words") layout.prop(prop_group, "time_offset", slider=True) layout.prop(prop_group, "frame_offset", slider=True) # register the classes AND set up a PointerProperty to the operator/panel options in WindowManager def register(): for classname in REGISTER_CLASSES: bpy.utils.register_class(classname) for classname in PREFERENCES_CLASSES: bpy.utils.register_class(classname) bpy.types.SEQUENCER_PT_effect.append(appearing_text_panel_layout) bpy.types.WindowManager.appearing_text_options = \ bpy.props.PointerProperty(type=AppearingWordsOptions) def unregister(): for classname in REGISTER_CLASSES: bpy.utils.unregister_class(classname) for classname in PREFERENCES_CLASSES: bpy.utils.unregister_class(classname) bpy.types.SEQUENCER_PT_effect.remove(appearing_text_panel_layout) del bpy.types.WindowManager.appearing_text_options
Since some of the references to this dated back ~10 years, I asked a follow-up question about whether this was still the right approach on BlenderArtists. Hopefully this and the follow up Q will spare someone else’s hair!