Calling a widget from another class to change its properties in tKinter - python

I have made a function in the main constructor of my tKinter app which updates certain properties of widgets e.g. their text across multiple frames. What I'm trying to do is change widgets in multiple frames at the same time while in a controller frame.
def update_widgets(self, frame_list, widget_name, criteria, output):
for i in frame_list:
i.widget_name.config(criteria=output)
# update_widgets(self, [Main, AnalysisSection], text_label, text, "foo")
# would result in Main.text_label_config(text="foo") and
# AnalysisSection.text_label_config(text="foo") ideally.
However with this code, I'm encountering two problems. Firstly, I'm getting an attribute error stating that both frames don't have the attribute widget_name. Secondly, when I tried to refer to the widget names with the self prefix, both frames say they don't have the attribute self. Is there a way to fix this?
Full program below:
import tkinter as tk
class Root(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.frames = {}
container = tk.Frame(self)
container.pack(side="bottom", expand=True)#fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
for X in (A, B):
frame=X(container, self)
self.frames[X]=frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(A)
def show_frame(self, page):
frame = self.frames[page]
frame.tkraise()
def update_widgets(self, frame_list, widget_name, criteria, output):
for i in frame_list:
frame = self.frames[i]
widget = getattr(frame, widget_name)
widget[criteria] = output
class A(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.text = 'hello'
self.classLabel = tk.Label(self, text="Frame A")
self.classLabel.pack(side=tk.TOP)
# trying to change this widget
self.wordLabel = tk.Label(self, text="None")
self.wordLabel.pack(side=tk.TOP)
self.changeTextLabel = tk.Label(self, text="Change text above across both frames").pack(side=tk.TOP)
self.changeTextEntry = tk.Entry(self, bg='pink')
self.changeTextEntry.pack(side=tk.TOP)
self.changeFrameButton = tk.Button(text="Change to Frame B", command=lambda: self.controller.show_frame(B))
self.changeFrameButton.pack(side=tk.TOP, fill=tk.X)
self.changeTextEntryButton = tk.Button(self, text="ENTER", width=5, command=lambda: self.controller.update_widgets([A, B], 'self.wordLabel', 'text', self.changeTextEntry.get()))
self.changeTextEntryButton.pack(side=tk.TOP, fill=tk.X)
### calling this function outside of the button; this is already
### called within a function in my project.
x = self.controller.update_widgets([A, B], 'wordLabel', 'text', '*initial change*')
class B(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.text = 'hello'
self.classLabel = tk.Label(self, text="Frame B")
self.classLabel.pack(side=tk.TOP)
# trying to change this widget
self.wordLabel = tk.Label(self, text="None")
self.wordLabel.pack(side=tk.TOP)
self.changeTextLabel = tk.Label(self, text="Change text above across both frames").pack(side=tk.TOP)
self.changeTextEntry = tk.Entry(self, bg='light yellow').pack(side=tk.TOP)
self.changeFrameButton = tk.Button(text="Change to Frame A", command=lambda: self.controller.show_frame(A))
self.changeFrameButton.pack(side=tk.TOP, fill=tk.X)
self.changeTextEntryButton = tk.Button(self, text="ENTER", width=5, command=lambda: self.controller.update_widgets([A, B], 'self.wordLabel', 'text', self.changeTextEntry.get()))
self.changeTextEntryButton.pack(side=tk.TOP, fill=tk.X)
if __name__ == '__main__':
app = Root()

The problem in your code is that you're trying to get an attribute of a class rather than an instance of a class. You need to convert i to the actual instance of that class. You have the additional problem that you're passing 'self.wordLabel' rather than just 'wordLabel'.
A simple fix is to look up the instance in self.frames
def update_widgets(self, frame_list, widget_name, criteria, output):
for i in frame_list:
frame = self.frames[i]
label = getattr(frame, widget_name)
label[criteria] = output
You also need to change the button command to look like this:
self.changeTextEntryButton = tk.Button(... command=lambda: self.controller.update_widgets([A,B], 'wordLabel', 'text', self.changeTextEntry.get()))
If you intend for update_widgets to always update all of the page classes, there's no reason to pass the list of frame classes in. Instead, you can just iterate over the known classes:
def update_widgets(self, widget_name, criteria, output):
for frame in self.frames.values():
label = getattr(frame, 'classLabel')
label[criteria] = output
You would then need to modify your buttons to remove the list of frame classes:
self.changeTextEntryButton = tk.Button(..., command=lambda: self.controller.update_widgets('wordLabel', 'text', self.changeTextEntry.get()))

Related

How to access methods from a parent class which enables the frame swapping?

This is the window provides the container and methods which allow frame swapping:
class Login_Window(ctk.CTk):
def __init__(self, *args, **kwargs):
super().__init__()
self.geometry('400x400')
self.title('Music Mayhem')
self.resizable(False, False)
container = ctk.CTkFrame(master=self)
container.pack(side='top', fill='both', expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames={}
for F in (LoginFrame, RegEmailFrame):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky= 'nsew')
frame.grid_columnconfigure(0,weight=1)
frame.grid_rowconfigure(0,weight=1)
self.show_frame(LoginFrame)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
In order to swap the frames, a button has to be created in the frame that is going to be swapped. How would i go about creating an instance of another class within these frames which will also call the show_frame method? Here is the code for the frames- this could be ran as long as you have tkinter and custom tkinter installed. The only aspect that should supposedly not work are the buttons in the menu frame.
Yes, in this situation the menu frame is not needed but this is just a simple example because the actual code is way too long to be included here.
I have tried adding the menu frame into the list of frames to be swapped (in the class above) and giving it the same parent and controller attributes as the other frame but that required a parent and controller argument to be passed through when it is called in the Login and Register frames.
Is there a way to get round this or a simpler method that could be implemented instead?
class LoginFrame (tk.Frame):
def __init__(self,parent, controller):
tk.Frame.__init__(self, parent)
self.menu = Menu(self)
self.menu.grid(row=0, column=0)
self.loginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(RegEmailFrame)
self.loginBtn.grid(row=1, column=0)
class RegEmailFrame(tk.Frame):
def __init__(self, parent, controller,header_name="Register Email"):
tk.Frame.__init__(self, parent)
self.menu = Menu(self)
self.menu.grid(row=0, column=0)
self.emailLabel = ctk.CtKLabel(master=self,width=100, height=20 text='Frame swapped')
self.emailLabel.grid(row=1, column=0)
class Menu(tk.Frame):
def __init__(self, *args, header_name="Logo Frame",
width=175, height=175,**kwargs):
super().__init__(*args, width=width, height=height, **kwargs)
self.menuloginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(LoginFrame)
self.menuloginBtn.grid(row=0, column=0)
self.menuRegBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login',
state='normal',
command=lambda: controller.show_frame(RegEmailFrame)
self.menuRegBtn.grid(row=1, column=0)
In the current implementation, the Menu class does not have access to the controller object that is used to switch between frames in the Login_Window class. One way to fix this would be to pass the controller object to the Menu class during instantiation.
You can do this by adding a parameter called controller in the Menu class constructor and then passing it as an argument when creating an instance of the Menu class in the LoginFrame and RegEmailFrame classes.
For example, in the LoginFrame class:
def __init__(self,parent, controller):
tk.Frame.__init__(self, parent)
self.menu = Menu(self, controller)
self.menu.grid(row=0, column=0)
And in the Menu class constructor:
def __init__(self, parent, controller, *args, header_name="Logo Frame",
width=175, height=175,**kwargs):
super().__init__(parent, *args, width=width, height=height, **kwargs)
self.controller = controller
With this changes, the Menu class now has access to the controller object and can use it to switch between frames using the show_frame method.
You should also make the same changes in the RegEmailFrame class and in the constructor of the Menu class.
Hope this helps!

how to call a function of class based tkinter Frame from another class based frame [duplicate]

This question already has answers here:
Calling functions from a Tkinter Frame to another
(2 answers)
Closed 1 year ago.
I have a Tkinter application that has multiple class based frames and I would like to call a function which is in tkinter Frame from another frame/class.
For example, my frames are like this:
class_B(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def Update_class_b():
label = tk.Label(class_B, text=f"Welcome", font=LARGEFONT)
label.grid(row=1, column=0)
# wigdgets
button = tk.Button(self, text="START_TRIP", command=lambda: controller.show_frame(D_file.D_class)
#i am using this controller to navigate between those pages
# packing/grid
button.grid(row=2, column=0, padx=10, pady=10)
Now, I would like to call the function update_class_b from another class base frame. How can I do that right now I am passing class_B in label widget while making it as you can see and directly calling it in class_A, but it is not working. Please if anyone could help me regarding this.
Also, I would like to call this update_class_b function from inside another class based frame like while pressing a button in Class_A this function should be triger
class A would be like
class class_A(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def do_something_in_class_B():
#this should trigger Update_class_b function in class_B
button = tk.Button(self, text="DO SOMETHING IN CLASS B", command=do_something_in_class_B).pack()
controller class
class tkinterApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
self.attributes('-fullscreen', True)
for F in (file_A.Class_A,file_B.class_B):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(startPage.StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
if __name__ == "__main__":
app = tkinterApp()
app.mainloop()
Assuming you have your frames stored in a dictionary in the controller frame:
func = controller.frames[class_B].update_class_b
button = tk.Button(self, text="START_TRIP", command=func)
button.grid(row=2, column=0, padx=10, pady=10)
We'd have to see a MCVE to give a specific answer.

tkinter: Using same frame but having different command button?

Pardon me for my bad grammar or explanation, since I didn't know how to explain this properly.
I try to build some gui that could switch between frame, using script from this as base Switch between two frames in tkinter.
In this case, I will have a few frame that had similar design, but different function when the button is pressed. For example, I have 2 frames that have similar 2 entries and 1 button, but the button do different command (where at sub01 frame it will multiply and at sub02 frame will divide)
This is my code:
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.grid(row=1,columnspan=4,sticky='nsew')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (sub01, sub02):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=1,sticky="nsew")
self.choices = {'sub01','sub02'}
self.tkvar = tk.StringVar()
self.tkvar.set('sub01')
self.popMenu = tk.OptionMenu(self,self.tkvar,*self.choices)
self.popMenu.grid(row=0)
self.show_frame()
self.button1 = tk.Button(self, text="Go to Layer",command=lambda: self.show_frame())
self.button1.grid(row=0, column=1)
def show_frame(self):
'''Show a frame for the given page name'''
page_name = self.tkvar.get()
frame = self.frames[page_name]
frame.tkraise()
class sub01(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This SubLayer 1")
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='we')
i += 1
self.btn = tk.Button(self,text="multiply", command=lambda : self.multiply())
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def multiply(self):
pass
class sub02(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This SubLayer 2")
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='w')
i += 1
self.btn = tk.Button(self,text="divide",command=lambda : self.divide())
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def divide(self):
pass
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
This code itself works, but when I need to create more of these frames, it becomes inconvenient. How could I make this code simpler? Like having that similar frame as a class, and the button as other class that do differ behaviour depend of the layer shown.
Thank you in advance
The canonical way to do this sort of thing is to create a class hierarchy for your Page classes and put common functionality in the base classes and derive subclasses from them that specify the behavior that differs between them. Below is how you could do that with the sample code in your question.
Since the things that are different between them are:
The text displayed on the Label.
The text displayed on the Button.
The code in that's execute when the Button is clicked.
This means the derived classes only need to know what code to run in a generically named btn_func() method and what the text to displayed on the two widgets. The code below illustrates how to do that.
Note that I've changed the spelling of your class names to conform to the naming conventions describe in PEP 8 - Style Guide for Python Code.
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.grid(row=1,columnspan=4,sticky='nsew')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Sub01, Sub02):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=1,sticky="nsew")
self.choices = {'Sub01','Sub02'}
self.tkvar = tk.StringVar()
self.tkvar.set('Sub01')
self.popMenu = tk.OptionMenu(self,self.tkvar,*self.choices)
self.popMenu.grid(row=0)
self.show_frame()
self.button1 = tk.Button(self, text="Go to Layer",command=lambda: self.show_frame())
self.button1.grid(row=0, column=1)
def show_frame(self):
'''Show a frame for the given page name'''
page_name = self.tkvar.get()
frame = self.frames[page_name]
frame.tkraise()
class BaseSubLayer(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text=self.lbl_text)
label.grid(row=0)
self.entries=[]
i = 0
while i < 2:
self.entries.append(tk.Entry(self,width=10))
self.entries[i].grid(row=i+1,columnspan=2,sticky='we')
i += 1
self.btn = tk.Button(self,text=self.btn_func_name, command=self.btn_func)
self.btn.grid(row=i+1, columnspan=2,sticky='we')
def btn_func(self):
raise NotImplementedError
class Sub01(BaseSubLayer):
lbl_text = 'This SubLayer 1'
btn_func_name = 'multiply'
def btn_func(self):
print('Running multiply() method.')
class Sub02(BaseSubLayer):
lbl_text = 'This SubLayer 2'
btn_func_name = 'divide'
def btn_func(self):
print('Running divide() method.')
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

Variables from other classes in Tkinter [duplicate]

This is a shortened example of a longer application where I have multiple pages of widgets collecting information input by the user. The MyApp instantiates each page as a class. In the example, PageTwo would like to print the value of the StringVar which stores the data from an Entry widget in PageOne.
How do I do that? Every attempt I've tried ends up with one exception or another.
from tkinter import *
from tkinter import ttk
class MyApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand = True)
self.frames = {}
for F in (PageOne, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky = NSEW)
self.show_frame(PageOne)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
ttk.Label(self, text='PageOne').grid(padx=(20,20), pady=(20,20))
self.make_widget(controller)
def make_widget(self, controller):
self.some_input = StringVar
self.some_entry = ttk.Entry(self, textvariable=self.some_input, width=8)
self.some_entry.grid()
button1 = ttk.Button(self, text='Next Page',
command=lambda: controller.show_frame(PageTwo))
button1.grid()
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
ttk.Label(self, text='PageTwo').grid(padx=(20,20), pady=(20,20))
button1 = ttk.Button(self, text='Previous Page',
command=lambda: controller.show_frame(PageOne))
button1.grid()
button2 = ttk.Button(self, text='press to print', command=self.print_it)
button2.grid()
def print_it(self):
print ('The value stored in StartPage some_entry = ')#What do I put here
#to print the value of some_input from PageOne
app = MyApp()
app.title('Multi-Page Test App')
app.mainloop()
Leveraging your controller
Given that you already have the concept of a controller in place (even though you aren't using it), you can use it to communicate between pages. The first step is to save a reference to the controller in each page:
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
Next, add a method to the controller which will return a page when given the class name or some other identifying attribute. In your case, since your pages don't have any internal name, you can just use the class name:
class MyApp(Tk):
...
def get_page(self, classname):
'''Returns an instance of a page given it's class name as a string'''
for page in self.frames.values():
if str(page.__class__.__name__) == classname:
return page
return None
note: the above implementation is based on the code in the question. The code in the question has it's origin in another answer here on stackoverflow. This code differs from the original code slightly in how it manages the pages in the controller. This uses the class reference as a key, the original answer uses the class name.
With that in place, any page can get a reference to any other page by calling that function. Then, with a reference to the page, you can access the public members of that page:
class PageTwo(ttk.Frame):
...
def print_it(self):
page_one = self.controller.get_page("PageOne")
value = page_one.some_entry.get()
print ('The value stored in StartPage some_entry = %s' % value)
Storing data in the controller
Directly accessing one page from another is not the only solution. The downside is that your pages are tightly coupled. It would be hard to make a change in one page without having to also make a corresponding change in one or more other classes.
If your pages all are designed to work together to define a single set of data, it might be wise to have that data stored in the controller, so that any given page does not need to know the internal design of the other pages. The pages are free to implement the widgets however they want, without worrying about which other pages might access those widgets.
You could, for example, have a dictionary (or database) in the controller, and each page is responsible for updating that dictionary with it's subset of data. Then, at any time you can just ask the controller for the data. In effect, the page is signing a contract, promising to keep it's subset of the global data up to date with what is in the GUI. As long as you maintain the contract, you can do whatever you want in the implementation of the page.
To do that, the controller would create the data structure before creating the pages. Since we're using tkinter, that data structure could be made up of instances of StringVar or any of the other *Var classes. It doesn't have to be, but it's convenient and easy in this simple example:
class MyApp(Tk):
def __init__(self):
...
self.app_data = {"name": StringVar(),
"address": StringVar(),
...
}
Next, you modify each page to reference the controller when creating the widgets:
class PageOne(ttk.Frame):
def __init__(self, parent, controller):
self.controller=controller
...
self.some_entry = ttk.Entry(self,
textvariable=self.controller.app_data["name"], ...)
Finally, you then access the data from the controller rather than from the page. You can throw away get_page, and print the value like this:
def print_it(self):
value = self.controller.app_data["address"].get()
...
I faced a challenge in knowing where to place the print_it function.
i added the following to make it work though I don't really understand why they are used.
def show_frame(self,page_name):
...
frame.update()
frame.event_generate("<<show_frame>>")
and added the show_frame.bind
class PageTwo(tk.Frame):
def __init__(....):
....
self.bind("<<show_frame>>", self.print_it)
...
def print_it(self,event):
...
Without the above additions, when the mainloop is executed,
Page_Two[frame[print_it()]]
the print_it function executes before PageTwo is made Visible.
try:
import tkinter as tk # python3
from tkinter import font as tkfont
except ImportError:
import Tkinter as tk #python2
import tkFont as tkfont
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family="Helvetica", size=18, weight="bold", slant="italic")
# data Dictionary
self.app_data = {"name": tk.StringVar(),
"address": tk.StringVar()}
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others.
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0,weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
''' Show a frame for the given page name '''
frame = self.frames[page_name]
frame.tkraise()
frame.update()
frame.event_generate("<<show_frame>>")
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="this is the start page", font=self.controller.title_font)
label.pack(side="top", fill="x", pady=10)
# Update the Name value only
self.entry1 = tk.Entry(self,text="Entry", textvariable=self.controller.app_data["name"])
self.entry1.pack()
button1 = tk.Button(self, text="go to page one", command = lambda: self.controller.show_frame("PageOne")).pack()
button2 = tk.Button(self, text="Go to page Two", command = lambda: self.controller.show_frame("PageTwo")).pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=self.controller.title_font)
label.pack(side="top", fill="x", pady=10)
# Update the Address value only
self.entry1 = tk.Entry(self,text="Entry", textvariable=self.controller.app_data["address"])
self.entry1.pack()
button = tk.Button(self, text="Go to the start page", command=lambda: self.controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
# Bind the print_it() function to this Frame so that when the Frame becomes visible print_it() is called.
self.bind("<<show_frame>>", self.print_it)
label = tk.Label(self, text="This is page 2", font=self.controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: self.controller.show_frame("StartPage"))
button.pack()
def print_it(self,event):
StartPage_value = self.controller.app_data["name"].get()
print(f"The value set from StartPage is {StartPage_value}")
PageOne_value= self.controller.app_data["address"].get()
print(f"The value set from StartPage is {PageOne_value}")
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

Import data from another class python

I need to access the temperature variable from Monitor Class and print on Graph Class. How can I do that? Please see code below which should compile.
from tkinter import *
import tkinter as tk
import time
class ScientificPumpGui(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.container = tk.Frame(self)
self.container.pack(side="top", fill="both", expand=True)
self.container.grid_rowconfigure(0, weight=1)
self.container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (MonitorPage, GraphPage):
frame = F(self.container)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MonitorPage)
self.create_buttons()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def exit_app(self):
exit()
def create_buttons(self):
main_b_height = 2
main_b_width = 20
page_button_pady = 10
self.page_button_main_toolbar = tk.Frame(self, borderwidth=1)
self.page_button_main_toolbar.pack(side=TOP, anchor=CENTER, fill=X)
self.page_button_toolbar = tk.Frame(self.page_button_main_toolbar, borderwidth=1)
self.page_button_toolbar.pack(side=TOP, anchor=CENTER)
self.monitor_page_button = Button(self.page_button_toolbar, text="Monitor Page", width=main_b_width, height=main_b_height, command=lambda: self.show_frame(MonitorPage))
self.monitor_page_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
self.graph_page_button = Button(self.page_button_toolbar, text="Graph Page", width=main_b_width, height=main_b_height, command=lambda: self.show_frame(GraphPage))
self.graph_page_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
self.exit_app_button = Button(self.page_button_toolbar, text="Exit App", width=main_b_width, height=main_b_height, command=lambda: ScientificPumpGui.exit_app(0))
self.exit_app_button.pack(side=LEFT, anchor=CENTER, pady=page_button_pady)
class MonitorPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.monitor_data_counter = 0
self.page_label = tk.Label(self, text="Monitor Page")
self.page_label.pack(pady=10, padx=10)
def value_function(self):
self.temperature = 100
class GraphPage(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Graph Page!")
label.pack(pady=5, padx=10)
app = ScientificPumpGui()
app.mainloop()
When I tried to read temperature using:
monitor_page=MonitorPage(ScientificPumpGui())
print(str(monitor_page.temperature))
monitor_page.mainloop()
The error I get is:
AttributeError: 'MonitorPage' object has no attribute 'temperature'
Your MonitorPage class does not declaretemperature in the constructor function, but in value_function.
You can either declare temperature inside __init__ function, or call value_function before reading temperature.
You get this error because the member temperature is initialized in value_function method, which is not getting called.
As you didn't called to this method, the member temperature is not initialized and therefore you get the error.
In order to prevent this error, you should define the member temperature in your __init__ method with a default value.
You also can fix it by calling to the value_function method in order to initialize the member temperature.

Categories

Resources