Reputation: 25
Say I have a collection of classes using Tkinter:
class MAIN(tk.Tk):
def __init__(self, *args):
...
class Page1(tk.Frame): #Login page Frame
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
...
def Method(self):
...
class Page2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
...
Say Page2
needs to use Method(self)
from Page1
, how would it be designed?
Assuming that we have Tkinter frames, each frame is written in a class format with all the frames initiated in the MAIN
class.
What I am trying to do, for a while now with no success, is to get my main menu page to update it's List Box which takes and displays arguments from a database. For example, When I insert another row in the db using Page2
it has to bring me back to Page1
, However, Page1
is static and does not update. I'm learning UI, and stuck on this for days now. I have a function that deletes the list box arguments, and a function to display arguments again, But, they are in Page1
and once I raise Page1
back up from Page2
, Page1
won't update arguments.
EDIT: This is the Class with the method I need to use elsewhere (Continue below)
class mainMenuPage(tk.Frame): #Main Menu Frame, consists of the main navigation page, List box
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.listBox()
#self.listBox_delete(numrow)
self.label = tk.Label(self, text="Search employee by typing his/her last name:")
self.label.grid(row=0, column=1)
self.entry_username = tk.Entry(self)
self.entry_username.grid(row=0, column=3)
button = tk.Button(self, text='Add Employee', width=15,
command=lambda: (controller.clear_shared_data(), controller.raise_frame(addEmpFrame)))
button.grid(row=2, column=3)
button = tk.Button(self, text='Remove Employee', width=15, command=lambda: controller.raise_frame(removeEmpFrame))
button.grid(row=3, column=3)
button = tk.Button(self, text='Employee Lookup', width=15, command=lambda: controller.raise_frame(EmpLookupFrame))
button.grid(row=4, column=3)
button = tk.Button(self, text='Calculate Pay Roll', width=15, command=lambda: controller.raise_frame(calcPayRollFrame))
button.grid(row=5, column=3)
def listBox(self):
self.row = []
self.scrollbar = tk.Scrollbar(self)
self.scrollbar.grid(row=2, column=2)
connection: Connection = pymysql.connect(host='localhost',
port=3306,
user='root',
password='root',
db='hrdb')
cursorObject = connection.cursor()
cursorObject.execute('SELECT `firstName`,`lastName` from `employeeprofile`')
numrows = int(cursorObject.rowcount)
for x in range(0, numrows):
self.row.append(cursorObject.fetchone())
self.list1 = tk.Listbox(self, height=10, width=35)
for x in self.row:
self.list1.insert(END, x)
self.list1.grid(row=2, column=0, columnspan=2)
self.scrollbar.config(command=self.list1.yview)
return numrows
def listBox_delete(self, numrow):
self.list1 = tk.Listbox(self, height=10, width=35)
for x in range(0, numrow):
self.list1.delete('0', 'end')
self.list1.grid(row=2, column=0, columnspan=2)
self.list1 = tk.Listbox(self, height=10, width=35)
The class, which is a Frame of my UI, is basically a main menu navigator with a list box of values from mySQL. The problem here, is after using any other Frame or operation within the program which leads me back to the main menu, the program wont update the List Box list1
, i.e when I add an employee
into the system, the new employee shows in the list box only after I restart the app. (The methods work with the db). I know the reason is the frame is static, and my list box is created once and nothing is manipulated since, but, I can't find a way to basically generate the list box every time the frame is called, and in turn get all the values from the db again to update the list box.
Upvotes: 1
Views: 955
Reputation: 10542
The only way to reference one frame from the other is through the controller. Consider this example:
import tkinter as tk
class MAIN(tk.Tk):
def __init__(self, *args):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (Page1, Page2):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def get_frame(self, page_name):
frame = self.frames[page_name]
return frame
class Page1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
tk.Label(self, text='Page 1').pack()
def Method(self):
print('Page 1 method')
class Page2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tk.Label(self, text='Page 2').pack()
tk.Button(self, text='Raise Page 1 and execute function', command=self.exit).pack()
def exit(self):
self.controller.show_frame("Page1")
self.controller.get_frame("Page1").Method()
app = MAIN()
app.mainloop()
Page1
has no reference to Page2
but it does have controller
which is a reference to the instance of MAIN
. Because MAIN
is the controller, it "knows" all the frames. I gave MAIN
the get_frame
method that returns a reference to the frame by its name. Therefore, to reference Page1
from any other frame, you can do self.controller.get_frame("Page1")
. To then call method Method()
, simply add that to make self.controller.get_frame("Page1").Method()
Because you haven't shown your actual main class, I'm not sure on how you save references to your frames, but I think you used this as a start. From my example I think the concept should be clear.
Another way to accomplish what you want would be to make sure a method is called every time Page1
is made visible, to do this follow the explanation in the answer to How would I make a method which is run every time a frame is shown in tkinter.
Upvotes: 2