3
$\begingroup$

I would like to set various parameters when I open a panel. By parameter I mean toggling the bounds for an object or changing object.display_type etc. Is there a python way of calling some code (method, script, operator, handle etc.) when I open or close a panel?

$\endgroup$
2
  • 3
    $\begingroup$ I imagine it would be useful to be more specific about the circumstances you’re interested in as “a panel” is quite generic. Do you mean a tool panel? A panel in the tool shelf? A panel in the properties? A panel from an operator (either a custom one (your own or someone else’s?) or a pre-built one). Providing as much detail as possible as to the specific area you’re interested in will drastically improve the chances of someone being able to pick this up and provide an answer. $\endgroup$ – Rich Sedman Jan 13 at 11:50
  • $\begingroup$ Thanks for the comment. I didn't really know there are much difference in the panels. I used to put my panels in the properties space, but I changed them to the 3D view tool-tab. They are part of a custom bigger addon I am working on. $\endgroup$ – DrDress Jan 13 at 12:42
3
+50
$\begingroup$

Spit data from draw method to a draw call back.

enter image description here

When playing around with draw callbacks noticed that can modify objects, whereas you cannot from a panel draw method.

Here is an example Is automatic "Reload from disk" confirmation in Text Editor possible?

A panel can be registered as default closed. In this case only the poll and draw header method will be called. When it is expanded we see the content of the draw method. Explained here.

For convenience the drawing class is added to the driver namespace. As explained in How do you remove a draw handler after it's been added? See also answer to operator error when open a new file re setting up a on the window manager.

Test script: defines a draw callback class and a panel (default closed). The drawing class has a list being fed from the poll and draw methods of the panel. The list is kept to 5 members. If the panel is being drawn it sees this and sets the active object to 'TEXTURED' else to 'WIRE'.

import bpy
import blf


class DrawingClass:
    def __init__(self, context, prop):
        self.status = [prop]
        self.prop = prop
        self.handle = bpy.types.SpaceView3D.draw_handler_add(
                   self.draw_text_callback,(context,),
                   'WINDOW', 'POST_PIXEL')

    def draw_text_callback(self, context):
        font_id = 0  # XXX, need to find out how best to get this.

        # draw some text
        blf.position(font_id, 15, 50, 0)
        blf.size(font_id, 20, 72)
        blf.draw(font_id, "%s %s" % (context.scene.name, ",".join(self.status)))
        ob = context.object
        if ob:
            ob.display_type = 'TEXTURED' if "Open" in self.status else 'WIRE'

    def remove_handle(self):
        bpy.types.SpaceView3D.draw_handler_remove(self.handle, 'WINDOW')         

class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_options = {'DEFAULT_CLOSED'}

    def __init__(self):
        print("init")

    @classmethod 
    def poll(self, context):
        print("poll")

        dc = dns.get("dc")
        if dc:
            while len(dc.status) > 4:
                dc.status.pop()
            dc.status.insert(0, "Closed")

        return True    
        
    def draw(self, context):
        print("draw")
        layout = self.layout
        dc = dns.get("dc")
        if dc:
            dc.status.insert(0, "Open")
        layout.label(text="Open")

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


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()
    

    context = bpy.context             
    dns = bpy.app.driver_namespace
    dc = dns.get("dc")
    if not dc:
        dns["dc"] = DrawingClass(context, "Closed")

Have used similar for a panel "heat" addon, where a counter is added to the panel class for every draw call. After a while "cold" panels, are closed, moved or unregistered.

$\endgroup$
0
1
$\begingroup$

Notice: You must save the script to __init__.py and put it in a file to zip it.
The file name you can change it for zip.
If you install it without zip, it doesn't work.

import bpy
from bpy.app.handlers import persistent
bl_info = {
    "name" : "Test Call in start",
    "author" : "Public",
    "version" : (1, 0),
    "blender" : (2, 90, 0),
    "location" : "View3d > Tool",
    "category" : "3D View",
}

def get_user_prefs(context):
    if hasattr(context, "user_preferences"):
        return context.user_preferences

    return context.preferences

@persistent
def load_handler(dummy): # you can do something when blender open
    P = get_user_prefs(bpy.context).addons[__package__].preferences
    P.update_panel_category(bpy.context)

class MY_Preferences(bpy.types.AddonPreferences):
    bl_idname = __package__

    def update_panel_category(self, context):
        has_panel = hasattr(bpy.types, My_Panel.bl_idname)
        if has_panel:
            try:
                bpy.utils.unregister_class(My_Panel)
            except:
                pass
        My_Panel.bl_category = self.my_category
        bpy.utils.register_class(My_Panel)

    my_float: bpy.props.FloatProperty(name = "My Float", default = 1.0,)
    my_category: bpy.props.StringProperty(name = "Category", default = "Item",
        update = update_panel_category,
    )
    my_message: bpy.props.StringProperty(name = "message", description = "message", default = '')

class My_Panel(bpy.types.Panel):
    bl_idname = "MY_PT_Panel"
    bl_label = "My Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Item"

    def draw(self, context):
        layout = self.layout
        P = get_user_prefs(bpy.context).addons[__package__].preferences

        layout.row().operator("message.messagebox", text = "")
        layout.row().prop(P, "my_category")

class MessageBox(bpy.types.Operator):
    bl_idname = "message.messagebox"
    bl_label = ""

    def execute(self, context):
        self.report({'INFO'}, "do something when you press 'ok'")
        return {'FINISHED'}
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self, width = 400)

    def draw(self, context):
        P = get_user_prefs(bpy.context).addons[__package__].preferences
        layout = self.layout
        layout.label(text="my float")
        layout.prop(P, 'my_float', text="")

classes = (MY_Preferences, My_Panel, MessageBox,)
def register():
    for c in classes:
        bpy.utils.register_class(c)
    bpy.app.handlers.load_post.append(load_handler)
def unregister():
    for c in classes:
        bpy.utils.unregister_class(c)
    bpy.app.handlers.load_post.remove(load_handler)
if __name__ == "__main__":
    register()
$\endgroup$
4
  • 1
    $\begingroup$ Can also look at Panel.is_registered see blender.stackexchange.com/a/202896/15543 For a single python file addon would instead call it test_addon.py (or some such) rather than going thru the rigmarole of zipping and making a zip test_addon.zip to make a folder named test_addon to put a single __init__.py file into. Do not ever place an addon named __init__.py directly into any blender addons folder. $\endgroup$ – batFINGER Jan 15 at 7:09
  • 1
    $\begingroup$ btw not sure that this fits the question remit in that (I assume) OP wants to do something when the panel is expanded as opposed to registered. $\endgroup$ – batFINGER Jan 15 at 7:20
  • $\begingroup$ @ batFINGER. Exactly right. Both multifile addons and running code on startup are questions I asked priviously. But is not what I meant in this post. $\endgroup$ – DrDress Jan 15 at 9:00
  • $\begingroup$ Hmm. I am told that the bounty was auto awarded. I thought that it was given when I accepted the answer. Which I did. Thanks batFINGER! $\endgroup$ – DrDress Jan 20 at 9:15

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.