tl;dr: How can I let the user define presets that tie together a user-defined property (eg colour, location, size, duration) with a user-defined key binding?
Context
I am writing an addon to speed up working with text sequences in the VSE (see my last question for more info)). I can do all the individual bits if I hard-code them which is fine for my own use, but Iād like to let users define their own presets. So I have a query or two about addon preferences key bindings:
(1) Should an addon, whose primary or sole interface are hotkeys, let the user define their key bindings via the addonās preferences panel; or via Blenderās own keymap editor?
I can see arguments for and argainst either option, or even for doing bothā ! The Human Interface Guidelines donāt make a suggestion either way.
Addon preferences | Blender Keymap | Both |
---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
![]() |
![]() |
(ā : I am aware that custom hotkeys added to keymap_items
will show up in the keymap editor, so even managing hotkeys āonlyā with the addon results in ābothā)
Addon preferences approach
I looked at doing it via addon preferences, hoping to end up with something like this:
Please forgive the non-descriptive names etc, this is an example of the layout left over from an earlier version where hotkeys were hard-coded in the script!
Iāve got code working for storing a colour name + colour:
I used a CollectionProperty
(which to my understanding works like a list) in the preferences and a custom class PropertyGroup
to store the colour_name and colour; with an operator to add a new item to the list:
class NewQTEPreset(bpy.types.Operator):
"""Create a new QTE preset"""
bl_idname = "qte.newpreset"
bl_label = "Add a new preset"
def execute(self, context):
addonprefs = context.preferences.addons[__name__].preferences
newpreset = addonprefs.presets.add()
return {'FINISHED'}
class ColourPresets(bpy.types.PropertyGroup):
colour_name: bpy.props.StringProperty(
name="Name",
description="Colour preset",
default="Red"
)
colour: bpy.props.FloatVectorProperty(
name="Text colour",
subtype='COLOR',
description="Colour for text",
size=4,
min=0.0,
max=1.0,
default=(1.0, 0.0, 0.0, 1), # red in RGBA
)
class QTEPreferences(bpy.types.AddonPreferences):
bl_idname = __name__
presets: bpy.props.CollectionProperty(type=ColourPresets)
def draw(self, context):
layout = self.layout
for preset in self.presets:
row = layout.row()
row.prop(preset, "colour_name")
row.prop(preset, "colour")
layout.operator("qte.newpreset", icon='ADD')
(As an aside, Iām not sure how kosher the context.preferences.addons[__name__].preferences
line is for adding to the addonsā preferences property is; I think this could be done another way.)
The Problem
Where Iām tripping up is tying in to the key bindings!
I thought I could add another property to the ColourPresets
PropertyGroup; thereās no property for key bindings (?), so I figured I could use a PointerProperty to point to a (new, empty) KeyMapItem created via an appropriate call to km.keymap_items.new()
. However, the following property definition (part of the ColourPresets ProeprtyGroup) fails:
keymapitem: bpy.props.PointerProperty(
name="Key",
type=bpy.types.KeyMapItem,
)
fails on registration with:
TypeError: PointerProperty(...) expected an RNA type derived from ID or ID Property Group (...) ValueError: bpy_struct "ColourPresets" registration error: 'keymapitem' PointerProperty could not register (see previous error)
(2) How can I let the user define presets that tie together a user-defined property (eg colour, location, size, duration) with a user-defined key binding?
Despite having dozens of tabs open across the API reference, here and BSE, Iām still pretty unfamiliar with the API, so if thereās something obvious Iāve overlooked Iād be grateful if that could be pointed out