EN VI

Python - TypeError: 'StringVar' object is not iterable -- tkinter ListBox()?

2024-03-16 02:00:10
Python - TypeError: 'StringVar' object is not iterable -- tkinter ListBox()

I'm building a Python version of a popular party game, and I'm using tkinter to build the setup GUI with player name and game preferences.When I run the code the window opens and looks right except for one critical thing.

There are radio buttons to select the type of deck, and there's a listbox that's meant to display the names of the decks of cards in the game, that should update whenever a radio button is clicked.

Getting the list of deck names worked fine on the command line, but I can't make it work in the GUI.

The problem is that when the window loads only the first name in the deck list is shown. When I click a radio button I get an error the console.

TypeError: 'StringVar' object is not iterable

I'm sure there's something simple that I'm missing. Here's the relevant code:

master_deck_list = []
deck_type_selection = tk.StringVar(value="official")

    with open('cah-cards-full.json', encoding="utf8") as c:
                    master_deck_list = json.loads(c.read())
    
    def get_available_decks(master_deck_list, deck_type_selection):
        # Assign the list of deck names to deck_list
        decks_list = []
        
        for av_deck in master_deck_list:
            if deck_type_selection == 'official':
                if av_deck['official'] == True:
                    decks_list.append(av_deck['name'])
            elif deck_type_selection == 'community':
                if av_deck['official'] == False:
                    decks_list.append(av_deck['name'])
            elif deck_type_selection == 'all':
                decks_list.append(av_deck['name'])
    
            return decks_list
    
    decklist = get_available_decks(master_deck_list, deck_type_selection="official")
    list_items = tk.StringVar(value=decklist)
    
    def update_decklist(list_items):
        for item in list_items:
            listbox.insert(tk.END, item)
    
    ...
    
    # Deck type radio buttons
    radio_1 = tk.Radiobutton(decktypes_frame, text="Official", variable=deck_type_selection, value="official", command=lambda: update_decklist(list_items))
    radio_1.grid(column=0, row=0, padx=5, pady=5)
    radio_2 = tk.Radiobutton(decktypes_frame, text="Community", variable=deck_type_selection, value="community", command=lambda: update_decklist(list_items))
    radio_2.grid(column=1, row=0, padx=5, pady=5)
    radio_3 = tk.Radiobutton(decktypes_frame, text="All", variable=deck_type_selection, value="all", command=lambda: update_decklist(list_items))
    radio_3.grid(column=2, row=0, padx=5, pady=5)
    
    # decksframe holds the available and selected decks lists
    decksframe = tk.LabelFrame(main, text="Available Decks")
    decksframe.columnconfigure(0, weight=3)
    decksframe.columnconfigure(1, weight=3)
    
    # Available Decks
    listbox = tk.Listbox(decksframe, selectmode="multiple", listvariable=list_items, exportselection=0)
    listbox.grid(column=0, row=0, padx=5, pady=5)

Here's the full traceback (thanks, Michael):

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.8_3.8.2800.0_x64__qbz5n2kfra8p0\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "setup.py", line 63, in <lambda>
    radio_2 = tk.Radiobutton(decktypes_frame, text="Community", variable=deck_type_selection, value="community", command=lambda: update_decklist(list_items))
  File "setup.py", line 40, in update_decklist
    for item in list_items:
TypeError: 'StringVar' object is not iterable

Solution:

The error message means exactly what it says. list_items is not a list and it's not a string, it's a StringVar. You can't do for item in list_items because a StringVar isn't something that supports iteration.

The solution seems to be to simply set list_items to decklist (or just throw away list_items and use decklist directly). Then, just insert the items into the listbox rather than using listvariable. For example:

listbox = tk.Listbox(decksframe, selectmode="multiple", exportselection=0)
listbox.insert("end", *decklist)

listvariable works much better when using Tcl rather than python, since tkinter doesn't have a built-in ListVar object type.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login