There were many pits and I fell in them all
- KeyMapItem operator properties don’t update properly
- KeyMapItem IDs change to fill gap if an item is removed
- The change of KeyMapItem IDs is not instant
- Assignment fails with ‘int’ object does not support item assignment
find()method doesn’t seem to usefully work
- You can’t inherit from / extend the CollectionProperty (seemingly)
- Accessing a CollectionProperty via property is different from accessing it via subscripting
- The CollectionProperty list accessed via subscript does not support
- Next Steps
When making videos in Blender’s VSE I use a lot of text sequences for captions and the like. Doing common operations — like changing colour, location, size etc — can be slow when using the GUI, so I am writing an addon called QTE (Quicker Text Editing, pronounced ‘cutie’) to make the process faster. This series of posts outlines the process and tries to explain parts of the Blender API that I interacted with along the way.
Show all parts (click to expand)Parts:
- Part 1: What We Want To Do & Proof of Concept
- Part 2: Operators, Dynamic Classes & a Basic Script
- Part 3: Keymaps
- Part 4: User Preferences (& Panels)
- Part 5: QTE Alpha Release
- Part 6: Preferences and the right keymap
- Part 7: QTE Beta Release (& ‘Remove Binding’ Operator)
- Part 8: Bugs & Features
- Part 9: Prototyping Appearing Text
- Interlude: Why is my Blender addon panel property read only? (Solved)
- Part 10: Beta 2 Release (+ Development Hurdles)
- Part 11: Reflection and Next Steps
- Part 12: Preferences + Properties = Bugs
- Part 13: CollectionProperty, KeyMapItem ID, Preferences Pitfalls
In part 12 I mentioned the issue with storing operator properties in a KeyMapItem- the tl;dr is that updating the properties from a panel (eg in addon preferences) doesn’t seem to properly update the values if the key binding is not updated as well. Having run into the issue of something (panel options) seemingly not updating before, I thought I’d try a similar approach, ie: store the options elsewhere, and pass in a reference to the operator to tell it where to find the options. This approach works (spoilers!) but led me down a long rabbit hole of trying to figure out appropriate data structures, implementing and unimplementing solutions, trying workarounds and then trying to figure out workarounds for problems with the original workarounds…
I am no professional programmer, but it was the kind of experience that wonderfully demonstrates that measuring someone’s performance by line of code for the folly that it is. I spent a day thinking, writing, testing, scrubbing what was written, re-writing, re-testing, (repeat), consulting API docs, forum posts and QAs…
Having working familiarity with a system — which I don’t yet have but is slowly growing! — is valuable in and of itself. It’s one of those soft attributes which is hard to quantify, but aids just about every aspect of development.
I have collected some of the pitfalls here partly as a testament to what I did, as by raw output it didn’t feel like much (though that is not a great metric on which to judge, see the aside); and partly so that is someone else is pulling their hair out, they can at least know it wasn’t just them.
KeyMapItem operator properties don’t update properly
This is the one we discussed in the last part. Just seeing if you’re paying attention!
KeyMapItem IDs change to fill gap if an item is removed
Simply put, if you use the KeyMapItem’s
.id property to track and sync options, the saved value will become invalid if a KeyMapItem is remove()’d and that KMI is not the last one added:
Aside: I’m not sure why Blender uses negative integers for user-added KeyMapItems, but it does help to distinguish them.
The change of KeyMapItem IDs is not instant
If you have an operator which removes a keymapitem, and the other corresponding data, like a preference CollectionProperty entry, you might think- “I can update the ids of any that are out of sync”. Well no:
I’m not sure why, but setting
context.preferences.is_dirty = True might do it. I say ‘might’ because…
Assignment fails with ‘int’ object does not support item assignment
To be honest, I only noticed this issue when looking through my console scrollback history- by this point my brain just saw errors with what I was doing and assumed it was more of the same.
This appears to simply be a thinko- looking at my code it seems I used the returned value[s] from another function incorrectly- I assumed it would give me a collection of matching objects; instead it returns a list of the ids to the matching objects.
I’d complain to whoever wrote the function for not being pythonic, but…
find() method doesn’t seem to usefully work
I was trying to remove an item from a CollectionProperty, like in this BSE QA. The QA explains that you can use either the
clear() methods, depending on how much you want to remove, it is unclear on how to get the index of the element you want to remove.
The second answer on the QA suggests using
find(), which is documented albeit in a hard-to-find place, and so it has contextual help in Blender’s python console:
However, for whatever reason I could not get it to return anything other than
You can’t inherit from / extend the CollectionProperty (seemingly)
Given the lack of ease of removing items, I thought I’d implement my own class (with blackjack etc) with convenience functions, which I started working on
However, trying to do so gives an error:
TypeError: cannot create 'builtin_function_or_method' instances
Some quick searching leads me to believe this is due to how the class is implemented on the C side of Blender’s python API, though I could be wrong.
Accessing a CollectionProperty via property is different from accessing it via subscripting
gives a different object from
I’ve used subscript notation to access properties that have
update defined without creating a circular reference, so I thought that I could do the same again to pass in a reference (more accurately: a key) to the preset set I wanted to updated.
The dot notation access seems to give the actual object (that is the class which you have to look for specifically in the docs, mentioned above), whereas subscripting gives a list of items instead.
Looking at the objects inside each:
Once again, the dot notation access seems to give the “full” version of the object, whereas the subscript gives a slightly different representation.
You can go further and examine the
IDPropertyGroup objects and see that they do have the data that they should:
The CollectionProperty list accessed via subscript does not support
While the two data structures above look similar, it isn’t possible to mimic a
remove() action by trying to
del a member of the ColelctionProperty list:
Whereas this of course works with regular lists, as intended:
I thought the objects might be silently immutable / readonly, but you can update individual properties just fine:
No doubt if I could locate the relevant part of the C code implementing these types (probably could if I had to) and actually have a decent appreciation of what the code was doing (probably not) I would understand this behaviour- unfortunately I am stuck with the caveman approach of trying things out and then trying alternatives when they don’t work.
I still want to transition the ‘original four’ text operators to store their options in the addon preferences structure, with a uuid to keep the options and keymapitem in sync. I should also move the options for the ‘split to appearing words’ operator to preferences from window_manager, but that should hopefully be a much simpler operation.