pywinauto

Need help in selecting an item from a combobox, have tried MANY solutions


Ok team, I need help with this one, I'm at a loss. I will not be able to provide a working example as the GUI I'm using you likely don't have (it's the TradeZero ZeroPro's stock/options trading desktop program that I use to place trades manually, would like to automate it), so I will do my best to provide screenshots and code samples so I can get some direction regardless.

This is what the window looks like:

WindowInQuestion

Without going into too much here, the dropdown/combobox in question is the Strike. It's not a text-editable box, it only wants a selection from the dropdown, which looks like this when expanded:

StrikeComboBoxExpanded

As you can see, there's a scroll wheel available as there's too many items. The item I want to select is "430.00", which is about half way down that list.

I will show you the code that I've created with the number of attempts I've tried and their results. I've also printed the outputs of the element's properties to help give you some insight. I'm at a loss for how to "select" 430.00 from that dropdown menu, between pywinauto constantly changing its focus to the Root text box, and being at it for hours, I'm stuck!

    # Format the strike_price as a string with two decimal places, like the list items in the dropdown menu
    strike_price_str = "{:.2f}".format(strike_price)
    print(strike_price_str)


>>> 430.00


    # Inspect the dropdown menu's properties
    strike_dropdown_element = option_chain_window_element["StrikeComboBox"]
    print("Dropdown list's combo box properties:")
    print(strike_dropdown_element.get_properties(), "\n")


>>> Dropdown list's combo box properties:
>>> {'class_name': 'WindowsForms10.Window.8.app.0.3ddfb6e_r7_ad1', 
>>>'friendly_class_name': 'ComboBox', 
>>>'texts': ['', ''], 
>>>'control_id': 81007534, 
>>>'rectangle': <RECT L22, T462, R107, B484>, 
>>>'is_visible': True, 
>>>'is_enabled': True, 
>>>'control_count': 2, 
>>>'is_keyboard_focusable': True, 
>>>'has_keyboard_focus': False, 
>>>'automation_id': 'comboStrike'}
    

    # Inspect the texts of the dropdown menu's list items
    texts = strike_dropdown_element.texts()
    print(texts)


>>> ['370.00', '380.00', '382.00', '383.00', '384.00', '385.00', '386.00', '387.00', '388.00', '389.00', '390.00', '391.00', '392.00', '393.00', '394.00', '395.00', '396.00', '397.00', '398.00', '399.00', '400.00', '401.00', '402.00', '403.00', '404.00', '405.00', '406.00', '407.00', '408.00', '409.00', '410.00', '411.00', '412.00', '413.00', '414.00', '415.00', '416.00', '417.00', '418.00', '419.00', '420.00', '421.00', '422.00', '423.00', '424.00', '425.00', '426.00', '427.00', '428.00', '429.00', '430.00', '431.00', '432.00', '433.00', '434.00', '435.00', '436.00', '437.00', '438.00', '439.00', '440.00', '441.00', '442.00', '443.00', '444.00', '445.00', '446.00', '447.00', '448.00', '449.00', '450.00', '451.00', '452.00', '453.00', '454.00', '455.00', '456.00', '457.00', '458.00', '459.00', '460.00', '461.00', '462.00', '463.00', '464.00', '465.00', '466.00', '467.00', '468.00', '469.00', '470.00', '471.00', '472.00', '473.00', '474.00', '475.00', '476.00', '477.00', '478.00', '479.00', '480.00', '481.00', '482.00', '483.00', '484.00', '485.00', '486.00', '487.00', '488.00', '489.00', '490.00', '491.00', '492.00', '493.00', '494.00', '495.00', '496.00', '497.00', '498.00', '499.00', '500.00', '505.00', '510.00', '515.00', '520.00']


    # Define the item you want to select
    item_to_select = strike_dropdown_element.child_window(title=strike_price_str, control_type="ListItem")
    print("Item to select's properties:")
    print(item_to_select.get_properties(), "\n")
    

>>> Item to select's properties:
>>> {'class_name': '', 
>>>'friendly_class_name': 'ListItem', 
>>>'texts': ['430.00'], 
>>>'control_id': None, 
>>>'rectangle': <RECT L0, T0, R0, B0>, 
>>>'is_visible': True, 
>>>'is_enabled': True, 
>>>'control_count': 0, 
>>>'is_keyboard_focusable': False, 
>>>'has_keyboard_focus': False, 
>>>'automation_id': '[Editor] [valuelist] ValueListItem 50'}



    # MAYBE SOME THINGS TO NOTE HERE ARE THAT IT IS VISIBLE, ENABLED, NOT KEYBOARD FOCUSABLE?



    # START OF ATTEMPTS
    # attempt 0.5 - scrolling with down arrow, VERY SLOW, even with all individual
    # Timings set to 0! The gui keeps auto-focusing on the first text box in the top
    # left of the window for some reason, so I have to keep re-setting the focus on
    # the dropdown menu. This is very annoying, and makes the process extremely slow.
    # It "scrolls down" one option every 2-3 seconds because of this weird behaviour, 
    # and this dropdown has many elements in it so this is not an acceptable solution.
    texts = strike_dropdown_element.texts() # gets all texts available in combo box
    while True:
        strike_dropdown_element.set_focus()
        if strike_dropdown_element.texts()[0] == strike_price_str:
            break
        else:
            strike_dropdown_element.type_keys("{DOWN}", pause=0.1)
            continue
    
    
    
    # attempt 1 - Click the dropdown menu to expand it
    strike_dropdown_element.click()

>>> "AttributeError: Neither GUI element (wrapper) nor wrapper method 'click' were found (typo?)
    
    
    
    # attempt 2 - try click_input() instead
    strike_dropdown_element.click_input()
    # This is hard to describe. This works to expand the dropdown, 
    # however any input made after this, like trying to perform "item_to_select.click_input()"
    # later results in an entirely different text box to be selected on the gui, and the focus is 
    # off of the dropdown menu. Same as in attempt 0.5 with the scrolling. I'm not telling
    # it to focus on that textbox, but it's doing it anyway. Very annoying. Anyway...

    # Click the item. See issue above. It's weird here too because the code does not crash at this point,
    # it seems to "work", but when I look at the gui, the dropdown menu is not expanded anymore
    # and the item I wanted to select isn't actually selected? So this doesn't work either.
    item_to_select.click_input()
    
    
    
    # attempt 3 - Select the item directly
    strike_dropdown_element.select(strike_price_str)
>>> _ctypes.COMError: (-2146233067, None, (None, None, None, 0, None))
    
    
    
    # attempt 4 - same as 3, but with the dropdown click_input'd first
    strike_dropdown_element.click_input()
    strike_dropdown_element.select(strike_price_str)
>>> _ctypes.COMError: (-2146233067, None, (None, None, None, 0, None))
    
    
    
    # attmept 5 - different attempt at selecting the item directly
    strike_dropdown_element.child_window(title=strike_price_str, control_type="ListItem").select()
>>> _ctypes.COMError: (-2146233067, None, (None, None, None, 0, None))
    
    
    
    # attempt 6 - same as 5, but with the dropdown click_input'd first
    strike_dropdown_element.click_input()
    strike_dropdown_element.child_window(title=strike_price_str, control_type="ListItem").select()
>>> _ctypes.COMError: (-2146233067, None, (None, None, None, 0, None))
    
    
    
    # attempt 5 - Scrolling to the item
    max_scrolls = 50
    scroll_count = 0
    while scroll_count < max_scrolls:
        # Reselect the dropdown menu element cuz pywinauto "defaults" to focusing
        # on the first available text box all the time.
        strike_dropdown_element.click_input()
        x, y = pyautogui.position()
        scroll((x,y), -5 * scroll_count) # Scroll down by 5 units * whatever the current count is
        # Click the item to select it after scrolling
        try:
            item_to_select.click()
            break
        except Exception as e:
            scroll_count += 1
    # This doesn't work either. It should scroll down more and more each time the
    # dropdown is re-expanded, but it's not, it scrolls down once and then stops each time.
    # very annoying!

Solution

  • I figured out that, it isn't the ComboBox that I should be trying to interact with, but the EDIT element that's attached to it!

    Edit - ''    (L23, T463, R89, B483)
    ['Edit4', 'StrikeEdit']
    child_window(auto_id="[Editor] Edit Area", 
    

    Knowing that, I was able to make it work with this code:

        # Get the strike price EDIT element, and set the value to the strike price
        strike_dropdown_edit_element = option_chain_window_element["StrikeEdit"]
        # print(strike_dropdown_element.get_value())
        strike_dropdown_edit_element.set_text(str(430.00 * 100000))
    

    The values were weird with the edit element too, which I think is just a ZeroPro thing as they have the values of the prices multiplied by one hundred thousand, so that's what I send as the text value and it worked.