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()
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)
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.
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()
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.
Related
I am writing a simple GUI using tkinter in Python3. It allows the user to press a button which calls a function, select_files, that lets the user select a bunch of files. The function processes the files and outputs a tuple of three things that need to be assigned to class variables, say a, b, c, for further processing. These variables should be accessible to any function that I define inside the class.
I attempted the following, but I get name 'self' is not defined error. The function select_files is already defined and written in a separate .py file and I am importing it from the correct location. I'm relatively new to GUI programming and object-oriented programming in general.
I studied a similar question asked here: python tkinter return value from function used in command, which was helpful in general, but couldn't help me with my specific problem.
Should these self.a, self.b, self.c variables appear under the __init__() method? If so, how do I assign them to the return values of select_files function? Also, would this make the function open_files unnecessary?
from ... import select_files
import tkinter as tk
from tkinter import filedialog
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.run_button()
def open_files(self):
self.a, self.b, self.c = select_files()
print(self.a) #self is not defined error comes from here
def run_button(self):
self.button = tk.Button(root, text = 'run', command = self.open_files)
self.button.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
EDIT: based on rassar's comment, I made the following change, and it works. The user can press the print button, and the object gets printed. Can someone now explain why self.run_button() and self.print_button() need to be in the init method?
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.run_button()
self.print_button()
def open_files(self):
self.a, self.b, self.c = select_files()
def print_files(self):
print(self.a)
def run_button(self):
self.button = tk.Button(root, text = 'run', command = self.open_files)
self.button.pack()
def print_button(self):
self.button = tk.Button(root, text = 'print', command = self.print_files)
self.button.pack()
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
This question already has an answer here:
How to save askdirectory result in a variable I can use using tkinter with OOP?
(1 answer)
Closed 4 years ago.
first of all im pretty new to OOP coding, so sorry for asking stupid questions.
I have a problem returning a value from Class AskDir that gets its value from Class SelectDir to Class MainWindow if that makes sense
Currently the code i have works, in every other way, except I cannot save the actual "self" (which is the path i.e. "/home/etc/") return value from Class AskDir() to another variable no matter what i do.
From what i have read i should be able to get the return value with a = AskDir(self)
print(a), but what I get instead is ".!frame.!functionchangename.!askdir"
So in short, how to save actual return path from a function inside Class AskDir, to be saved to variable a in Class MainWindow()?
To clarify, what i want is for Class MainWindow() to have a variable (a) that gets the return value from a subfunction get() inside Class SelectDir(), and that value should be the path that get() function returns
simplified code:
class MainWindow:
self.controller = controller
# prints the button to this frame!
getdir = AskDir(self)
print(getdir) # this should return actual path (/home/etc/), but it doesnt
class AskDir(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
self.selectdir = SelectDir(self, "Select directory",
"/home/user/folder/")
button = tk.Button(frame, text="Select directory",
command=self.select_dir)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
def select_dir(self):
self.selectdir.show()
self.act_dir.set(self.selectdir.get())
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
# THIS RETURNS THE ACTUAL PATH!
def get(self):
return self.selected
Does this work for you?
I can see no need to have SelectDir as a class given that its purpose is just to call the askdirectory method and return the selected folder path so I've change it to a function.
With this example, pressing the "Select directory" button will open the directory dialog and then place the selected directory text in to an Entry widget.
If the MainWindow class wishes to do anything with the path once it has been set then you just need to call the get_dir method of AskDir e.g. print(askDir.get_dir()
import tkinter as tk
from tkinter import filedialog
def doStuffFunction(value):
print(value)
class MainWindow(tk.Frame):
def __init__(self,master=None,**kw):
tk.Frame.__init__(self,master=master,**kw)
self.askDir = AskDir(self)
self.askDir.grid()
doStuffButton = tk.Button(self, text="Do Stuff", command=self.doStuff)
doStuffButton.grid()
def doStuff(self):
doStuffFunction(self.askDir.get_dir())
class AskDir(tk.Frame):
def __init__(self, master, **kw):
tk.Frame.__init__(self, master=master,**kw)
button = tk.Button(self, text="Select directory",
command=self.select_dir)
button.grid(row=0,column=1)
self.act_dir = tk.StringVar()
self.act_dir.set("/home/")
self.pathBox = tk.Entry(self, textvariable=self.act_dir,width=50)
self.pathBox.grid(row=0,column=0)
def select_dir(self):
selectdir = SelectDir("Select directory", "/home/user/folder/")
self.act_dir.set(selectdir)
def get_dir(self):
return self.act_dir.get()
def SelectDir(title,initial):
result = filedialog.askdirectory(initialdir=initial,title=title)
return result
if __name__ == '__main__':
root = tk.Tk()
MainWindow(root).grid()
root.mainloop()
You can now use the AskDir class as many times as you want in the MainWindow (perhaps to select Source and Destination paths). This has been added to my example with the doStuff method inside MainWindow.
I keep getting this error and I cant seem to fix it, if anybody could help me I would really appreciate it. I have been looking at it for quite a while now and I can't seem to get my head around it, I am still quite new to programming in an Object Oriented Way.
Thanks & Merry Christmas
Welcome Page
from External_Menu import *
from tkinter import *
class Welcome(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.welcome_button()
self.pack()
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=ExternalMenu.menu)
self.welcome.pack()
self.pack()
if __name__ == "__main__":
root = Tk()
main = Welcome(root)
main.mainloop()
External Menu
from tkinter import *
class ExternalMenu(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.menu()
self.pack()
def menu(self):
self.external_menu_lbl = Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = Button(self, text="Sign Up")
self.sign_up_button.pack()
self.pack()
Your menu is a method so it needs an object it can manipulate in order to work. But you're trying to call it without an object created for it, essentially you're calling it like a function. First you need to create an object of the class that method is defined on:
an_ex_men = ExternalMenu(root)
and then you are able to call menu method on an_ex_men:
an_ex_men.menu()
But since you already call menu under your ExternalMenu's __int__ method it is called as soon as an object instance for that class is created. Shortly, you creating an ExternalMenu object is enough to achieve that as in, even without putting it to a variable to refer later:
ExternalMenu(root)
Since you want to swap between two windows you need an additional method to either hide or completely destroy the other window. Let's say you want to destroy the other window, and for that you could use a method defined for your button that does those 2 actions:
def welcome(self):
self.destroy()
ExternalMenu(root)
As in:
...
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=self.welcome)
self.welcome.pack()
def welcome(self):
self.destroy()
ExternalMenu(root)
...
Below example does exactly what you expect, first I created a parent class App as according to you the two frames you have are in the same level of hierarchy and having a parent for them is in my opinion better structured:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.welcome_frame = Welcome(self)
#I believe it's better to call geometry managers as parent
self.welcome_frame.pack()
#assigning parent method as button command as it affects siblings
self.welcome_frame.button['command'] = self.go_ex_men
def go_ex_men(self):
self.welcome_frame.destroy()
self.ex_men = ExternalMenu(self)
self.ex_men.pack()
class Welcome(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.button = tk.Button(self, text="Welcome!")
self.button.pack()
class ExternalMenu(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.external_menu_lbl = tk.Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = tk.Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = tk.Button(self, text="Sign Up")
self.sign_up_button.pack()
if __name__ == "__main__":
root = App()
root.mainloop()
I have the following code (example of my real program):
from tkinter import *
def class1(Frame)
def nv(self,x):
self.vent=Toplevel(self.master)
self.app=class2(self.vent)
self.value=x
def __init__(self,master):
super().__init__(master)
self.master=master
self.frame=Frame(self.master)
self.btn=Button(self, text="example", command=lambda: self.nw(1))
self.btn.pack()
self.pack()
def class2(Frame):
def __init__(self, master):
super().__init__(master)
self.master=master
self.frame=Frame(self.master)
self.value=class1.nw.value.get()
root= Tk()
marco=Frame(root)
marco.pack
lf=class1(marco)
root.mainloop()
this last part is the problem, i cant use .get() properly for this problem, I want to get the value of x when I create the new window.
I use lambda so i can execute the command with parameters.
So the question is, is there a way for me to access the value of x in class 2?
You appear to be quite confused about the usage of classes when using tkinter. super() should not be used with tkinter as explained here and when declaring a class you should use the class keyword, not def. .get() is a method of a tkinter variable class such as tkinter.IntVar, tkinter.StringVar, etc. so is not needed in the example you have given.
What you need is the function in the Frame you are trying to get the value of x from (nv) and then parse that value to the __init__ method in the child Frame.
Here is my solution:
from tkinter import *
class Class1(Frame):
def nv(self,x):
self.vent = Toplevel(self.master)
self.app = Class2(self.vent,x)
def __init__(self,master):
Frame.__init__(self,master)
self.master = master
self.btn = Button(self, text="example", command=lambda: self.nv(1))
self.btn.pack()
self.pack()
class Class2(Frame):
def __init__(self, master, x):
Frame.__init__(self,master)
self.master=master
self.frame=Frame(self.master)
self.x_text = Label(self, text=str(x))
self.x_text.pack()
self.pack()
root = Tk()
marco = Frame(root)
marco.pack()
lf = Class1(marco)
root.mainloop()
Python 3.1, tkinter/ttk
I wrote something very simple to try and understand how tkinter's variables linked to widgets can be stored in a different class to the widget. Code below.
Questions:
1) why doesn't pressing the button change the label?
2) do I need quite so many selfs? Can the variables within each method manage without self. on the start?
Hopefully the answer will be a useful learning exercise for other tkinter newbies...
from tkinter import *
from tkinter.ttk import *
root = Tk()
class Store:
def __init__(self):
self.v = IntVar()
self.v.set(0)
def set(self, v):
self.v.set(v)
class Main:
def __init__(self):
self.counter = 0
self.label = Label(root, textvariable = a.v)
self.label.pack()
self.button = Button(root, command = self.counter, text = '+1')
self.button.pack()
def counter(self):
self.counter = self.counter + 1
a.set(self.counter)
a = Store()
b = Main()
root.mainloop()
Your problem is that you have both a method and a variable named counter. When you click the button, your function isn't being called so the variable isn't being set. At the time you create the button, tkinter thinks that self.counter is a variable rather than a command.
The solution to this particular problem is to rename either the function or the variable. It then should work as you expect.
To answer the question about "too many selfs": you need to use self so that python knows that the object you are referring to should be available everywhere within a specific instance of the object. So, yes, you need all those self's, if you want to refer to variables outside of the function they are defined in.
As for self in the definition of a method, those too are necessary. When you do object.method(), python will automatically send a reference to the object as the first argument to a method, so that the method knows specifically which method is being acted upon.
If you want to know more about the use of "self", there's a specific question related to self here: What is the purpose of self?
If I were you, I will do this:
import tkinter
root = tkinter.Tk()
var = tkinter.IntVar()
label = tkinter.Label(root, textvariable=var)
button = tkinter.Button(root, command=lambda: var.set(var.get() + 1), text='+1')
label.pack()
button.pack()
root.mainloop()
UPDATE:
But, if you insist doing this with two classes (where the first class is only a somewhat pointless interface), then I would do this:
import tkinter
class Store:
def __init__(self):
self.variable = tkinter.IntVar()
def add(self, value):
var = self.variable
var.set(var.get() + value)
return var.get()
class Main(tkinter.Tk):
def __init__(self, *args, **kwargs):
tkinter.Tk.__init__(self, *args, **kwargs)
var = Store()
self.label = tkinter.Label(self, textvariable=var.variable)
self.button = tkinter.Button(self, command=lambda: var.add(1), text='+1')
self.label.pack()
self.button.pack()
root = Main()
root.mainloop()
As you may notice, I used in both times the get() and set() methods of the IntVar (in your version I do this thru the Store interface we made), therefore you don't need to use a new variable (like self.counter) because the variable we instantiated is storing the data.
The other thing I used is a lambda expression instead of a full-blown function definition, since we only want to get the current value, add 1 to it, and store it as the new value.
And in my version, the Main class is a subclass of the original tkinter.Tk class -- that's why I call it's __init__ method inside the Main's __init__ method.