3
$\begingroup$

Is there a way to use the Blender Text Editor for adding a custom menu and not get a duplicate every time I click on "Run Script" to test the menu? For example, in the text editor, load the template like so:

Template -> Python -> UI Menu

Then notice every time you press "Run Script" you get another menu in your main menu bar. How can I update my menu without getting a duplicate?

I tried simply commenting out register() but then new updates don't show up.

$\endgroup$
3
$\begingroup$

Execute Reload Scripts F8 first before re-running the script.

I think a custom menu function is supposed to run within an addon, where register() and unregister() are run in pairs. Each script execution yields different instance of each functions, even if their name doesn't change across each execution. So unless there is a way to check existing draw functions within a class by name (which I haven't found yet), Reload Scripts or restarting Blender is the way to go.

$\endgroup$
  • $\begingroup$ F8 can remove appended/prepended draw callbacks, but not remove draw handlers or timers (as unregister() is not called) - see bug tracker. $\endgroup$ – CoDEmanX Oct 19 '13 at 0:26
  • $\begingroup$ That obviously limits F8's use. At least it works for removing callbacks, as in @PolyMesh's case. $\endgroup$ – Adhi Oct 19 '13 at 1:00
2
$\begingroup$

Running a script from Text Editor won't call unregister() before registering all classes / the module again. Therefore, another draw callback is added to the header.

You could remove all extra draw funcs from the header like so:

def register():
    bpy.utils.register_class(CustomMenu)

    if hasattr(bpy.types.INFO_HT_header.draw, "_draw_funcs"):
        for f in bpy.types.INFO_HT_header.draw._draw_funcs:
            # could also test e.g. _draw_funcs[#].__code__.co_filename to be sure
            if not repr(f).startswith("<function INFO_HT_header.draw"):
                bpy.types.INFO_HT_header.draw._draw_funcs.remove(f)

    bpy.types.INFO_HT_header.append(draw_item)

or even

    if hasattr(bpy.types.INFO_HT_header.draw, "_draw_funcs"):
        func = (f for f in _draw_funcs if repr(f).startswith("<function INFO_HT_header.draw").__next__()
        del bpy.types.INFO_HT_header.draw._draw_funcs
        bpy.types.INFO_HT_header.draw = func
$\endgroup$
  • $\begingroup$ So, if not run through addon mechanism, or for testing purposes, is it better to redefine the draw handler (draw = func) rather than appending an extra callback function? $\endgroup$ – Adhi Oct 19 '13 at 1:04
  • $\begingroup$ There is no draw handler in the code, see API docs. No, you shouldn't replace the draw function instead of appending a draw callback. It would remove the stock header and only what you replace it with would show up. I don't think that's what you want. Another approach, similar to my above code snippets, could be to find your own draw function in _draw_funcs (only exists if you added a custom draw func before). Then replace just this one with a new function. $\endgroup$ – CoDEmanX Oct 19 '13 at 1:14
  • $\begingroup$ I really like this solution, though it does feel a bit hacky. I do like the idea of only switching out your own function rather than deleting them all. It looks like I can compare against draw function names (as long as I have a relatively unique function name) using drawFuncPointer.__name__ $\endgroup$ – PolyMesh Oct 20 '13 at 5:05
  • $\begingroup$ @CoDEmanX could you please explain this _draw_funcs as I don't understand? I have a function named newdraw I'm calling info_header_draw = bpy.types.INFO_HT_header.draw then bpy.types.INFO_HT_header.draw = newdraw the problem if I press the Run Script button twice for example, the header is updated twice and the newdraw function is executed and draw twice $\endgroup$ – Tak Apr 16 '17 at 23:50
1
$\begingroup$

One way around this is to make your changes and save the script, restart Blender and run the script again.

$\endgroup$
1
$\begingroup$

Based on the answers already given, it really seems best practice to fully unregister before registering again to avoid the duplication I was getting.

Although the other answers do answer my question, it seems like the best answer, with the least amount of hacky, is to not to use the "Run Script" button at all for these types of register/unregister ui modifying scripts.

Instead one should have their file on disk in the addon directory. Once the script is loaded with the User Preferences -> Addon manager, you can use the F8 trick (thank you Adhi) to reload scripts which does cause both unregister and register to run.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.