0

I'm trying to make a tkinter GUI with a certain amount of buttons and entry fields that is specified by the user, so in this code below for example if the user specifies the variable number to be 3 I want there to be 3 entry boxes and , and I want to set it up so that if I type a value into the first field and click the button next to it, I want that button to read the value from the entry field next to it. I also need to assign each entry field to a variable that will be created through the same iterative loop. However, I'm having difficulty especially in regards to mapping the buttons to the entry fields, as I always seem to run up against an error with the text "AttributeError: 'NoneType' object has no attribute 'get'". Would anyone be able to either fix my code or help me find an alternative solution to the problem? Sorry if the description's a bit confusing. This is different from just a problem of just getting the contents of the entry widget as I need it to create a certain amount of entry widgets and buttons using iteration, and the question that my question has been marked a duplicate of doesn't explain how to iteratively map each entry field to each button. For example, if I enter 3 as the variable, I need the program to create entry field 1, entry field 2 and entry field 3 and button 1, button 2 and button 3 and then map each button to its respective entry field using iteration. I've tried using dictionaries, but this doesn't seem to help much.

import tkinter as tk
root = tk.Tk()
number = 3
d={}
def callBack():
    print(d["E{0}".format(i)].get())
    return
for i in range(0,number):
    d["E{0}".format(i)] = tk.Entry(root)
    d["E{0}".format(i)].grid(row=i, column=0)
    d["B{0}".format(i)] = tk.Button(root, text="test", command=callBack)
    d["B{0}".format(i)].grid(row=i, column=1)
  • Possible duplicate of [Get contents of a Tkinter Entry widget](https://stackoverflow.com/questions/9815063/get-contents-of-a-tkinter-entry-widget) –  Aug 06 '19 at 14:31
  • You need to provide a callback as the `command` parameter. In the callback, call the relevant `get` method. –  Aug 06 '19 at 14:48
  • `AttributeError: 'NoneType' object has no attribute 'get'` - this is what you will receive if you define your widget and call the geometry manager method at the same time. Read [Tkinter: AttributeError: NoneType object has no attribute ](https://stackoverflow.com/questions/1101750/tkinter-attributeerror-nonetype-object-has-no-attribute-attribute-name) – Henry Yik Aug 06 '19 at 14:52
  • @DavidCullen I did what you said although I still have the same error! – ShtarkBochur Aug 06 '19 at 14:56
  • @HenryYik I tried that and it helped somewhat, but I still have a error where no matter what button I click, only the data in the third entry box is printed. How can I fix that so button 1 prints entry box 1, button 2 prints entry box 2 and button 3 prints entry box 3? – ShtarkBochur Aug 06 '19 at 15:01
  • Also I need to be able to create another iteration where a variable is created for each button so the data in entry 1 goes to Variable1 when button 1 is clicked, entry 2 goes to Variable2 when button 2 is clicked and entry 3 goes to Variable3 when button 3 is clicked – ShtarkBochur Aug 06 '19 at 15:03
  • @ShtarkBochur When I tried running your code, I realized the real problem is that you were saving the result of the `grid` calls, which is `None`. –  Aug 06 '19 at 15:06

3 Answers3

1

The solution to "'NoneType' object has no attribute 'get'" has been asked probably a hundred times on this site, and the answer is always the same.

In python, when you do x = y().z(), x will be given the value of z(). In the case of x=tk.Entry(...).grid(...), x will be None because grid(...) always returns None. The solution is to always call grid or pack or place separate from when you create a widget.

You also claim you are having problems with dictionaries, but I don't see any problem in your code other than you are making it more difficult than necessary. You can directly use i as an index without having to build up a string for the index. If you need to keep track of both buttons and entries, I recommend two variables rather than one.

Part of the problem may also have to do with the fact you're trying to do something very odd in your command. You're trying to call the get method of the entry, but that's pointless since it simply returns a value that gets thrown away. In almost all cases, the correct solution is to write a proper function rather than trying to squeeze functionality into a lambda.

Example:

def handle_click(i):
    entry = entries[i]
    print("the value is {}".format(entry.get()))

buttons = {}
entries = {}
for i in range(0,number):
    entry = tk.Entry(root)
    button = tk.Button(root, text="test", command=lambda i=i: handle_click(i))

    buttons[i] = button
    entries[i] = entry

    entry.grid(row=i, column=0)
    button.grid(row=i, column=1)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

You need to save the Entry and Button before calling grid:

import tkinter as tk

number = 3
root = tk.Tk()

def get_on_click(widget_dict, entry_name):
    def on_click():
        result = widget_dict[entry_name].get()
        print("%s = %s" % (entry_name, result))
        return result
    return on_click

d = dict()
for i in range(0, number):
    entry_name = "E{0}".format(i)
    button_name = "B{0}".format(i)
    print(entry_name, button_name)
    d[entry_name] = tk.Entry(root)
    d[entry_name].grid(row=i, column=0)
    d[button_name] = tk.Button(root, text="test", command=get_on_click(d, entry_name))
    d[button_name].grid(row=i, column=1)

root.mainloop()

This should help you get started.

In your comment, you ask how to save the value in the Entry. I would create a class to handle everything:

import tkinter as tk

number = 3
root = tk.Tk()

class EntryButton(object):
    def __init__(self, root, number):
        self.number = number
        self.entry = tk.Entry(root)
        self.button = tk.Button(root, text="test", command=self.on_click)
        self.entry.grid(row=number, column=0)
        self.button.grid(row=number, column=1)
        self.value = None

    def on_click(self):
        self.value = self.entry.get()

storage = dict()
for i in range(0, number):
    storage[i] = EntryButton(root, i)

root.mainloop()

for i in range(0, number):
    value = storage[i].value
    print(f"storage[{i}] = {value}")

As you can see, this eliminates a lot of extra work.

  • Thank you so much, this is a massive help for me and easy to understand. Any idea how I would be able to assign the entry field data to a variable via iteration, for example if entry field 1 has the data "potatoes" in it, how would I then assign that data to a variable called variable1 made through iteration so that variable1 = "potatoes" and the same with entry field 2 and variable2 and so on? – ShtarkBochur Aug 06 '19 at 15:29
  • I would create a class and use it to handle all the work. See my updated answer. –  Aug 06 '19 at 15:46
0

For get text from entry

Entry.get("1.0", "end-1c")
# 1.0 for get first line.
# end-1c for if last letter space, this deletes it.

More info

ForceVII
  • 355
  • 2
  • 16