Tkinter dynamically create widgets from button

后端 未结 2 1315
日久生厌
日久生厌 2021-01-29 06:40

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

相关标签:
2条回答
  • 2021-01-29 07:02

    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()
    
    0 讨论(0)
  • 2021-01-29 07:09

    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()
    
    0 讨论(0)
提交回复
热议问题