0

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()
2
  • what is the error that you're getting? Commented Oct 24, 2017 at 7:57
  • @Bryan I fixed one issue since you have last seen the code, but it seems that it is going into an infinite loop and not displaying the application at the moment which I do not know if that is because of the attempt at making list created widgets Commented Oct 24, 2017 at 12:25

2 Answers 2

3

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()
Sign up to request clarification or add additional context in comments.

Comments

0

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()

3 Comments

I need to be able to click the button multiple times with each new widget having a way of retrieving its data.
That wasn't in your initial question. I've provided a solution.
I realize that it may have not been entirely clear in my initial question that the button needed to be reusable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.