Trigger panel 'draw' from another function.

I have created a panel and on it i have a text label that I want to update from time to time from within a function. To do so, I need to call the “draw(self, context)” function.

What do I pass the function for "self, and “context” so it will fun?
I keep getting errors.

Thanks.

You shouldn’t be calling draw(), blender does that for you.

You want to update the header label?

If I have a routine that takes awhile to complete, I’d like to update a progress label on my panel.

The problem with the new 2.5 panel design is that the objects in the panel are only updated when an event triggers the update, such as a mouse move. So even if you write such a routine, the progress bar would only update when you move the mouse over the panel.

Perhaps some timer could force feed a mouse event…? Then the draw routine could just poll a global that was updated by the timer for the progress display.

Try running your routine as a thread. Properties that are changed by the routine will tick over on the panel.

How do I run the routine as a thread?

Don’t panic you don’t need all that code… below is a py from the mocap madness addon i’ve been grinding away on. In the execute method of the operator MocapMadness_ComparePoses(bpy.types.Operator): it calls the posecompare routine as a thread. I had to delete the code here to be under max post length. Full code is in the link. I’m using a global xml object as a datalayer. In my case as posecompare runs it finds cycles and branches and appends them as an xml node. The draw method of the panel MMComparePosePanel(MocapMadness3DPanel, bpy.types.Panel) updates the cycle and branch count.

In principle tho it should work with custom properties as well, for instance a %completed, which “click over” when displayed in the draw method of the panel.


import bpy
from bpy.props import *
from xml.etree.ElementTree import Element
from math import radians
import MocapMadness.MMData as mocapmadness
from threading import Thread
########################################################################
'''
POSE MATCHING SCRIPT

v 0.0.0

An attempt to find best match poses in an action collection and mark 
them in actions with markers


'''
########################################################################

def compare_fcurves(fcurves1,frame1,fcurves2,frame2,knockoff=100.0,MAX_EULER_ROT_DIFF=radians(10)):
    diff = 0.0
    
    '''
    BIG SCOPE for IMPROVEMENT HERE.. need to look at locs and rots to also ditch big rots
    
    make a class method.
    '''
    root = 'pose.bones["Hips"]'
    j = 0
    #fcurve1 = fcurves1[frame_index1]
    while j < len(fcurves1)-1:
        fcurve1 = fcurves1[j]
        fcurve2 = fcurves2[j]
        if fcurve1.data_path.find(root) < 0 :            
           #print("%d,%d"%(j,len(fcurves1)))
           df = abs(fcurve1.evaluate(frame1) - fcurve2.evaluate(frame2))
           #print(df)
           if diff > knockoff:
         
               return(1000.0)
           if fcurve1.data_path.find("rotation") >= 0 and df > MAX_EULER_ROT_DIFF : #work out how to use for quaternions
               #print("rotdiff")
               return(1000.0)
           diff += df
        j += 1
    #print(diff)
    return(diff)   

def posecompare(operator,tolerance,MIN_CYCLE_LENGTH=15,FRAMESTEP=2,MAX_EULER_ROT_DIFF=radians(5.0)): #Minimum Cycle length to set up.
    
    *****************8 CODE DELETED TO MAKE ROOM FOR POST ***************************************
    

class MocapMadness3DPanel():
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'

    @classmethod
    def poll(cls, context):

        return (mocapmadness.data is not None)



class MMComparePosePanel(MocapMadness3DPanel, bpy.types.Panel):
    bl_idname = "MMComparePoses"
    bl_label = "Compare Poses"
    
    @classmethod
    def poll(cls,context):

        
        return(mocapmadness.data != None)
    
    
    def draw(self, context):
        layout = self.layout
        layout.label(text = "Cycles:%d"%len(mocapmadness.data.findall("Actions/Action/cycle")))
        layout.label(text = "Branches:%d"%len(mocapmadness.data.findall("Actions/Action/branch")))
        layout.label("Small Class")
        layout.operator("mocapmadness.posecompare")
        layout.operator("mocapmadness.posecompare",text="Clean").clean=True


class MMPlayPanel(MocapMadness3DPanel, bpy.types.Panel):
    bl_idname = "VIEW3D_PT_test_2"
    bl_label = "Panel Two"
    
    @classmethod
    def poll(self,context):
        try:
            ad = context.object.animation_data or None
            if ad:
                return True
            
        except:
            return False
        return False
    
    #Going to be used as a play mocap panel
    def draw(self, context):
        layout = self.layout
        layout.menu("mocapmadness.actionmenu")
        self.layout.label("MocapMadness Player")
        if context.object.animation_data:
            action = context.object.animation_data.action or None
        
            if action:
                layout.prop(action,"name")
        
        #self.layout.operator("mocapmadness.compareposes")


class MocapMadness_ComparePoses(bpy.types.Operator):
    if mocapmadness.settings:
        print("GOT SETTINGS")
    bl_idname = "mocapmadness.posecompare"
    bl_label = "Compare Poses"
    bl_option = "REGISTER"
    mocapmadness_name = bl_idname
    print("COMPARE POSE OPERATOR CLASS DEF")
    for cmd in mocapmadness.SetOperatorProperties("mocapmadness.posecompare"):
        exec(cmd)
    clean = BoolProperty("Clean",default=False)
    
    #self.threshold = 0.001
    
    
    def clean_compare(self,context):
        #remove all the previous branches
        Actions = mocapmadness.data.findall("Actions/Action")
        print("cleaning %d cycles"%len(Actions))
        for Action in Actions:
            children = Action.findall("*") #// all childnodes
            for child in children:
                
                Action.remove(child)
        return True
        
    
    
    def invoke(self, context, event):
        mocapmadness.SetOperatorSettings(self)        
        wm = context.window_manager
        #return wm.invoke_props_dialog(self)        
        if not self.clean:
            
            return wm.invoke_props_dialog(self)
        else:
            print("clean")
            #return wm.invoke_popup(self)
        return self.execute(context)

    
    def draw(self,context):
        if self.clean:
            return
        layout = self.layout
        layout.prop(self,"tolerance")
        layout.prop(self,"max_euler_rot_Diff")
        layout.prop(self,"max_location_Diff")
        layout.prop(self,"framestep")
        layout.prop(self,"min_cycle_length")
        layout.prop(self,"Threaded",icon="ERROR")
        #print("DRAEWWWW")
            
    def execute(self, context):
        #posecompare(tolerance,MIN_CYCLE_LENGTH=15,FRAMESTEP=2)
        self.clean_compare(context)
        if self.clean:
            return{'FINISHED'} 
        
        
        if self.Threaded:
            t = Thread(target=posecompare,args=(self,self.tolerance, \
                       self.min_cycle_length,self.framestep,radians(self.max_euler_rot_Diff)))
            t.start()
        else:
            posecompare(operator=self,tolerance=self.tolerance,FRAMESTEP=self.framestep, \
                     MIN_CYCLE_LENGTH=self.min_cycle_length,MAX_EULER_ROT_DIFF=radians(self.max_euler_rot_Diff))
        mocapmadness.PreserveSettings(self)
        #bpy.ops.graph.clean(threshold=self.threshold)
        return{'FINISHED'} 



print("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD")    


def register():
    bpy.utils.register_class(MMComparePosePanel)
    bpy.utils.register_class(MMPlayPanel)
    bpy.utils.register_class(MocapMadness_ComparePoses)
    
def unregister():
    bpy.utils.unregister_class(MMComparePosePanel)
    bpy.utils.unregister_class(MMPlayPanel)
    bpy.utils.unregister_class(MocapMadness_ComparePoses)


bpy.data.screens[‘Scripting’].areas[4].regions[2].tag_redraw()

Does the trick in v3.3 for a panel in the Text Editor in the scripting tab. You can figure out the area and region index you need by typing / iterating though

bpy.data.screens[‘Scripting’].areas[x].type

bpy.data.screens[‘Scripting’].areas[x].regions[y].type

in the interactive console since this displays the name of that area/region as they aren’t keyed like the screens are.