PoserPython: AskMenu() - best way to get the actual list index of the selection ?



  • When using poser.DialogSimple.AskMenu("Title","Message",listofitems)* it returns the option in the listofitems that the user selected. Which is text. So I then loop through that list comparing the returned text with each list item in order to find the list index.

    There must be a better way ?

    Here's a snippet of my ropey (as in "held together with string and chewing gum", but the string wasn't strong enough so I used rope instead!) python:

    			
    		# Get a list of all parameters in the selected actor and let the user select the one they want to slave
    		parmtoslave = 0
    		plist = curact.Parameters()
    		asklist = []
    		if justshowvisibledials:
    			print "Building list of just visible parameters"
    			for i in range(len(plist)):
    				if not plist[i].Hidden():
    					asklist.append(plist[i].Name())
    		else:
    			print "Building list including hidden parameters"
    			asklist = [plist[i].Name() for i in range(len(plist))]
    		
    		asklistitem = poser.DialogSimple.AskMenu("Select Dial To Slave","'"+curact.Name()+"' dials:",asklist)
    		if asklistitem == None: # 'Cancel' selected
    			poser.DialogSimple.MessageBox("'Cancel' button clicked\nScript cancelled, no changes made.")
    		elif asklistitem == "": # Default 'None' selected
    			poser.DialogSimple.MessageBox("Default option 'None' selected\nScript cancelled, no changes made.")
    		else:
    			poser.DialogSimple.MessageBox("'"+asklistitem+"' selected.")
    			idx=0
    			for parm in plist:
    				if parm.Name() == asklistitem:
    					parmtoslave = parm
    				idx=idx+1
    		if parmtoslave:
    			print "You've selected to slave the '"+parmtoslave.Name()+"'dial of the '"+curact.Name()+"' actor"
    		else:
    			print "No parameter has been selected to slave - script will abort"
    
    

    *P.S. the (PP2014) manual's got it wrong - the menu items should be an array (list? whatever Python calls that thingy with square brackets - 'arrayorlist = [] # create an empty arry (or list?whatever Python calls that thingy with square brackets - ...etc... ad infinitum...'):

    AskMenu
    Explanation: Ask the user to select an item in a menu.
    Arguments: Enter the menu title, the request message, and each of the subsequent items in the menu.
    Syntax: <StringType> AskMenu(<StringType>title, <StringType> message, <StringType> item1,
    <StringType> item2, ...)

    PP2014 debug:
    asklistitem = poser.DialogSimple.AskMenu("Select Dial To Slave","'"+curact.Name()+"' dials:","Fred","Bert","AlbertRoss")
    TypeError: AskMenu() takes at most 3 arguments (5 given)



  • @3dcheapskate here's the way I've done it in the past:

    BakeMenu = [ 	'Bake All Morphforms', \
    		    	'Bake Arm Morphforms', \
    		    	'Split Arm Morphforms', \
    		    	'Bake Leg Morphforms', \
    		    	'Split Leg Morphforms', \
    		    	'Bake Torso Morphforms', \
    		    	'Bake Helper Bones' \
    		   ]
    BakeOrder = {}
    # Initialise BakeOrder dict. Each of the RHS assignments are collections.OrderedDict()
    BakeOrder[ BakeMenu[ 0 ] ] = BakeAllOrder
    BakeOrder[ BakeMenu[ 1 ] ] = BakeArmOrder
    BakeOrder[ BakeMenu[ 2 ] ] = SplitArmOrder
    BakeOrder[ BakeMenu[ 3 ] ] = BakeLegOrder
    BakeOrder[ BakeMenu[ 4 ] ] = SplitLegOrder
    BakeOrder[ BakeMenu[ 5 ] ] = BakeTorsoOrder
    BakeOrder[ BakeMenu[ 6 ] ] = BakeHelperOrder
    prompt = 'Select Baking Method'
    choice = poser.DialogSimple.AskMenu( prompt, prompt, BakeMenu )
    if choice == '': # Use default
    	choice = default
    if choice in BakeOrder.keys():
    	...
    

    BakeMenu is a list (the thing in square brackets you pass to AskMenu as the last (3rd) parameter).
    Each item (a quote delimited string) in the list is used as a dict key and also as the visible menu items the User chooses from.
    In this example, the dict (BakeOrder) values are OrderedDicts, but they could actually be methods (Python functions declared by "def MethodName()"), and as such, a call of

    BakeOrder[ choice ]()
    

    would act as a dispatcher, and execute the method chosen by the user.

    In your particular example, where you want to find the list index, you could just initialise the values of the dict with the list indices, like

    for index in xrange( len( BakeMenu ) ):
        BakeOrder[ BakeMenu[ index ] ] = index
    

    then

    chosenIndex = BakeOrder[ choice ]
    if BakeMenu[ chosenIndex ] == choice:
        print 'Found it'
    


  • I love to keep things simple. So I wrote a little wrapper:

    def MyAskMenu(title, message, alist):
        names = [ac.Name() for ac in alist]
        result = poser.DialogSimple.AskMenu(title, message, names)
        if not result:
            return None
        idx = names.index(result)
        return alist[idx]
    

    With this I can use Poser lists:

    figure = MyAskMenu("test", "Select a figure", poser.Scene().Figures())
    if figure:
        actor = MyAskMenu("test", "Select an actor",
                          [ac for ac in figure.Actors() if ac.IsBodyPart()])
        if actor:
            print("Selected actor:", actor.InternalName())
            param = MyAskMenu("test", "Select a parameter",
                              [parm for parm in actor.Parameters()
                               if parm.IsMorphTarget() or parm.IsValueParameter()])
            if param:
                print("Selected Parameter:", param.Name(), param.Value())
    
    

    To use other things I made myself a class:

    class MenuEntry(object):
        def __init__(self, name, **args):
            self.name = name
            for k, v in args.items():
                setattr(self, k, v)
    
        def Name(self):
            return self.name
    
        def __str__(self):
            return str(self.__dict__)
    

    With this class I can use MyAskMenu() like this:

    result = MyAskMenu("free list", "Select an entry",
                       [MenuEntry("Label 1", a=3, b=4),
                        MenuEntry("Label 2", abc=34),
                        MenuEntry("Label 3")]
                       )
    print(result)
    


  • Rather than start a new topic, since this discovery is directly relevant to AskMenu(), I'm posting it here.

    Using a preference menu to record a default choice for a menu, to be selected if Return is pressed in the AskMenu dialog without choosing a selection (you can always click Cancel if you don't want to proceed), I was frustrated that Poser would truncate my menu prompt in the dialog with ellipsis if the name was too long. Like this:

    Select Camera to Clone [Do...
    

    While scratching my head over whether it would be possible to hijack and resize the dialog, it occurred to me that there might be some escape characters that the text could contain which might pertain to formatting, such as inserting a newline ('\n') to try and put the subsequent text on another line below. Upon trying that very thing, (which didn't do what I'd hoped), the whole text was instead presented without truncation, which is a useful trick, giving:

    Select Camera to Clone [Dolly Camera]:
    

    which still fit within the dialog! (The '\n' was between the word 'Clone' and the first square bracket '[')

    This works in the prompt (second) text parameter, but may also work in the title (first) parameter of AskMenu().

    I have vague memories that this has been discussed before, but can't remember where, so thought I'd share it here.



  • have you thought about trying wx python?

    this routine will get the indices efficiently

    import  wx
    
    def Choose( title = '' , prompt = '' , OptionList = [] ):
    
        dialog = wx.MultiChoiceDialog( None, prompt, title, OptionList )
    
        if dialog.ShowModal() == wx.ID_OK:
    	    selections = dialog.GetSelections()
    	    strings = [OptionList[x] for x in selections]
    	    return (selections, strings)
    
         else:
    	return (False, False)
        dialog.Destroy()
    
    (selections, strings) = Choose( "test", "choose a color", ["red", "green", "yellow","pink"] )
    
    if ( selections, strings ):
        for s in range(len(selections)):
    	print selections[s]
    	print strings[s]
    

    Example output :

    0_1560485363620_2350553a-f6c1-47dc-a1f9-af00a4c1effe-image.png
    0_1560485473482_964718e1-7356-40f0-ac78-2977c7688cc7-image.png



  • oh nvm - it seems I am about a year too late :)



  • @structure not so, it's always relevant to have examples posted, especially when one is searching topics for specific text. It's a lot easier to glean relevant information if it's collected in a few, on-topic threads. :-)



  • @structure
    For me you were just in time. This is exactly what I was looking for for the project now at hand. Had it ben posted a year ago I would have missed it. Many tanks.

    I think by the way the code has a small inconsistency:
    The definition of the Choose function reads:
    0_1560498749923_Knipsel.JPG

    however the function called is named 'Choose2' (Because it returns a tuple and not just the selections, maybe?).

    0_1560498634736_Knipsel.JPG

    I assume this is just an oversight in the clean-up of the code.



  • @anomalaus it is indeed
    @f_Verbaas yeah - and edited it - glad it can be helpful :)



  • @structure I actually tried tkInter and wxPython years ago but for some reason my brain didn't like either. I still prefer the old, poser.DialogSimple stuff.
    But wxPython is definitely a sensible solution.



  • @structure
    The selection box in action to allow the user to pick the morphs he wants to export as a basis for morphs in conforming clothing.
    0_1560616140744_Knipsel.JPG
    works like a charm.



  • @F_Verbaas glad you got it working :) looks good.