2

How can I call a function change_label whenever global variable a changes its value? With change_variable I am trying to simulate actual changing of the variable (the variable changes on button click).

from tkinter import *

a = 3

class Application(Frame):

    def __init__(self, master):
        Frame.__init__(self, master)
        self.master = master
        self.button = Button(self.master, text='Change Variable', command=self.change_variable)
        self.button.grid(row=0)
        self.label = Label(self.master, text='Test')
        self.label.grid(row=1)

    def change_label(self):
        self.label.config(bg='Red', fg='Yellow')

    def change_variable(self):
        global a
        a = 1

def main():
    root = Tk()
    Application(root)
    root.mainloop()

if __name__ == '__main__':
    main()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
nuked0ne
  • 35
  • 5
  • If it isn't an issue, you can use a lightweight thread to monitor the value periodically. – cs95 Jun 06 '17 at 11:12
  • okay, why don't you just call `change_label` in all places that change `a`? – MSeifert Jun 06 '17 at 11:14
  • Also, check this out: [How to trigger function on value change?](https://stackoverflow.com/questions/6190468/how-to-trigger-function-on-value-change) – cs95 Jun 06 '17 at 11:14
  • 4
    Why not use a property on an object instead of a global then you could call your function from the setter. – Dan D. Jun 06 '17 at 11:14

4 Answers4

4

If you use one of tkinters special variables (StringVar, etc) you can add a "trace" that will trigger a callback whenever the variable is set or unset.

For example:

class Application(Frame):

    def __init__(self, master):
        Frame.__init__(self, master)
        ...
        self.a = tk.IntVar(value=3)
        self.a.trace("w", self.change_label)
        ...

    def change_label(self, *args):
        self.label.config(bg='Red', fg='Yellow')

    def change_variable(self):
        self.a.set(1)

With that, whenever you set the value of self.a via the set method, the function bound with the trace will be called.

Any widget that uses that variable also will be updated. For example, change your label to this:

self.label = tk.Label(self.master, textvariable=self.a)

When you click the button, notice that the label changes to reflect the change.

For a description of what arguments are passed to the trace function, see What are the arguments to Tkinter variable trace method callbacks?

These variables have a good description here: The Variable Classes (BooleanVar, DoubleVar, IntVar, StringVar)

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
0

If you are using Tk, this might be worth looking into: Tk's "Variable" classes

If that doesn't work for you (because you want to store your own type, or something like that), Shiva's comment is the way to go, and if you want to store multiple variables, this might be a good idea:

class Storage(dict):
    def __getattribute__(self, name):
        return self[name]

    def __setattr__(self, name, value):
        print(name, value)  # react to the change of name
        self[name] = value

storage = Storage({a: 3, b: 2})

storage.a = 4
print(storage.a)

If you don't want to be able to set the variable without triggering some code you put there, good luck. You can override __setitem__ too, but you can always call dict.__setitem__ with the Storage variable as the first argument.

CodenameLambda
  • 1,486
  • 11
  • 23
0

Try running this code it will give you the idea of what you want.

import tkinter
count = 5
def change_text():
        global count
        if count != 2:
                button.config(text='edit')

frame = tkinter.Frame(height=500,width=500)
button = tkinter.Button(frame,text='save',command=change_text)
button.place(x=0,y=0)
frame.pack()
frame.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
joshua
  • 28
  • 8
-1
The code was supposed be like this.
from tkinter import *

a = 3

class Application(Frame):

    def __init__(self, master):
        Frame.__init__(self, master)
        self.master = master
        self.button = Button(self.master, text='Change Variable', command=self.change_label)
        self.button.grid(row=0)
        self.label = Label(self.master, text='Test')
        self.label.grid(row=1)

    def change_label(self):
        global a
        a = 1
        if a != 3:
            self.label.config(bg='Red', fg='Yellow')

def main():
    root = Tk()
    Application(root)
    root.mainloop()
hope that is what you wanted.
joshua
  • 28
  • 8
  • Why did you add a second answer? What does "the code was supposed to be like this" mean? Are you aware you can edit your original answer? – Bryan Oakley Jun 06 '17 at 12:27
  • you don't need the change_variable method you can still do all that in one method the change_variable method like have done. – joshua Jun 06 '17 at 12:28