8
$\begingroup$

I just can't figure out how to create a custom popup which can receive data etc. What I want to do is let the user select one of the currently selected UV-layers and just hand that back to me. So I created a class like this:

class UVLayerPrompt(Operator):
    """
    Prompts the user for the uv-layer he wishes to select for each object
    """
    bl_idname = "mhmu.uvprompt"
    bl_label = "Choose the uv-layer"

    uv_layerPos = 0
    def execute(self, context):
        print('HI')
    def invoke(self, context, event):
        self.layout.template_list("UI_UL_list", #is this correct? I guess not...
            list_id="UVLayer_LIST", # I guess yes
            dataptr=context.object.data, # my guess: yes
            propname="uv_textures", # yes?
            active_dataptr=context.scene, # I could use some other location, okay
            active_propname="uv_layerPos") # I have to assign a IntegerProperty to this, right?
        return {'RUNNING_MODAL'}

after I registered my class etc.

when I call window_manager.invoke_popup(UVLayerPrompt) then it says:

TypeError: WindowManager.invoke_popup(): error with argument 1, "operator" - >Function.operator expected a Operator type, not OrderedMeta

I realized that this is not correct. Still I don't know how to react with the context (how do I popup a new instance? with python code ofc?, how do I read the chosen value from that?)

EDIT: I will try to clarify my question: I have an export script that may export several objects. For each of these objects I want to ask the user in a responsive way which uv-texture he wants to be exported. Only one layer per object should be possible and of course I have to call the popup within my script (NOT by the user) and after it run I have to know the result of it.

How far have I gotten with this by now? Using the answer below me I realized that I should use a script that is 'INTERNAL'. Secondly I think I now finally understand how Operators are supposed to work.

I figured out how to do it: if you want to call a script from inside a script you have to call bpy.ops.operator.name('INVOKE_DEFAULT')

$\endgroup$

2 Answers 2

13
$\begingroup$

invoke_props_dialog(...) seems more appropriate here. It opens a popup with an OK button, and the operator's execute() method will only be called if the user clicks this button. (The other popup types execute on every property change.)

A dynamic EnumProperty is populated automatically with all UV textures of the current object (see uvtex_items callback). It is added to the dialog as expanded prop(...), remove expand=True if you want a dropdown list instead.

invoke_* require an operator instance (object), not an operator class (type). You usually pass self, which is a reference to the operator instance:

import bpy

def uvtex_items(self, context):
    return [(t.name, t.name, t.name) for t in context.object.data.uv_textures]

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options = {'REGISTER', 'UNDO'}

    uvtex = bpy.props.EnumProperty(items=uvtex_items)

    @classmethod
    def poll(cls, context):
        return (context.object is not None and
                context.object.type == 'MESH' and
                len(context.object.data.uv_textures) > 0)

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.prop(self, "uvtex", expand=True)

    def execute(self, context):
        self.report({'INFO'}, self.uvtex)
        return {'FINISHED'}


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


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


if __name__ == "__main__":
    register()

Paste the code into Text Editor and Run Script. Then move your mouse over 3D View, hit Spacebar, search for Simple and run the Simple Object Operator.

$\endgroup$
6
  • $\begingroup$ So how would I call a new popup from outside? For each object U want to have the users choice. How would I let Blender open an instance of UVLayerPrompt? $\endgroup$ Jan 25, 2014 at 3:56
  • $\begingroup$ So you want to open a sequence of popups, to let the user pick a UV map, with no option to abort this process? Or let the user click some button for each object? Not sure what you mean with uvtex_items, functions are first-class citizens in python. My code requires the active object to be a mesh object with at least one UV map, or Simple Object Operator won't appear in the spacebar menu list. $\endgroup$
    – CodeManX
    Jan 25, 2014 at 10:18
  • $\begingroup$ I clearified the question a bit (in EDIT:) $\endgroup$ Jan 25, 2014 at 15:50
  • $\begingroup$ Ah ok, yeah. Well, you need to use a different execution context for a dialog, since its modal handler is added in the invoke method, not execute (but execute is called by default if you call a bpy operator in script, thus the action is taken but without user interaction). $\endgroup$
    – CodeManX
    Jan 26, 2014 at 0:41
  • $\begingroup$ One could also use col.prop_search(self, 'uvtex', context.object.data, 'uv_textures') instead of col.prop(self, 'uvtex', expand=True). This way the user can search for specific names if he has many items. $\endgroup$ Jan 26, 2014 at 17:41
0
$\begingroup$

There seems to be a bug in 2.8, so I fixed it.

    def update(self, context):

If you modify the "update" function, the process will be done for each selection.

import bpy


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options = {'REGISTER', 'UNDO'}
    
    def uvtex_items():
        a=0
        items = []
        for t in bpy.context.object.data.uv_layers:
            # When adding an icon, it is necessary to add the item number name as well.
            items.append((t.name, t.name, "description","UV",a)) 
            a+=1
        return items
    ietms=uvtex_items()
    
    def update(self, context):
        print('###0-0###UPDATE', self.uvtex)
    
    uvtex:bpy.props.EnumProperty(name="test", items=ietms, update=update)

    @classmethod
    def poll(cls, context):
        return (context.object is not None and
                context.object.type == 'MESH' and
                len(context.object.data.uv_layers) > 0)

    def invoke(self, context, event):    
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.prop(self,"uvtex", expand=True)
        
    def execute(self, context):       
        self.report({'INFO'}, self.uvtex)
        return {'FINISHED'}


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


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


if __name__ == "__main__":
    register()
    
bpy.ops.object.simple_operator('INVOKE_DEFAULT')

enter image description here

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .