Home > Blockchain >  Python3 - Tkinter - Widget creation with a dict
Python3 - Tkinter - Widget creation with a dict

Time:12-02

Im trying to create widgets dynamically onto the root window of tkinter with a json file as a widget config. I simplified the code so you can also test things out. (Im using grid() in my main code but its not necessary here)

The json file contains an items list with each widget in a seperate dict, it can look for example like this.

new_dict = {
    "items": [
        {
            "type": "frame",
            "id": "f1"
        },
        {
            "type": "button",
            "id": "b1",
            "text": "Button1"
        }
    ]
}

My goal here to create a widget, stored in the value of the id field, so i can later on change widgets states or other things via .config()

( Example: f1 = Frame(root) )

For this example my code looks like this:

( Note: im using locals() to create the specific variables, if there is a better way, please let me know )

# Example: Change Frame Color to Blue
def change_background(frame_id):
    locals()[frame_id].config(bg=blue)

# Root Window
root = Tk()
root.geometry("800x600")

# Widget Creation
for item in new_dict["items"]:
    if item["type"] == "frame":
        frame_id = item["id"]
        locals()[item["id"]] = Frame(root, width=200, height=200, bg="green")
        locals()[item["id"]].pack(side=BOTTOM, fill=BOTH)
    elif item["type"] == "button":
        locals()[item["id"]] = Button(root, text=item["text"], command=lambda: change_background(frame_id))
        locals()[item["id"]].place(x=50, y=50, anchor=CENTER)

root.mainloop()

Now my problem here is that i cant give the frame id into the change_background function. If i do that im getting following Error:

KeyError: 'f1'

I dont quite understand the problem here, because pack(), place() and grid() works fine with each widget.

CodePudding user response:

It is because you defined your frame_id inside the first if statement, and then you are trying to use it in elif. When you get to the elif then first if is skipped so that is the reason.

 for item in new_dict["items"]:
if item["type"] == "frame":
    frame_id = item["id"]
    locals()[item["id"]] = Frame(root, width=200, height=200, bg="green")
    locals()[item["id"]].pack(side=BOTTOM, fill=BOTH)
elif item["type"] == "button":
    locals()[item["id"]] = Button(root, text=item["text"], command=lambda: change_background(item["id"]))
    locals()[item["id"]].place(x=50, y=50, anchor=CENTER)

CodePudding user response:

As what the name locals means, it stores only local variables. So locals() inside the function just contains local variables defined inside the function.

It is not recommended to use locals() like this. Just use a normal dictionary instead:

...
# Example: Change Frame Color to Blue
def change_background(frame_id):
    widgets[frame_id].config(bg="blue")

# Root Window
root = Tk()
root.geometry("800x600")

# Widget Creation
# use a normal dictionary instead of locals()
widgets = {}
for item in new_dict["items"]:
    if item["type"] == "frame":
        frame_id = item["id"]
        widgets[item["id"]] = Frame(root, width=200, height=200, bg="green")
        widgets[item["id"]].pack(side=BOTTOM, fill=BOTH)
    elif item["type"] == "button":
        widgets[item["id"]] = Button(root, text=item["text"], command=lambda: change_background(frame_id))
        widgets[item["id"]].place(x=50, y=50, anchor=CENTER)
...
  • Related