How to access variables from different classes in tkinter?
At its core, your question has a simple answer. "How do I get a value from object X?" The answer is the same for any object: you get it by asking object X. All you need in order to do that is get a reference to the object and then access the attribute directly.
Accessing data from other pages
In your case, the code in PageTwo
needs a reference to PageOne
so you can get the v
variable.
So, how do you get a reference? The code (which you copied either from a tutorial, or from the stackoverflow answer that the tutorial copied from) was designed to make this easy. Each page is given a reference to a controller, and this controller has a reference to each page. You can therefore ask the controller to give you a reference to a page.
The first step is to save the reference to the controller in each class. Interestingly, you're already doing this in PageOne
, but you should do it in all the pages. Make sure you add self.controller = controller
in every __init__
method, like so:
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
...
self.controller=controller
...
Next, we need to add a method in the controller class that will return a reference to the page. Add the following function to SampleApp
:
class SampleApp(tk.Tk):
...
def get_page(self, page_class):
return self.frames[page_class]
...
Now, from within any "page" you can get access to the object for any other "page". For example, in PageTwo
you can access the v
variable from PageOne
like this:
page1 = self.controller.get_page(PageOne)
page1.v.set("Hello, world")
Using shared data
An even better solution is for your SampleApp
class to create a single set of variables that all of the pages share. You can create a dictionary in that class, and then use the controller to give every page access. For example:
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.shared_data = {
"username": tk.StringVar(),
"password": tk.StringVar(),
...
)
Then, from within any class you can access the data like this:
entry1 = tk.Entry(self, textvariable=self.controller.shared_data["username"])
...
username = self.controller.shared_data["username"].get()
The reason this is the better solution is that your pages don't have to know how the other pages are implemented. When a page relies on the exact implementation of another page this is called tight coupling. If the pages don't need to know how the other pages are implemented, this is called loose coupling.
Loose coupling gives you more flexibility. Instead of having every page tightly coupled to every other page, they are all tightly coupled to a single object: the controller. As long as every page knows only about the controller, each page is free to be changed at any time without affecting the rest of the program.
Of course, if you change the controller you have to change all of the pages, but if you do a good job designing the controller that's less likely to occur and easier to manage when it does occur.
Retrieving variable from another class
The culprit is that you should not share
class member variables that way.
If different classes share some common
data, that data is probably another class
and they can inherit from it.
class CommonData():
client = 100
class A(CommonData):
def __init__(self):
print(A.client)
class B(CommonData):
def __init__(self):
print(B.client)
a = A()
b = B()
CommonData.client = 300
print(a.client)
print(b.client)
In above case every instance of A and every instance of B
share all the CommonData class variables, like client.
CommonData.client = 400
class C():
pass
You can use multiple inheritance too.
define all common data as CommonData attributes
and use CommonData as a class to hold data, like
in above example, don't create instances from it:
class D(C, CommonData):
def __init__(self):
print(D.client)
c = C()
d = D()
A simpler option would be to just define
a variable CommonData in the outer scope and
use it from anywhere:
common_data = 500
class A():
def __init__(self):
global common_data
print(common_data)
common_data = 200
# ...
But global variables are generally seen as a bad thing in a program as their use can become a problem for several reasons.
Yet another way is to pass the variable to the object initializer.
That makes the instance to keep its own value copied from
the creation value:
common_data = 600
class A():
def __init__(self, data):
self.common = data
print(self.common)
a = A(common_data)
common_data = 0
print(a.common)
If you run all the code above it will print
100
100
300
300
400
600
600
Edit:
See my comment to your answer and a simple example here.
Here I opt for two global references to tkinter StringVars.
The stringvars exist themselves in the Tk() namespace, like the
widgets; besides they are global Python names.
import tkinter as tk
from tkinter import ttk
class Page1(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page1')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page2')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file2)
self.button1 = ttk.Button(self, text='Copy Here', command=copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename():
input_file2.set(input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
root.iconify()
input_file1 = tk.StringVar()
input_file2 = tk.StringVar()
page1 = Page1(root)
page2 = Page2(root)
root.mainloop()
Now in the next example see how I turn the stringvars into variables
of Page1 and Page2 instances (not classes), making them local instead
of global. Then I am forced to pass a reference for the widget page1
object into the widget page2 object.
This looks more close to what you are asking.
About MRO trouble, if you avoid multiple inheritance
it won't happen.
Or you deal with it usually by using super()
In your case the error is because you store the widget in
the object/instance (in self.somename
), and then you try
to invoke a widget method qualifying with the class name.
There is no widget there in the class for you to use a method.
So the search using the method resolution order fails,
because there is no corresponding name there.
Note that I have not used multiple inheritance, so I could
have just written tk.Frame.
instead of calling super
. I like
super because it makes clear in the text that I am invoking the parent
class but super is really needed only when there are multiple parents
and various levels of subclassing (usually forming a diamond shape).
Now the example:
import tkinter as tk
from tkinter import ttk
class Page1(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.input_file1 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Frame):
# note the page1 reference being
# passed to initializer and stored in a var
# local to this instance:
def __init__(self, parent, page1, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.page1 = page1
self.input_file2 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file2)
self.button1 = ttk.Button(self, text='Copy Here',
command=self.copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename(self):
# see how the page1 refernce is used to acess
# the Page1 instance
self.input_file2.set(page1.input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
page1 = Page1(root)
page2 = Page2(root, page1) # pass a reference to page1 instance
page1.pack(side=tk.LEFT)
page2.pack(side=tk.LEFT)
root.mainloop()
How to access variables from different class in tkinter to use in different functions?
printName
and close_window
methods will be evaluated at last, unstead of declaring them outside a class move them to Page1
and change buttons commands:
class Page1(Page):
def __init__(self, *args, **kwargs):
...
button_1 = tk.Button(..., command=self.printName)
button_3 = tk.Button(..., command=self.close_window)
def printName(self):
print(self.last_user_name.get())
def close_window(self):
root.destroy()
driver.close()
You have a NameError: name 'OMcreat' is not defined
in Page2
.
class Page2(Page):
def __init__(self, *args, **kwargs):
...
button_2 = tk.Button(..., command=self.OMcreat)
button_2.place(x=750,y=400)
def OMcreat(self):
pass
The value of last_user_name
should be empty until a user fills in it.
replace it with
self.last_user_name = self.shared_data["last_user_name"]
and use get
method when needed.
heading
variable will be of None
type which is the result of pack
method (same with place
method), so change it to the following and do the same with other variables:
heading= tk.Label(...)
heading.pack()
EDIT
The reference of the MainView instance is already available in args
.To access pages instances, you can define them as an instance variable:
self.p1 = Page1(self)
How to pass a data between different classes defined for tabs in my tkinter GUI?
You never create an instance of Data
. Instead, you're initializing self.data
to an empty dictionary. You need to create an instance of Data
and pass that to the tabs.
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.app_data = Data()
...
How to properly access a StringVar() of a class from another class - Python - tkinter
I had to change a few things but for the most part the major solution is to move your StringVar()
's into the main class. Then next we can use the controller
argument in the other 2 classes to manipulate the data.
I added a function on page 2 to deal with updating the the label StringVar.
Because of this I deleted the other function you had for this.
I had to change your entry field to a class attribute so we can use its content in the new method. I also had to create a class attribute for the controller
in page 2 so we can use the controller in the method as well.
Now there might be an easier way but this is what I managed with your code.
import tkinter as tk
class TestMain(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title('TEST GUI')
# Moved StringVar()'s to the main class
self.page1_label = tk.StringVar()
self.page2_entry = tk.StringVar()
container = tk.Frame(self)
container.pack(side='top')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.configure(background='lightgrey')
frame.grid(row=0, column=0, sticky='nswe')
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# Deleted this function
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
frame_eb_data = tk.Frame(self, width=100, height=100, bg="orange")
frame_eb_data.grid(row=0, column=0, sticky='nsew', padx=5, pady=5)
frame_but_right = tk.Frame(self, width=240, height=60, bg="yellow")
frame_but_right.grid(row=1, column=0, padx=5, pady=5, sticky='nsew')
lab_eb_data = tk.Label(frame_eb_data, background='#DDD4EF', textvariable=controller.page1_label)
lab_eb_data.grid(row=0, column=0)
b_ebdata = tk.Button(frame_but_right, text="Page 2", width=10, height=2, command=lambda: controller.show_frame(PageTwo))
b_ebdata.grid(row=0, column=0)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# Added the self.controller so the method below can use it.
self.controller = controller
frame_buttons = tk.Frame(self, width=455, bg="#DDD4EF", colormap="new")
frame_buttons.grid(row=0, column=0, padx=5, pady=5, sticky='e')
frame_up_left = tk.Frame(self, width=485, height=260, bg="#89E3FA", colormap="new")
frame_up_left.grid(row=1, column=0, sticky='w', padx=5, pady=5)
b_data = tk.Label(frame_buttons, text='Example GUI', font='TrebuchetMS 30 bold', background="#DDD4EF")
b_data.grid(row=0, column=0, padx=13, pady=5, sticky='w')
b5 = tk.Button(frame_buttons, text='Set Text', command= self.update_p2_label)
b5.grid(row=0, column=2, padx=5, pady=5, sticky='e')
b6 = tk.Button(frame_buttons, text='Page 1', command=lambda: controller.show_frame(PageOne))
b6.grid(row=0, column=3, padx=5, pady=5, sticky='e')
self.entry_nombre_fld = tk.Entry(frame_up_left, width=40)
self.entry_nombre_fld.grid(row=1, column=1, columnspan=3, sticky='w')
label_2 = tk.Label(frame_up_left, text="Name:", font=("bold", 14))
label_2.grid(row=1, column=0, sticky='e')
# Added this function to update the page1_label StringVar.
def update_p2_label(self):
self.controller.page1_label.set(self.entry_nombre_fld.get())
app = TestMain()
app.mainloop()
How do I pass variables in different classes?
First, nextpage
should definitely not inherit from SampleApp
. So, change the definition to:
class nextpage(tk.Frame):
Second, margaritapizza
is an attribute of SampleApp
, so you need to refer to it via the instance of SampleApp
. In your case, self.controller
is that instance, so you can use self.controller.margaritapizza
.
There are other problems with how you define the entry, I'll fix them as well:
self.entry = tk.Entry(self, borderwidth=2,width=15,textvariable=self.controller.margaritapizza)
self.entry.pack()
Related Topics
I Have a Problem with Sending Mail:Typeerror: _Init_() Got an Unexpected Keyword Argument 'Context'
Module Not Found After Building Python Project by Using Pysinstaller
How to Deploy a Perl/Python/Ruby Script Without Installing an Interpreter
Is There a Built-In Function to Print All the Current Properties and Values of an Object
Removing Duplicates from a List of Lists
What Is the Most Efficient Way to Loop Through Dataframes with Pandas
Check If a Word Is in a String in Python
How to Import a Module That Is Definitely Installed
Slice 2D Array into Smaller 2D Arrays
Python 3.4.3 Modules Installation in Linux Error
Python Equivalent of Ruby's 'Method_Missing'
Create Multiple Dataframes in Loop
Is There a Simple Way to Delete a List Element by Value
Urllib2.Httperror: Http Error 403: Forbidden
How to Explode a List Inside a Dataframe Cell into Separate Rows