Tkinter dynamically create widgets from button

我只是一个虾纸丫 提交于 2019-12-02 22:46:51

问题


I'm attempting to make a dynamic GUI where clicking a button causes the creation of a new frame that is placed above the button with 3 entry widgets (user options) inside of it, and I need to be able to read the user input from the 3 entry widgets & possibly alter them. Each time the button is pressed, three new callable entry widgets should appear.

I know that this is wrong because it has been giving me errors, but could something similar to the lists be used to create the widgets dynamically?

from Tkinter import *

app = Tk()

frameNames = []
widgetNames = []

def createwidgets():
    global widgetNames
    global frameNames
    frameNames += (str("g"+str(len(frameNames))))  #why does the letter & number get added as seperate elements?
    widgetNames += [(str("w"+str(len(widgetNames)-1))), 
                    (str("w"+str(len(widgetNames)))),    
                    (str("w"+str(len(widgetNames)+1)))]

    frameNames[len(frameNames) - 1] = Frame(app)
    frameNames[len(frameNames) - 1].pack()

    widgetNames[len(widgetNames) - 3] = Entry(frameNames[len(frameNames) - 1])
    widgetNames[len(widgetNames) - 3].pack()
    widgetNames[len(widgetNames) - 2] = Entry(frameNames[len(frameNames - )- 1])
    widgetNames[len(widgetNames) - 2].pack()
    widgetNames[len(widgetNames) - 1] = Entry(frameNames[len(frameNames) - 1])
    widgetNames[len(widgetNames) - 1].pack()

createWidgetButton = Button(app, text="createWidgets", command=createwidgets())
createWidgetButton.grid(sticky=S)

app.mainloop()

回答1:


The main problem is these four lines of code:

frameNames[len(frameNames) - 1] = Frame(app)
frameNames[len(frameNames) - 1].pack()
...
createWidgetButton = Button(app, text="createWidgets", command=createwidgets())
createWidgetButton.grid(sticky=S)

You are creating both the frame and button as a child of app, but you are using grid for one and pack for the other. You must be consistent with all direct descendants of app - they must all use pack or they must all use grid.

The second problem is this line:

frameNames += (str("g"+str(len(frameNames))))  #why does the letter & number get added as seperate elements?

Here, frameNames is a list and you are trying to add it with a string. Adding is not the same as appending. You need to append the new name, or put the new name in a temporary list before adding it.

frameNames.append(str(...))

The third problem is this line:

createWidgetButton = Button(app, text="createWidgets", command=createwidgets())

The above is exactly the same as this:

result = createWidgets()
createWidgetButton = Button(app, text="createWidgets", command=result)

You must pass a reference to a function, not call the function. Change the line to this (notice the lack of parenthesis after createWidgets):

createWidgetButton = Button(app, text="createWidgets", command=createwidgets)

Unrelated to the problem, but your code would be much easier to read if you used temporary variables instead of repeating the pattern (str("w"+str(len(widgetNames)-1). As written, your code is almost impossible to read. Also, you don't want to be storing widget names, you need to store the actual widgets themselves.

And finally, don't do a wildcard import. There is simply no good reason to do it.

Here is how I would rewrite your code:

import Tkinter as tk

app = tk.Tk()

frames = []
widgets = []

def createwidgets():
    global widgetNames
    global frameNames

    frame = tk.Frame(app, borderwidth=2, relief="groove")
    frames.append(frame)

    frame.pack(side="top", fill="x")

    for i in range(3):
        widget = tk.Entry(frame)
        widgets.append(widget)

        widget.pack(side="left")

createWidgetButton = tk.Button(app, text="createWidgets", command=createwidgets)
createWidgetButton.pack(side="bottom", fill="x")

app.mainloop()



回答2:


This is relatively simple.

We can do this by creating a Frame widget but not packing it, filling it with whatever we need and then having a Button call the pack on the Frame widget.

Much like the below:

from tkinter import *

class App:
    def __init__(self, root):
        self.root = root
        self.create = Button(self.root, text="Create", command=self.draw)
        self.create.pack(side="bottom")
        self.frame = Frame(self.root)
        self.entry1 = Entry(self.frame)
        self.entry2 = Entry(self.frame)
        self.entry3 = Entry(self.frame)
        self.entry1.pack()
        self.entry2.pack()
        self.entry3.pack()
        self.submit = Button(self.frame, text="Submit", command=self.command)
        self.submit.pack()
    def draw(self):
        self.frame.pack(side="top")
    def command(self):
        print(self.entry1.get())
        print(self.entry2.get())
        print(self.entry3.get())

root = Tk()
App(root)
root.mainloop()

If you need to add multiple of these forms you can do something like the below which makes use of anonymous functions (lambda) in order to have "self aware" buttons which "know" which frame they're in:

from tkinter import *

class App:
    def __init__(self, root):
        self.frames = []
        self.entries = []
        self.count = 0
        self.root = root
        self.create = Button(self.root, text="Create", command=self.draw)
        self.create.pack(side="bottom")
    def draw(self):
        self.frames.append(Frame(self.root, borderwidth=1, relief="solid"))
        self.frames[self.count].pack(side="top")
        self.entries.append([Entry(self.frames[self.count]), Entry(self.frames[self.count]), Entry(self.frames[self.count])])
        for i in self.entries[self.count]:
            i.pack()
        Button(self.frames[self.count], text="Submit", command=lambda c=self.count: self.submit(c)).pack()
        self.count += 1
    def submit(self, c):
        for i in self.entries[c]:
            print(i.get())

root = Tk()
App(root)
root.mainloop()


来源:https://stackoverflow.com/questions/46903529/tkinter-dynamically-create-widgets-from-button

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!