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