fix(blender): hold a reference to the project enum items
Deploy / deploy (push) Successful in 6m6s

The project dropdown rendered garbled labels. EnumProperty does not copy
the strings a callback returns — Python has to keep a reference or
Blender reads freed memory, which shows as mojibake rather than as a
crash. The callback built fresh tuples on every redraw, so each set was
collected the moment it returned.

The items are now built once when the project list changes and held at
module level; the callback returns that list and allocates nothing, which
also makes a per-redraw callback free rather than merely correct.

Add-on 1.0.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
twotalesanimation
2026-08-15 19:24:46 +02:00
parent 3733c0c4fa
commit a18aad4373
+22 -5
View File
@@ -1,7 +1,7 @@
bl_info = { bl_info = {
"name": "VFXReview — Submit Render", "name": "VFXReview — Submit Render",
"author": "Two Tales", "author": "Two Tales",
"version": (1, 0, 1), "version": (1, 0, 2),
"blender": (3, 6, 0), "blender": (3, 6, 0),
"location": "Properties > Output > VFXReview", "location": "Properties > Output > VFXReview",
"description": "Queue this .blend on the studio render farm instead of rendering locally", "description": "Queue this .blend on the studio render farm instead of rendering locally",
@@ -101,12 +101,28 @@ def api_request(path, payload=None, method="GET"):
_project_cache = [] _project_cache = []
# Blender does not copy the strings an EnumProperty callback returns — Python
# must keep a reference to them or the UI renders freed memory, which shows up
# as garbled labels rather than as a crash. So the items are built once when the
# project list changes and held here; the callback only ever returns this list.
_enum_items = [("", "Refresh projects...", "")]
def rebuild_enum_items():
global _enum_items
if _project_cache:
_enum_items = [
(p["code"], "%s - %s" % (p["code"], p["name"]), "")
for p in _project_cache
]
else:
_enum_items = [("", "Refresh projects...", "")]
return _enum_items
def project_items(self, context): def project_items(self, context):
"""Project dropdown. Cached because Blender calls this on every redraw.""" """Called on every redraw, so it does no work and allocates nothing."""
if not _project_cache: return _enum_items
return [("", "Refresh projects…", "")]
return [(p["code"], "%s — %s" % (p["code"], p["name"]), "") for p in _project_cache]
# ── Scene → job ─────────────────────────────────────────────────────────────── # ── Scene → job ───────────────────────────────────────────────────────────────
@@ -142,6 +158,7 @@ class VFXR_OT_refresh_projects(Operator):
return {"CANCELLED"} return {"CANCELLED"}
_project_cache = data.get("projects", []) _project_cache = data.get("projects", [])
rebuild_enum_items()
self.report({"INFO"}, "%d project(s) loaded" % len(_project_cache)) self.report({"INFO"}, "%d project(s) loaded" % len(_project_cache))
return {"FINISHED"} return {"FINISHED"}