8

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')

CodeManX
  • 29,298
  • 3
  • 89
  • 128
WorldSEnder
  • 1,616
  • 1
  • 15
  • 37

2 Answers2

13

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.

p2or
  • 15,860
  • 10
  • 83
  • 143
CodeManX
  • 29,298
  • 3
  • 89
  • 128
  • 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? – WorldSEnder Jan 25 '14 at 03:56
  • 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. – CodeManX Jan 25 '14 at 10:18
  • I clearified the question a bit (in EDIT:) – WorldSEnder Jan 25 '14 at 15:50
  • 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). – CodeManX Jan 26 '14 at 00:41
  • 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. – WorldSEnder Jan 26 '14 at 17:41
  • How do you make it modal so that when you click outside the popup, it will not disappear so you are forced to click OK? – Harry McKenzie Feb 07 '24 at 11:25
0

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

mml
  • 443
  • 2
  • 9