I am creating a Tkinter GUI with a lot of pages. A lot of them have the same display of different databases.
I want to create multiple pages following the list of databases
I have a list of pandas dataframes in a list called Databases and I want to create pages automatically fitted to the databases
global Pages
Pages=[]
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
Pages_List=[Studies_List]+Pages
self.frames = {}
for F in Pages_List: #dont forget to add the different pages
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Studies_Page)
def show_frame(self, c):
frame = self.frames[c]
frame.tkraise()
class Studies_Page(tk.Frame): #Should, from each Site, show only the XRays corresponding to this Site. Plots sheet is an option too.
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent,background=bg_color)
self.controller=controller
tk.Label(self, text="Home > Studies ",bg=bg_color,font=('Arial',9,'italic')).pack(side='top')
label = tk.Label(self, text="Choose the Study you want the data from :",bg=bg_color, font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
Studies_frame=tk.Frame(self,bg=bg_color)
############ Studies Buttons
for i in range(len(Databases)):
tk.Button(Studies_frame, text=Pages[i]['Study'][0],command=lambda: controller.show_frame(Pages[i])).pack(in_=Studies_frame)
tk.Label(Studies_frame, text=" ",bg=bg_color,font=('Arial',11,)).pack()
Studies_frame.pack(expand=True,side='top')
for i in range(len(Databases)):
class page(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller=controller
df=Databases[i]
f=tk.Frame(self)
f.pack()
self.table=pt=Table(f,dataframe=df)
pt.show()
return
Pages.append(page)
if __name__ == "__main__":
app = SampleApp()
app.title("X-Rays App")
app.mainloop()
It is particularly needed because I have multiple buttons in the startpage, each of them leading to a database.
I was expecting a page for each database but instead every occurence of the Pages List returned the last list.
I have a lot of pages to create with tkinter and they have a lot in common so I would like to use lists with classes in a dynamic way but I didn't find how.
I tried changing the class name everytime but it still returns the last database of the Databases lists
There are no reasons to redefine the class Page(tk.Frame): multiple times in a for loop.
What you should probably do, is create instances of that class inside the loop.
Maybe like this:
class Page(tk.Frame):
def __init__(self, parent, controller, db_index):
tk.Frame.__init__(self, parent)
self.controller=controller
self.db_index = db_index
df = Databases[self.db_index]
self.pack()
self.table = Table(f, dataframe=df)
pt.show()
pages = []
for idx in range(len(database_collection)):
pages.append(Page(parent, controller, idx)) # create an instance of Page
Related
I have a question about why my child class can't see parents method(it can't see them, but it can use them). I'm creating a GUI in tkinter and I need to use some methods from my parent window.
My parent class look like this:
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")
# 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)
manager_of_action = Action_manager()
self.frames = {}
for F in (MenuView, LoginView, GameView, ResultView ):
page_name = F.__name__
frame = F(parent=container, controller=self,action_manager= manager_of_action)
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("MenuView")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
and child class look like this:
class MenuView(tk.Frame):
def __init__(self, parent, controller, action_manager):
tk.Frame.__init__(self, parent)
self.controller = controller
button1 = tk.Button(self, text="Multiplayer",command=lambda: controller.show_frame("LoginView"),height=3,width=6, bg=("white"), font=controller.title_font)
button2 = tk.Button(self, text="Exit",command=lambda: exit(0),height=3,width=6,bg=("white"), font=controller.title_font )
button1.pack(side="top", fill="both", expand=True)
button2.pack(side="top", fill="both", expand=True)
But VS Code can't see .title_font() and .show_frame() but if I run my program, it is running as it should. Why it's happening and can I somehow fix it? (If I would have 50 methods in my parent class, I don't want to always look back to parent class and copy + paste my desired fuction.)
Thanks a lot for every help.
EDIT:
VScode doesn't highlight .title_font() and .show_frame() as it can be seen on picture. This means that VS code is not showing them in IntelliSense (Pylance) menu (. + space menu that will pop up after calling some object). Normally, functions are highlighted in yellow.
no highlight for .show_frame, .title_font
I am new to python and GUI-Thinker. I'm learning about how to switch windows on GUI using Tkinker as UI, and python as a programming language.
I followed this guideline Switch between two frames in tkinter to switch frames in thinker and it worked.
Then, I'm trying to show widgets and hide all other widgets using bind("<<ComboboxSelected>>". But when I choose Monthly option, widgets as I set under DeleteOptions condition didn't show up. The same thing to Period option, widgets as I set under DeleteOptions condition didn't show up.
My Code:
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter import font as tkfont
from tkcalendar import DateEntry
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")
# 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):
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()
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=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button1.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=controller.title_font)
label.grid()
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.grid()
def DeleteOptions():
if SVcmb_Delete.current()=="Monthly":
HideDeleteOptions()
lblMonthly.grid(row=4)
SVcmb_Monthly.grid(row=4, column=1)
if SVcmb_Delete.current()=="Period":
HideDeleteOptions()
lblFrom.grid(row=4)
txtFrom.grid(row=4, column=1)
lblTo.grid(row=5)
txtTo.grid(row=5, column=1)
lblSV_Search=tk.Label(self,text="Delete by")
lblSV_Search.grid(row=3)
SVcmb_Delete=ttk.Combobox(self,state="readonly",justify=CENTER,font=("times new roman",15))
SVcmb_Delete["values"]=("Select","Monthly","Period")
SVcmb_Delete.grid(row=3,column=1)
SVcmb_Delete.bind("<<ComboboxSelected>>", lambda event:DeleteOptions)
def HideDeleteOptions():
lblMonthly.grid_forget()
SVcmb_Monthly.grid_forget()
lblFrom.grid_forget()
txtFrom.grid_forget()
lblTo.grid_forget()
txtTo.grid_forget()
lblMonthly=tk.Label(self,text="Monthly")
SVcmb_Monthly=ttk.Combobox(self,state="readonly",values=[1,2,3],justify=CENTER)
lblFrom=tk.Label(self,text="From")
txtFrom=DateEntry(self,selectmode='day',date_pattern='mm/dd/y')
lblTo=tk.Label(self,text="To")
txtTo=DateEntry(self,selectmode='day',date_pattern='mm/dd/y')
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
On page one. I'm looking for a way like when I chose Monthly option, all widgets under DeleteOptions condition show up and all widgets of Period option are hidden as I set under DeleteOptions condition. The same thing to Period option, all widgets under DeleteOptions condition show up and all widgets of Monthly option are hidden as I set under DeleteOptions condition.
Please help me. Thank you so much.
There are two problems with your code. This first is with your lambda expression:
SVcmb_Delete.bind("<<ComboboxSelected>>", lambda event:DeleteOptions)
is wrong because it doesn't actually call the DeleteOptions() function the way it's written.
Instead, do it like this:
SVcmb_Delete.bind("<<ComboboxSelected>>", lambda event: DeleteOptions())
The second problem is with your use of the SVcmb_Delete.current() method in the DeleteOptions() function because it returns the index of the the current entry text, not the entry text itself. To obtain that, you should use the get() method. With that correction made the function should look like something like this:
def DeleteOptions():
if SVcmb_Delete.get() == "Monthly":
HideDeleteOptions()
lblMonthly.grid(row=4)
SVcmb_Monthly.grid(row=4, column=1)
if SVcmb_Delete.get() == "Period":
HideDeleteOptions()
lblFrom.grid(row=4)
txtFrom.grid(row=4, column=1)
lblTo.grid(row=5)
txtTo.grid(row=5, column=1)
Lastly I also strongly suggest that you read and start following the PEP 8 - Style Guide for Python Code to make your code more readable.
I'm new to Python and trying to understand the code below. This code should create 3 frame objects that can be rotated to the front to swap pages.
The APP class should create these 3 new objects. I'm not sure that it is.
What I am trying to be is modify a label on the Dashboard class through a function in that class.
i.e. Dashboard.update()
Can someone please explain how the APP class is creating frame objects for the 3 windows. I'm now sure that it is and I think I am trying to update text in the class and not a object of that class.
### Import libaries
import requests
import pyodbc
import tkinter as tk
from tkinter import *
from tkinter import messagebox, ttk
### Set global fonts
TITLE_FONT = ("Verdana", 12)
### Define the applicaiton class
class APP (Frame):
### Build the init function to create the container and windows
def __init__ (self, master=None ):
Frame.__init__(self, master)
self.grid()
# Set the application window title
self.master.title("Playing Around with Classes")
# set the size of the row height for the application
self.master.rowconfigure(0, weight=1)
self.master.rowconfigure(1, weight=35)
self.master.rowconfigure(2, weight=1)
self.master.rowconfigure(3, weight=1)
#Row 0 - Title area
label = tk.Label(master, text="Playing Around with Classes", font=TITLE_FONT)
label.grid(row=0, columnspan=3, sticky="nsew")
# Main presentation are
Frame2 = Frame(master, bg="#263D42")
Frame2.grid(row = 1, column = 0, rowspan = 1, columnspan = 3, sticky = "nsew")
# List of pages
self.frames = {}
# i think this loop defines the class objects
for F in (NetworkMap,AuthorPage,Dashboard):
frame = F(Frame2, self)
self.frames[F] = frame
frame.grid(row=0, column=1, sticky="nsew")
self.show_frame(Dashboard)
### Define the show_frame function that will bring the selected fram to the front so it can be viewed
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
### Create a class for the Dashboard page. This will also be the start page when the application starts
class Dashboard (tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent, bg="#263D42")
label = tk.Label(self, text="Text to change", font=TITLE_FONT, bg="#263D42", fg="white", pady = 20)
label.grid(row=0, column=0, sticky="nsew")
def update(self):
self.allPapersLabel.config(text="Changed Text")
### Create a page to get the Author detasil
class AuthorPage (tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Get Author", font=TITLE_FONT)
label.grid(row=0, column=0, sticky="nsew")
class NetworkMap (tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Network Map", font=TITLE_FONT)
label.grid(row=0, column=0, sticky="nsew")
def changeText():
Dashboard.update()
changeText()
root = tk.Tk()
root.geometry("600x800+100+100")
app = APP(master=root)
app.mainloop()
Can someone please explain how the APP class is creating frame objects for the 3 windows
The crux is here:
for F in (NetworkMap,AuthorPage,Dashboard):
frame = F(Frame2, self)
self.frames[F] = frame
frame.grid(row=0, column=1, sticky="nsew")
Keep in mind that NetworkMap, AuthorPage and Dashboard are classes. Classes are callables that function as a factory for new instances of the particular type.
So basically the for-loop makes F an alias (or label) for each of those classes and calls them in turn to instantiate an object.
Keep in mind that what we call "variables" in most languages are refered to as names in Python. From the language manual:
Names refer to objects. Names are introduced by name binding operations.
So F is nothing more than a handy label to refer to the three classes. The for-loop header binds the name to the classes.
BTW: This looks like a re-implementation of a ttk.Notebook. I would suggest to use that instead.
Edit
The frames are saved into the frames dictionary the App object. So in all of the methods of App instances you can access self.frames to get the individual frames.
The somewhat weird (to me at least) thing is that the class object of the frame is used as the key for selecting from the dictionary.
So using self.frames[AuthorPage] in methods of App should return the AuthorPage frame.
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()
I am trying to access the Text widget defined in class FirstPage from outside of the class.
I tried to solve this problem by creating a new instance of FirstPage, but could not find the right arguments to use. Also tried to use instance of GUI to gain the access, but unsuccessfully.
My problem is solved when I can use text.insert(0.0, t) from outside of the classes. It would help me modify the text displayed with Tkinter by functions that are not directly related with the GUI.
The origin of the code I am trying to use is found: Switch between two frames in tkinter
Also I removed lines that were not necessary for this question..
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
text = tk.Text(self , height=25, width=80)
text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.mainloop()
EDIT:
Here is the working code:
import Tkinter as tk
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.geometry(self, '580x410')
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 = {}
frame = FirstPage(container, self)
self.frames[FirstPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame = self.frames[FirstPage]
frame.tkraise()
page_name = FirstPage.__name__
self.frames[page_name] = frame
def get_page(self, page_name):
return self.frames[page_name]
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.text = tk.Text(self , height=25, width=80)
self.text.grid(column=0, row=0, sticky="nw")
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")
app.mainloop()
There's nothing special you need to do. As with any python object, you simply need a reference to the object in order to manipulate it.
The concept in the code you started with is to have a "controller" that controls access to all of the pages, since that object is where the pages are created. You can add a function in the controller that gives you a reference to a page, and then you can use that to call a function on that page.
Here's the changes you need to make to the controller:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
...
page_name = FirstPage.__name__
self.frames[page_name] = frame
...
def get_page(self, page_name):
return self.frames[page_name]
You also need to modify FirstPage to keep a reference to the widget so that you can access it later:
class FirstPage(tk.Frame):
def __init__(self, parent, controller):
...
self.text = tk.Text(...)
...
From within any other code you can now access the text widget via get_page (but your pages must save a reference to the controller for this to work).
class AnotherPage(tk.Frame):
def __init__(self, parent, controller):
...
self.controller = controller
...
def some_function(self):
...
first_page = self.controller.get_page("FirstPage")
text = first_page.text.get("1.0", "end-1c")
...
first_page.text.insert("end", "some new text\n")
Note that this technique works outside of any GUI pages. In your code, app is the controller, so you can do something like this:
app = GUI()
app.get_page("FirstPage").text.insert("1.0", "Hello, world")