how to update items of a previously created menu during script execution ?

I get it for props directly assigned to types.Object or Scene etc, but how does it work with group of property like types.Object.My_group.xxx or types.Scene.My_group.My_subGroup.xxx ?

I try to adapt the method described below here to my script to update my property fields while running :

class MyPropGroup(bpy.types.PropertyGroup):
    pass
MyPropGroup.my_float = bpy.props.FloatProperty()

it’s from here : http://wiki.blender.org/index.php/Dev:2.5/Py/API/Overview#Manipulating-Classes

two menus (bpy.props.EnumProperty) items lists are updated with a function, during my script execution. I mean it’s the goal, atm it’s updated at init and does not refresh. the menus are declared in a propertyGroup class. (in fact a sub group class)


class ChessTheme(bpy.types.PropertyGroup) :
    '''
    '''
    path = bpy.utils.script_paths('addons/io_pgn/themes/')[0]
    
    used_board  = 'default'
    used_pieces = 'default'
    <b>thm</b> = themesRefresh(path)
    <b>board  </b>= bpy.props.EnumProperty( items = <b>thm</b>['board'],  default = used_board, name = "Board",  description = "" )
    <b>pieces </b>= bpy.props.EnumProperty( items = <b>thm</b>['pieces'], default = used_pieces, name = "Pieces", description = "" )

class Chess(bpy.types.PropertyGroup) :
    '''
    '''
    theme = bpy.props.PointerProperty(type=ChessTheme)
    # sys
    path = bpy.utils.script_paths('addons/io_pgn/')[0]
    [...]
    my_float = bpy.props.FloatProperty(name="Some Floating Point", default=3)

the classes are registered under types.Scene, chess and chess.theme

but I can’t update my class props. sure I can read it from console or from the script:

&gt;&gt;&gt; bpy.context.scene.chess.my_float
-192.04258728027344
&gt;&gt;&gt; bpy.context.scene.chess.theme.board
'lite'

but I can’t redeclare the property to refresh the items list.


bpy.types.Scene.chess<b>?????</b> = bpy.props.EnumProperty( items = <b>thm</b>['pieces'], default = used_pieces, name = "Pieces", description = "" )

the example should mention the api path before MyPropGroup :

bpy<b>.???.</b>MyPropGroup.my_float  = bpy.props.FloatProperty()

thx atom, grabbing… I like your #

Add these properties to every object in the entire Blender system (muha-haa!!)

:slight_smile:

ok I had a look, I found the

row.template_list(ob, "ot_List", ob, "ot_List_Index",4,4,'DEFAULT')

you use to control the ‘managed target’ ui with add and remove buttons, and use of collection
(2.5 api really rocks about this ui concept…)

unfortunately I don’t think it fits to my need.
which is, simply said, redeclare an enumproperty with new items while running.
maybe I missed something else in your script ?

MacKraken’s code does work. You just have to run it everytime you change the list. This kind of code is ideal for placing in a frame change event or modal timer.


import bpy

#quotes a string
def qu(cad):
    return "'"+cad+"'"

#returns a tuple with object's names
def objectsnames():
    
    items = "("
    for i, obj in enumerate(bpy.context.scene.objects):         
        items+="("+qu(str(i))+","+qu(obj.name)+",''), "
            
    items = items[0:-2]+")"
    
    myitems = eval(items)
    
    return myitems


class OBJECT_PT_hello(bpy.types.Panel):
    bl_label = "Hello World Panel"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "object"

    myitems = objectsnames()
    bpy.types.Scene.list = bpy.props.EnumProperty(name="Objects",items=myitems)
    
    def draw(self, context):
        layout = self.layout

        obj = context.object

        row = layout.row()
        row.label(text="Hello world!", icon='WORLD_DATA')

        row = layout.row()
        row.label(text="Active object is: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")

        row = layout.row()
        row.prop(context.scene, "list")

def register():
    bpy.utils.register_class(OBJECT_PT_hello)
def unregister():
    bpy.utils.unregister_class(OBJECT_PT_hello)

if __name__ == "__main__":
    register()

Run the code and you will get a list of objects in your scene. Add an object to the scene and run the code again and the list is updated.

I already did smtg similar.
you mean… you register/unregister everytime a refresh is needed ? :confused:
you already test that in a modal script ?

I tried onorico56 code (the othe one later in the other thread) with the init way but :

location:&lt;unknown location&gt;:-1
Traceback (most recent call last):
  File "\Text", line 35, in __init__
  File "\Text", line 18, in objectsnames
AttributeError: pyrna_struct_meta_idprop_setattro() can't set in readonly state 'Scene.listObj'

( with 2.58 )

I did not unregister, I just ran it twice. It only needs to register the first time.

MacKraken’s code does work. You just have to run it everytime you change the list.

in fact : I want the items to be updated DURINGthe script execution, not only at init.
I agree this work, sure, but it’s just run at register() time. I want to redeclare the props in other words.

it was ok in 2.49, by redeclaring the ‘bpy.props’ like menu variable from a function called in some occasion from def draw.

EDIT :
sorry if I missed something important and obvious for you, it’s my first week on the API. will look to modal things.
for example, I don’t see how I can reexecute the panel INIT from an event or a button yet, or from the console

What about something like this…
Every half-second the thread runs the code. The panel menu picks up on it.


import threading, time
import bpy
from bpy.props import *

def do_something():
    # just a trivial example to see its working
    global myitems
    
    try:
        myitems = objectsnames()
        bpy.types.Scene.list = bpy.props.EnumProperty(name="Objects",items=myitems)
    except:
        print ("Error in do_something")
        
class review(threading.Thread):
    
    def __init__(self):
        threading.Thread.__init__(self)
        self.daemon = True # so Blender can quit cleanly
        self.name='review'
        self.active = True
    
    def run(self):
        while self.active:
            time.sleep(0.5)
            try:
                # call functions here
                do_something()
            except Exception as detail:
                print("Review exception:", detail)
                

############################################################################
# Code for returning names, objects, curves, scenes or meshes.
############################################################################
def fetchIfObject (passedName= ""):
    try:
        result = bpy.data.objects[passedName]
    except:
        result = None
    return result

def returnAllObjectNames ():
    # NOTE: This returns all object names in Blender, not scene specific.
    result = []
    for ob in bpy.data.objects:
        result.append(ob.name)
    return result 

#quotes a string
def qu(cad):
    return "'"+cad+"'"

#returns a tuple with object's names
def objectsnames():
    
    items = "("
    i = 1
    objs = returnAllObjectNames()
    for ob_name in objs:
        items+="("+qu(str(i))+","+qu(ob_name)+",''), "
        i = i + 1
            
    items = items[0:-2]+")"
    
    myitems = eval(items)
    
    return myitems


myitems = objectsnames()
bpy.types.Scene.list = bpy.props.EnumProperty(name="Objects",items=myitems)


class OBJECT_PT_hello(bpy.types.Panel):
    bl_label = "Hello World Panel"
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "object"
    
    def draw(self, context):
        layout = self.layout

        obj = context.object

        row = layout.row()
        row.label(text="Hello world!", icon='WORLD_DATA')

        row = layout.row()
        row.label(text="Active object is: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")

        row = layout.row()
        row.prop(context.scene, "list")

if __name__ == "__main__" :    
    bpy.utils.register_class(OBJECT_PT_hello)
     
    # shut down any existing threads
    for t in threading.enumerate():
        if t.name == 'review':
            t.active = False
    
    r = review()
    r.start()

:slight_smile: maaaany thanks Atom :slight_smile:
sry for annoying you about that.
I’ll have a deep look at it

I get it Atom, thanks again. I got another issue in fact, a particular case for which I have a work-around but I explain below, maybe someone will be interested :slight_smile:

the threading and the refresh thing are interesting, thx for that. ok for modal. I don’t get it right for context usages but on my way.

also I understand better how the invoke function can work great in similar case but with operators (for example a dropdown in a popup) : the ‘refresh items’ function can be put in an invoke function of an operator class ok ok… nice…

the issue I have is that my enumproperties are declared in a propertyGroup class :

class ChessTheme(bpy.types.PropertyGroup) :
    '''Select another board/pieces'''
    path = bpy.utils.script_paths('addons/io_pgn/themes/')[0]
    library = <b>themesRefresh</b>(path)
    board   = bpy.props.EnumProperty( items = library['board'],  default = 'default', name = "Board",  description = "" )
    pieces  = bpy.props.EnumProperty( items = library['pieces'], default = 'default', name = "Pieces", description = "" )

so themesRefresh works one time at startup, grab some file info to feed the items.
and another main class link it as a pointer, so I can access values with

&gt;&gt;&gt; chess = bpy.context.scene.chess
&gt;&gt;&gt; chess.theme.board
'default'

&gt;&gt;&gt; chess.theme.pieces
'default'

but I don’t know how to update the enumprops since :

&gt;&gt;&gt; bpy.types.Scene.chess[0]
&lt;built-in function PointerProperty&gt;

&gt;&gt;&gt; bpy.types.Scene.chess[1]
{'type': &lt;class '__main__.Chess'&gt;, 'attr': 'chess'}

ctrl-space don’t give me the pseudo path I’d like to know, where are my board and pointers props hidden ? and I can’t redefine them (how would you say ‘path’ in english ?).
so something like…


library = themesRefresh(path)
bpy.types.Scene.chess.theme.board   = bpy.props.EnumProperty( items = library['board'],  default = 'default', name = "Board",  description = "" )

…won’t work.

ok I see now how to solve that, will ‘proxy’ it, but is there a way to redefine props declared in a class directly ?