0

I have created a code to create tkinter OptionMenus with values in a dictionary using a for loop. The code seems to work successfully, with OptionMenus appearing with keywords on a window as desired...

import tkinter as tk
from tkinter import *

class Example:

    def __init__(self): 
        #Dictionary with categories and their relative keywords
        self.categorykeywords={"Category 1":["Keyword 1", "Keyword 2", "Keyword 3"], "Category 2":["Keyword A","Keyword B","Keyword C"], "Category 3":["Another Keyword"]}

        #Dictionary containing the option menus referenced by category name
        btn_dict={}

        #Storing tkvar variable for later referencing
        self.dropdownreference={}

        #Number to assign to the tkvar name, to make the unique variables for each category
        i=1
        columncounter=0

        for category in self.categorykeywords:
            #Creating a unique variable / name for later reference
            exec('self.tkvar_' + str(i) + ' = ' + 'StringVar(root)') 

            #Creating OptionMenu with unique variable
            btn_dict[category] = tk.OptionMenu(root, exec('variable=self.tkvar_'+str(i)), *self.categorykeywords[category])

            btn_dict[category].grid(row=0, column=columncounter, padx=1, pady=1)

            #Storing the variable used for later use
            self.dropdownreference[category]=exec('variable=self.tkvar_'+str(i)) 

            columncounter+=1
            i+=1

root = Tk()
my_gui = Example()
root.mainloop()

However, when they are selected, I receive an error:

Traceback (most recent call last):
  File "c:\users\czuczor\appdata\local\programs\python\python36\lib\tkinter\__init__.py", line 1699, in __call__
    return self.func(*args)
  File "c:\users\czuczor\appdata\local\programs\python\python36\lib\tkinter\__init__.py", line 3434, in __call__
    self.__var.set(self.__value)
AttributeError: 'NoneType' object has no attribute 'set'

I'm guessing it's having trouble actually assigning the variable, or possibly even just displaying the selected keyword. I get the same error when attempting to use ttk.OptionMenu, which automatically displays the first value. Any ideas on how to fix this?

Erico9001
  • 75
  • 4
  • Possible duplicate of [How do I create a variable number of variables?](https://stackoverflow.com/questions/1373164/how-do-i-create-a-variable-number-of-variables) – Aran-Fey Mar 23 '18 at 15:22
  • 1
    Get rid of all that `exec` horror and learn how to use dictionaries, for the love of everything that is holy. – Aran-Fey Mar 23 '18 at 15:23
  • Do not import tkinter twice. Remove the `from tkinter import *` and just keep `import tkinter as tk`. Also its not very save to use `exec`. You should avoid this method. – Mike - SMT Mar 23 '18 at 15:23
  • 1
    @Aran-Fey, you are saying that the issue is just the exec command? Frankly, I don't care if it is 'a horror' or 'unholy'. I just need something that works. Also, I am aware of how to use dictionaries, as should be clear by the example. You haven't helped me. – Erico9001 Mar 23 '18 at 15:47
  • Yes, the issue is/are the exec command(s). Why aren't you using dictionaries instead of exec? – Aran-Fey Mar 23 '18 at 15:54
  • Saw somebody else doing it. New to python. Answered with adjusted code. – Erico9001 Mar 23 '18 at 15:59
  • It wasn't my intention to be harsh. (Rather, the harshness wasn't directed at you.) It's the irresponsible usage of `exec` and `eval` that tricks newbies like you into thinking they're the right tool for the job that drives me crazy. Can I ask where you got this `exec` idea from, so I can downvote them and revoke their programmer license? – Aran-Fey Mar 23 '18 at 17:12
  • Heh, I get it. The post I took it from did warn that a dictionary is the right approach to use, but since his answer nor any other answers provided an example of this, I ended up going with his exec code. https://stackoverflow.com/questions/2488457/how-to-increment-variable-names-is-this-a-bad-idea – Erico9001 Mar 23 '18 at 18:14

1 Answers1

2

Thanks to the comments, here is the issue solved by using a dictionary to define the variable instead of the exec command.

import tkinter as tk

class Example:

    def __init__(self): 
        #Dictionary with categories and their relative keywords
        self.categorykeywords={"Category 1":["Keyword 1", "Keyword 2", "Keyword 3"], "Category 2":["Keyword A","Keyword B","Keyword C"], "Category 3":["Another Keyword"]}

        #Dictionary containing the option menus referenced by category name
        btn_dict={}

        #Storing tkvar variable for later referencing
        self.dropdownreference={}

        #Number to assign to the tkvar name, to make the unique variables for each category
        i=1
        columncounter=0

        for category in self.categorykeywords:
            #Creating a unique variable / name for later reference
            self.dropdownreference[category] = StringVar(root)

            #Creating OptionMenu with unique variable
            btn_dict[category] = tk.OptionMenu(root, self.dropdownreference[category], *self.categorykeywords[category])

            btn_dict[category].grid(row=0, column=columncounter, padx=1, pady=1)

            columncounter+=1
            i+=1

root = Tk()
my_gui = Example()
root.mainloop()
Erico9001
  • 75
  • 4