2

I am trying to write a program that will display 4 rows of 2 columns with column 0 being labels and column 1 being entries. Then, pass those 4 integer entries through as arguments into a function when a button is left clicked. Here is my code so far:

from tkinter import *

root = Tk()


class ClassName():

     def __init__(self, master):

       self.run_button = Button(self.master, text="Button Text", bg="green", fg="black", 
                               command="HERE IS WHERE I NEED HELP")
       self.run_button.grid(row=4, columnspan=2)

       self.label1 = Label(master, text="Entry 1").grid(row=0, sticky=E)
       self.label2 = Label(master, text="Entry 2").grid(row=1, sticky=E)
       self.label3 = Label(master, text="Entry 3").grid(row=2, sticky=E)
       self.label4 = Label(master, text="Entry 4").grid(row=3, sticky=E)

       self.entry1 = Entry(master).grid(row=0, column=1, sticky=W)
       self.entry2 = Entry(master).grid(row=1, column=1, sticky=W)
       self.entry3 = Entry(master).grid(row=2, column=1, sticky=W)
       self.entry4 = Entry(master).grid(row=3, column=1, sticky=W)

I want to then take the 4 entries and pass them through a different function called the_function. All the_function does is print out something based on the values of the 4 entries. So my remaining code looks like this:

def the_function(self, a, b, c, d):
#    Ensure a, b, c, and d are integers, 
#    do some math on the numbers and print something out based on the
#    values of a, b, c and d.

irrelevant_variable = ClassName(root)
root.mainloop()

The function works properly without the GUI but I cannot figure out how to create a button that takes self.entry1 and passes it through as a in the_function.

Other posts have lead me to think I should use the lambda command, but I'm not sure how this would work within this function.

5
  • 2
    You should not assign a widget on the same line that you grid it. Are you using a tutorial? I'm trying to determine if this common mistake has a single source, or if every new user falls victim to it independently. Commented Aug 17, 2015 at 18:18
  • Mark, please respond to Kevin's question in a comment here. Please post a link to the tutorial you're using so we can try to prevent others from having the same problem in the future. Commented Aug 17, 2015 at 18:41
  • @Kevin - I don't usually miss widget assignments with geometry method chaining, but I did today. :) I'll mention it in my answer. Commented Aug 17, 2015 at 18:42
  • The tutorial I had referenced that suggested formatting the widgets on the same line I introduce them was posted on YouTube by thenewboston entitled "Python GUI with Tkinter - 4 - Grid Layout". Perhaps I would be better suited in going through a full tutorial and then approaching the project. Any credible tutorial suggestions? Commented Aug 17, 2015 at 20:52
  • Technically, you can do this chaining and it will display the widgets, but it becomes unsuitable the moment you want to refer back to one of those widgets, such as getting the content of an Entry widget. I like Effbot, but that's more of a reference with occasional examples than a full tutorial. Commented Aug 17, 2015 at 21:12

2 Answers 2

3

You're on the right track - lambda allows you to define an anonymous, in-line function:

...command=lambda: the_function(entry1.get(), entry2.get(), entry3.get(), entry4.get())

It basically acts as a wrapper. You could also do this:

def mywrapper(self):
    the_function(entry1.get(), entry2.get(), entry3.get(), entry4.get())

And then bind that to the button:

...command=self.mywrapper)

Alternatively, simply have the_function get the necessary variables itself:

def the_function(self):
    a = entry1.get()
    b = entry2.get()
    c = entry3.get()
    d = entry4.get()
    #    Ensure a, b, c, and d are integers

And bind that function without a lambda:

...command=self.the_function)

However, this won't help in your current code - chaining your geometry management methods onto your widget creation when you're actually interested in referencing the widget will cause a problem. The widget constructors (Button(), Entry(), etc.) will return that widget for future reference. However, the geometry management methods (grid(), pack(), etc.) simply act on their widget and return None. This means that you're assigning None to self.label1, self.entry1, and so on. Separate the widget creation from the geometry management:

self.label1 = Label(master, text="Entry 1")
self.label1.grid(row=0, sticky=E)

You will now have actual widgets to work with.

Sign up to request clarification or add additional context in comments.

1 Comment

I decided to have the_function get the values and then call it explicitly via the command parameter as well as reformat the widget creation/ manipulation and it works. Thank you.
0

The normal solution is to have your button call a function that is specific for that button. The job of the function is to gather data, and then act on the data:

def __init__(...):
    ...
    self.run_button = Button(..., command=self.do_run)
    ...

def do_run(self):
    e1 = self.entry1.get()
    e2 = self.entry2.get()
    e3 = self.entry3.get()
    e4 = self.entry4.get()

    self.the_function(e1, e2, e3, e4)

You can use lambda or functools.partial, but that brings disadvantages with no clear advantages. Your code will be easier to write, read and maintain by using real functions rather than lambdas whenever possible.

You can also call the get() methods inside of the_function. The choice is up to you. If the_function can be used in multiple contexts, it can be useful to have it be decoupled from the UI. If the only purpose ever is to always process the values from the entry widgets, you can skip the intermediate function and simply have the_function get the values and then use them.

Comments

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.