In BPython, GUI elements are created using the constructors and factory methods in the Draw module. Some methods like Create() return a python object while Button() simply creates the interface button without an associated python object. These methods have an optional tooltip argument that takes a python string.
For both types of methods, a blender uiBut is created as a side effect. If passed a tooltip arg, the uiBut struct ends up with a char* pointer into the character data stored in a python string. If the tooltip pystring was created inside the draw() callback, when the callback returns, the pystring goes out of scope and is destroyed since there is no other reference to it.
Tooltip events occur after the draw() callback has executed. If the tooltip string does not exist, the uiBut tooltip pointer is left pointing to junk in the python interpreter stack space. Not good!
As an aside, it looks as if the design of the BPy GUI creates a new blender uiBut everytime the draw() callback gets executed. If this is true, it seems like an awful lot of mallocing and freeing of blender memory blocks.
I haven't followed up on when and how the uiBut structs get destroyed.
Essentially, we need to keep our blender tooltip pointer pointed at valid memory. The 'obvious' solution of storing the tooltip text inside the bpy button doesn't work because the object can go out of scope and be destroyed by the time the tooltip is activated.
This leaves two other possibilites:
<typolist type="1">The Social Engineering Approach: Require and document the fact that python tooltip text must be created outside of the draw callback. This way the pystrings exist when the tooltip is activated. This works as long as everyone follows the rules and never forgets. Yeah. Right.
Having said that, it's worth pointing out that managing object lifetimes is a basic Object Oriented programming skill. Insisting an object to be alive when you reference it is not unreasonable. Just create your tooltips outside the draw() callback.
The Mess with Blender Internals Approach: Rather than holding a pointer to memory that may not be valid, the uiBut must allocate space and copy the tooltip text. This leaves the uiBut responsible for freeing the memory when it is no longer needed.
A caching mechanism similar to how strdata is handled in uiBut could be implemented.</typolist>
This is an example of the trashed tooltip problem
<ccode>import Blender
from Blender.Draw import *
from Blender.BGL import *
# this bit causes problems because the tooltips end
# up being parsed out of this list and become local to
# the draw() callback.
listOfNames = ["aa-tooltip1","bb-tooltip2","cc-tooltip3","dd-tooltip4"]
# if listOfNames looked like this, no problem:
# listOfNames=[ [["aa"], ["tooltip1"]],...
def draw():
glClearColor(0.0, 0.0, 0.0, 0.0)
glClear(GL_COLOR_BUFFER_BIT)
i = 0; height = 200
for Name in listOfNames:
buttonName = Name.split(".")[0]
buttonLabel = buttonName.split("-")[0]
buttonTtip = buttonName.split("-")[1]
Number(buttonLabel, \
1, \
100, \
height, \
100, \
20, \
0.5,\
-1, 1, buttonTtip )
print "name,LABEL AND TOOLTIP: ", \
buttonName, \
buttonLabel, \
buttonTtip
i += 1; height -= 20
def bevent(evt):
if (evt):Exit(); return
def event(evt, val):
if (evt== ESCKEY and not val): Exit(); return
else: return
Register(draw, event, bevent)</ccode>