0

I have two sets of code. One works the other doesn't and I don't know why. In the first set of code I am storing my application into a class and referencing everything there. In the second one I am storing everything in the main function and using it there.

The main details of the problem are here:

I am using a entry widget and a button widget which is initially disabled. I want the button's state to be normal when there is text in the entry widgets text field.

I have searched online for many answers to the first set of code but the closest I have gotten is the second set of code. First I tried to integrate it into my code however that did not work. So what I did was take the code and strip it to the bare minimum and put everything into the main function.

The main difference between the two is one is in a class and the other is in the main function.

import tkinter as tk

class Aplication(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self,master)
        self.grid()
        self.button_clicks = 0

        something = tk.StringVar()
        button3 = tk.Button(self, text="Print entry", padx = 10, height = 2, bg = "blue", state = "disabled")

        entry = tk.Entry(self, textvariable = something)
        something.trace('w', self.ActivateButton)     #need this to always be exsecuted
        entry.grid(column = 2, row = 1)

        button3["command"] = lambda: self.PrintEntry(entry.get())
        button3.grid(padx = 10, pady = 10)

    def PrintEntry (self, entry):
            print(entry)
        def ActivateButton(self, *_):
            if something.get():
                button3['state'] = "normal"
            else:
                button3['state'] = "disabled"

if __name__ == '__main__':
    top= tk.Tk()
    top.title("Simple Button")
    top.geometry("500x300")

    app = Aplication(top)

    top.mainloop()

def PrintEntry (entry):
    print(entry)

def ActivateButton(*_):
    if entry.get():
        button3['state'] = "normal"
    else:
        button3['state'] = "disabled"

if __name__ == '__main__':
    top= tk.Tk()
    top.title("Simple Button")
    top.geometry("500x300")

    something = tk.StringVar()
    button3 = tk.Button(top, text="Print entry", padx = 10, height = 2, bg = "blue", state = "disabled")

    entry = tk.Entry(top, textvariable = something, bd = 2)
    something.trace('w', ActivateButton)
    entry.grid(column = 2, row = 3)

    button3["command"] = lambda: PrintEntry(entry.get())
    button3.grid(row = 3, column = 1, padx = 10, pady = 10)

    top.mainloop()

There are no error messages; however you will find in the first set, the button's state never gets set to normal. Is there a way to do this for the first one? What is the difference between the two that makes it to where I can't enable the button in the first one if it is impossible?

martineau
  • 119,623
  • 25
  • 170
  • 301
dbzeis
  • 3
  • 1
  • 1
    if you use classes then you should use `self.` to have access to variables create in other methods - for example: `self.something = tk.StringVar()`. If you don't use `self` then `self.something` is removed from memory after Python execute `__init__` (where you create `something). – furas Jun 27 '19 at 23:01

2 Answers2

1

You have to use self. to create class variables so they will be accesible in all methods in class. Currently you create local variable something in __init__ and it is deleted when Python ends __init__ - so finally it removes something.trace() and it doesn't check value in entry.

In this code I use self.something and self.button3 and it works correctly.

import tkinter as tk

class Aplication(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self,master)
        self.grid()
        self.button_clicks = 0

        self.something = tk.StringVar()
        self.button3 = tk.Button(self, text="Print entry", padx = 10, height = 2, bg = "blue", state = "disabled")

        entry = tk.Entry(self, textvariable = self.something)
        self.something.trace('w', self.ActivateButton)     #need this to always be exsecuted
        entry.grid(column = 2, row = 1)

        self.button3["command"] = lambda: self.PrintEntry(entry.get())
        self.button3.grid(padx = 10, pady = 10)
    def PrintEntry (self, entry):
        print(entry)
    def ActivateButton(self, *_):
        if self.something.get():
            self.button3['state'] = "normal"
        else:
            self.button3['state'] = "disabled"

if __name__ == '__main__':
    top= tk.Tk()
    top.title("Simple Button")
    top.geometry("500x300")

    app = Aplication(top)

    top.mainloop()

EDIT: the same little different - without lambda. I use self.entry (as class variable) directly in print_entry.

import tkinter as tk

class Aplication(tk.Frame):

    def __init__(self, master):
        super().__init__(master)#tk.Frame.__init__(self,master)
        self.grid()
        self.button_clicks = 0

        self.something = tk.StringVar()

        self.entry = tk.Entry(self, textvariable=self.something)
        self.entry.grid(column=2, row=1)

        self.button3 = tk.Button(self, command=self.print_entry, text="Print entry", padx=10, height=2, bg="blue", state="disabled")
        self.button3.grid(padx=10, pady=10)

        self.something.trace('w', self.activate_button) #need this to always be exsecuted

    def print_entry(self):
        print(self.entry.get())

    def activate_button(self, *_):
        if self.something.get():
            self.button3['state'] = "normal"
        else:
            self.button3['state'] = "disabled"

if __name__ == '__main__':
    top= tk.Tk()
    top.title("Simple Button")
    top.geometry("500x300")

    app = Aplication(top)

    top.mainloop()
furas
  • 134,197
  • 12
  • 106
  • 148
0

As pointed out by @furas you need to prefix your variables in the constructor method def __init__ with self. so you can access attributes and methods since it literally represents an instance of the class. This link explains it in more detail https://stackoverflow.com/a/2709832/7585554.

import tkinter as tk


class Application(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.grid()
        self.button_clicks = 0

        self.something = tk.StringVar()
        self.button3 = tk.Button(self, text="Print entry", padx=10, height=2, bg="blue", state="disabled")

        self.entry = tk.Entry(self, textvariable=self.something)
        self.something.trace('w', self.ActivateButton)
        self.entry.grid(column=2, row=1)

        self.button3["command"] = lambda: self.PrintEntry()
        self.button3.grid(padx=10, pady=10)

    def PrintEntry (self):
        print(self.entry.get())

    def ActivateButton(self, *_):
        if self.something.get():
            self.button3['state'] = "normal"
        else:
            self.button3['state'] = "disabled"


if __name__ == '__main__':
    top = tk.Tk()
    top.title("Simple Button")
    top.geometry("500x300")
    app = Application(top)
    top.mainloop()
probat
  • 1,422
  • 3
  • 17
  • 33