Refresh a tkinter frame on button press - python-3.x

I am utilising code from Switch between two frames in tkinter to make my GUI. I have a frame with refresh and restart buttons.
My original idea was for the restart button to go to the start page as in the code below but if this frame is called again it has the entries from the previous attempt still showing.
I've tried.destroy() for the refresh button but then I get an traceback message when I call the PLG frame again.
For the restart button, how would I close the PLG frame, go to the Start page and then be able to select PLG again?
For the refresh button, how would I remove the entries in the entry widget and text arrear so that another entry can be made and new answer returned?
class PLG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter the engine size (cc) below", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
vcmd = (self.register(self.onValidate), '%S')
self.weight_entry = tk.Entry(self, validate='key', vcmd = vcmd)
self.weight_entry.pack(pady = 10)
tk.Button(self, text='Click here to display price', command=self.show_option).pack()
self.text = tk.Text(self)
self.text.pack(pady = 10)
self.text.config(state='disabled')
restart_button = tk.Button(self, text="Restart",
command=self.restart)
restart_button.pack()
refresh_button = tk.Button(self, text="Refresh", command=self.refresh).pack()
refresh_button.pack()
def onValidate(self,S):
if S in ['0','1','2', '3', '4', '5', '6', '7', '8', '9']:
return True
else:
self.bell() # adds a sound effect to error
self.text.delete(1.0, tk.END) # deletes the error message if valid entry provided
self.text.insert(tk.END, "Invalid entry. Please try again.") # displays an error message if a number not provided in entry widget
return False
def restart(self):
self.refresh()
show_frame("StartPage")
def refresh(self):
self.text.config(state='normal')
self.weight_entry.delete(0,tk.END)
self.text.delete("1.0", "end")
Advice on both elements would be appreciated.

The OP's question was about clearing input fields so prior input isn't still in the page when you expected to see empty fields for fresh input. I'm posting the finished code while omitting features of the OP's original code which were irrelevant to his question so the solution could easily be seen in its full context. I had been looking to solve this problem with this same frame-switching code from Bryan Oakley's famed tutorials on this topic. I also included an alternate version using grid_remove instead of tkraise since this is how I had solved the problem of ever-active but unseen frames trying to participate in the focus traversal as the user tries to tab through the page. It also kept the frames from all trying to be the same size.
import tkinter as tk
class SampleApp(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)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
# alternate ways to create the frames & append to frames dict: comment out one or the other
for F in (StartPage, PLG):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
# self.frames["StartPage"] = StartPage(parent=container, controller=self)
# self.frames["PLG"] = PLG(parent=container, controller=self)
# self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
# self.frames["PLG"].grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
# alternate version of show_frame: comment out one or the other
def show_frame(self, page_name):
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
# def show_frame(self, 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="start page")
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One", command=lambda: controller.show_frame("PLG"))
button1.pack()
button2 = tk.Button(self, text="focus traversal demo only")
button2.pack()
button2.focus_set()
button3 = tk.Button(self, text="another dummy button")
button3.pack()
lbl = tk.Label(self, text="tkraise messes up focus traversal\nwhich you can see by testing the two versions of show_frame.()\nUsing grid_remove instead of tkraise solves that,\nwhile preventing frames from being unable to resize to fit their own contents.")
lbl.pack()
class PLG(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Enter something below; the two buttons clear what you type.")
label.pack(side="top", fill="x", pady=10)
self.wentry = tk.Entry(self)
self.wentry.pack(pady = 10)
self.text = tk.Text(self)
self.text.pack(pady = 10)
restart_button = tk.Button(self, text="Restart", command=self.restart)
restart_button.pack()
refresh_button = tk.Button(self, text="Refresh", command=self.refresh)
refresh_button.pack()
def restart(self):
self.refresh()
self.controller.show_frame("StartPage")
def refresh(self):
self.wentry.delete(0, "end")
self.text.delete("1.0", "end")
# set focus to any widget except a Text widget so focus doesn't get stuck in a Text widget when page hides
self.wentry.focus_set()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()

The first step is to have your button call a proper function rather than using lambda. Unless you understand why and when to use lambda, it usually just makes the code harder to write and understand.
Once you have it call a function, you can use the function to clear the entries.
Example:
class PLG(tk.Frame):
def __init__(self, parent, controller):
...
tk.Button(self, text="Restart", command=self.restart)
tk.Button(self, text="Refresh", command=self.refresh)
...
def restart(self):
self.refresh()
self.controller.show_frame("StartPage")
def refresh(self):
self.weight_entry.delete(0, "end")
self.text.delete("1.0", "end")

Simple way:
Just call that window with button or bind in which frame lies.
works good for windows refresh.

Related

Trouble Binding a Button in a Class - Python (Tkinter)

Just started learning Tkinter and was hoping someone could help me. I've been trying to bind a keyboard character (Enter button) to a tk button following this example and not getting anywhere.
Say I take the button (Enter) and try bind it nothing happens:
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
If I bind to self instead using Lambda nothing happens also. I can get it to trigger if I remove the lambda but that's not the desired outcome
self.bind('<Return>', lambda:self.retrieve_Input(t))
My Code:
import sys
import tkinter as tk
from tkinter import ttk
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Test Application")
self.lift() #Bringing the GUI to the front of the screen
main_frame = tk.Frame(self, height=400, width=600) #Creating a main Frame for all pages
main_frame.pack(side="top", fill="both", expand=True)
main_frame.grid_rowconfigure(0, weight=1) #Configuring the location of the main frame using grid
main_frame.grid_columnconfigure(0, weight=1)
# We will now create a dictionary of frames
self.frames = {}
for F in (MainPage, CompletionScreen): #Add the page components to the dictionary.
page = F(main_frame, self)
self.frames[F] = page #The windows class acts as the root window for the frames.
page.grid(row=0, column=0, sticky="nsew")
self.show_page(MainPage) #Method to switch Pages
def show_page(self, cont):
frame = self.frames[cont]
frame.tkraise()
##########################################################################
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#switch_window_button = tk.Button(self, text="Go to the Side Page", command=lambda: controller.show_page(SidePage))
#switch_window_button.pack(side="bottom", fill=tk.X)
tk.Label(self, text="Project Python Search Engine", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
tk.Label(self, text="Song", bg='white').pack()
tk.Label(self, text="", bg='white').pack()
t = tk.Entry(self, bg='white', width = 50)
t.pack()
tk.Label(self, text="", bg='white').pack()
Enter = tk.Button(self, text='Search', command= lambda:self.retrieve_Input(t))
Enter.pack()
tk.Button(self, text="Latest Popular Songs", command=lambda:self.Popular_Songs(t)).pack() #Line 210 onwards
Enter.bind('<Return>', lambda:self.retrieve_Input(t))
def retrieve_Input(self, t):
print ("work")
print (t)
class CompletionScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Completion Screen, we did it!")
label.pack(padx=10, pady=10)
switch_window_button = ttk.Button(
self, text="Return to menu", command=lambda: controller.show_page(MainPage)
)
switch_window_button.pack(side="bottom", fill=tk.X)
if __name__ == "__main__":
App = windows()
App.mainloop()
I'm not really sure what I'm missing
Answer: The button probably doesn't have the keyboard focus. When I run your code and then use the keyboard to move the focus to the button, your binding works. You probably want to bind to the entry widget rather than the button since that's what will have the keyboard focus. – Thanks Bryan Oakley

How to get/ print a current value in a tkinter radiobutton

I am trying to print a current value of a radiobutton in a oop structured application. I have seen a couple of solutions on how to write a working code (for example https://www.tutorialspoint.com/python/tk_radiobutton.htm) but when I try to use it in my classes I am doing something wrong.
I have the following background code:
class OutFileGUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, ".Out GUI")
container = tk.Frame(self)
container.pack(side='top', fill='both', expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
menubar = tk.Menu(container)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="Read file", command=self.open_file)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="File", menu=filemenu)
tk.Tk.config(self, menu=menubar)
self.frames = {}
for F in (StartPage, MainPage, ConvBehaviour, GaussPoints):
frame = F(parent=container, controller=self) =
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
def open_file(self):
name = askopenfilename(
filetypes=((".Out File", "*.out"), ("All Files", "*.*")),
title="Choose a file.")
message = ("File location: " + str(name))
print(message)
In the frame below I am trying to place a radiobutton and depending on the selected value by a user print the value.
class ConvBehaviour(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="See behaviour", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(MainPage))
button1.pack()
button2 = ttk.Button(self, text="See points statistics",
command=lambda: controller.show_frame(GaussPoints))
button2.pack()
norms = [("norm 1", 1),
("norm 2", 2),
("norm 3", 3)]
#Here is the problem
self.v1 = tk.IntVar()
self.v1.set(1)
for text, num in norms:
radiobutton = tk.Radiobutton(self, text=text, value=num, variable=self.v1, command=self.show_choice)
radiobutton.pack()
def show_choice(self):
print('int ' + str(self.v1.get()))
The problem I have is that the method show_choice(self) does not work in the current implementation. It prints the current value set in self.v1.set(1) instead of either of 1, 2, 3 depending on a user selection. Where does the problem lie?
Try to build a minimal example as possible such that the problem of the variable not catching the user input still showing up while removing as much background noise as possible. See also https://stackoverflow.com/help/mcve
For example, the most minimal example only containing the radiobuttons I can build from your code is the following (and it is working as intended):
import tkinter as tk
def main():
root = tk.Tk()
norms = [("norm 1", 1),
("norm 2", 2),
("norm 3", 3)]
v1 = tk.IntVar()
v1.set(1)
def show_choice():
print('int ' + str(v1.get()))
for text, num in norms:
radiobutton = tk.Radiobutton(root, text=text, value=num, variable=v1,
command=show_choice)
radiobutton.pack()
root.mainloop()
if __name__ == '__main__':
main()
Try to build from there up - in small steps (maybe first try to add your own class inheriting from tk.Frame, etc.). This is essential: dont copy big chunks of code (with confusing classes and conventions) from someone else, but start from small working examples and work upwards.

How to display a dataframe in tkinter

I am new to Python and even newer to tkinter.
I've utilised code from stackoverflow (Switch between two frames in tkinter) to produce a program where new frames are called and placed on top of each other depending on what options the user selects. A stripped down version of my code is below. There are a lot more frames.
import tkinter as tk
from tkinter import font as tkfont
import pandas as pd
class My_GUI(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")
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, Page_2):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
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="Welcome to....", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Option selected",
command=lambda: controller.show_frame("Page_2"))
button1.pack()
class Page_2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="The payment options are displayed below", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
#I want the able to be display the dataframe here
button = tk.Button(self, text="Restart",
command=lambda: controller.show_frame("StartPage"))
button.pack()
a = {'Option_1':[150,82.50,150,157.50,78.75],
'Option2':[245,134.75,245,257.25,128.63]}
df = pd.DataFrame(a,index=['a',
'b',
'c',
'd',
'e'])
print(df.iloc[:6,1:2])
if __name__ == "__main__":
app = My_GUI()
app.mainloop()
When Page_2 appears I want it to display a dataframe with the code below.
a = {'Option_1':[150,82.50,150,157.50,78.75],
'Option2':[245,134.75,245,257.25,128.63]}
df = pd.DataFrame(a,index=['a',
'b',
'c',
'd',
'e'])
print(df.iloc[:6,1:2])
I've searched SO e.g. How to display a pandas dataframe in a tkinter window (tk frame to be precise) (no answer provided) and other websites for an answer to similar question but without success.
How and where would I place my dataframe code selection to appear in the area I want when I select Page_2?
Check out pandastable.
It is quite a fancy library for displaying and working with pandas tables.
Here is a code example from their documentation:
from tkinter import *
from pandastable import Table, TableModel
class TestApp(Frame):
"""Basic test frame for the table"""
def __init__(self, parent=None):
self.parent = parent
Frame.__init__(self)
self.main = self.master
self.main.geometry('600x400+200+100')
self.main.title('Table app')
f = Frame(self.main)
f.pack(fill=BOTH,expand=1)
df = TableModel.getSampleData()
self.table = pt = Table(f, dataframe=df,
showtoolbar=True, showstatusbar=True)
pt.show()
return
app = TestApp()
#launch the app
app.mainloop()
and here a screenshot (also from their docs):
As a start, you could have a look at Label and Text widgets, that usually are used to display text in your GUI.
You could probably try something like:
class Page_2(tk.Frame):
def __init__(self, parent, controller):
# ... your code ...
global df # quick and dirty way to access `df`, think about making it an attribute or creating a function that returns it
text = tk.Text(self)
text.insert(tk.END, str(df.iloc[:6,1:2]))
text.pack()
# lbl = tk.Label(self, text=str(df.iloc[:6,1:2])) # other option
# lbl.pack() #
In the end, it really boils down to how fancy you want to be: the widgets are highly customizable, so you could achieve something very pleasing to the eye instead of the basic look of this example.
Edit:
I added a Combobox widget to select the option to display and a Button that prints it to the "display" widget of your choice.
from tkinter import ttk # necessary for the Combobox widget
# ... your code ...
class Page_2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="The payment options are displayed below", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
global df
tk.Label(self, text='Select option:').pack()
self.options = ttk.Combobox(self, values=list(df.columns))
self.options.pack()
tk.Button(self, text='Show option', command=self.show_option).pack()
self.text = tk.Text(self)
self.text.pack()
tk.Button(self, text="Restart",
command=lambda: controller.show_frame("StartPage")).pack()
def show_option(self):
identifier = self.options.get() # get option
self.text.delete(1.0, tk.END) # empty widget to print new text
self.text.insert(tk.END, str(df[identifier]))
The text that is displayed is the default string representation of a data-frame's column; a custom text is left as an exercise.

Tkinter updating labels in stacked frame windows

I am still dabbling with the stacked frames set up for a tkinter app and fairly new to OOP and Tkinter.
I have copied below code from another SO question and amended it slightly.
What I do not get done:
I want to update the label2 on the StartPage based on the click on Button2 on PageTwo from "Hello" to "5". But the update does not take place. What do I have to do differently to accomplish my task?
Many thanks in advance
import tkinter as tk
TITLE_FONT = ("Helvetica", 18, "bold")
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.var = tk.StringVar()
self.var.set('Hello')
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, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, 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=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
label2 = tk.Label(self, textvariable=self.controller.var, font=TITLE_FONT)
label2.pack(side="top", fill="x", pady=10)
label2.config(text=self.controller.var)
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
button2 = tk.Button(self, text="Change X",
command=lambda: self.calculate())
button2.pack()
def calculate(self):
self.controller.var = 5
return self.controller.var
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
There are many ways to accomplish what you want. Since you are using a StringVar for the label you want to change, the simplest solution is to update that variable with the new value:
def calculate(self):
self.controller.var.set(5)
This solution tightly couples the controller and the other class. That is, your PageTwo must know that the controller associates a StringVar with that label. If you modify the controller to use some other mechanism, you will have to change every other class that tries to set that variable.
A way to provide loose coupling is to have the controller provide an API for changing the value. In a sense, this is why controllers exist -- to control the flow of information between pages. The details of exactly how that value is stored and displayed is hidden from the other classes.
For example:
class SampleApp(...):
...
def set_label(self, value):
self.var.set(value)
class PageTwo(...):
...
def calculate(self):
self.controller.set_label(5)
The advantage to the above is that it provides loose coupling between the two classes. The other pages don't need to know that the label is implemented with a StringVar or a Label widget. The controller simply provides and interface that says "when you need to change variable X, call this function". As long as the controller maintains that function, you can change the implementation without having to modify every other class that may need to change that label.
See What is the difference between loose coupling and tight coupling in the object oriented paradigm? for more information about the difference between loose coupling and tight coupling.

switching windows in tkinter with classes

in a [former question][1] received a perfect script from #acw1668 for creating popup-windows (see below).
How can this be rewritten in a form that the new windows are not popups but just a switch from one page to the next (the listboxes/candvas are not necessarily needed here)?
Edit: tried to amend the code according to #Bryan Oakley's suggestions.
My issue here: I do not manage to pass the list lst from the GUI class to the other page classes without an error message:
File "/.spyder-py3/temp.py", line 25, in __init__
frame = F(parent=container, controller=self)
TypeError: __init__() missing 1 required positional argument: 'lst'
What am I missing here?
And I do not understand what's happening here:
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=container, controller=self,)
self.frames[page_name] = frame
If somebody could explain, please?
import tkinter as tk
from tkinter import ttk
class GUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.lst = ['a', 'b', 'c']
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", self.lst)
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
def show_popup(self, page, lst):
win = page(self, lst)
win.grab_set() # make window modal
self.wait_window(win) # make window modal
class StartPage(tk.Frame):
def __init__(self, parent, controller, lst):
tk.Frame.__init__(self, parent)
self.controller = controller
self.lst = lst
# ------------------------------------------------------------------- #
label = tk.Label(self, text="Check this out")
label.pack(pady=10,padx=10)
# ------------------- create buttons ---------------------------------
button1 = ttk.Button(self, text="show all",
width = 25, command=lambda:
controller.show_popup(App, self.lst))
button1.pack(pady=10, padx=10)
button2 = ttk.Button(self, text="show page one",
width = 25, command=lambda:
controller.show_frame(PageOne))
button2.pack(pady=10, padx=10)
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")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2")
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class App(tk.Toplevel):
def __init__(self, parent, lst):
tk.Toplevel.__init__(self, parent)
self.lst = lst
self.title('This is the pop up window')
self.geometry('400x200')
label = tk.Label(self, text=self.lst)
label.pack(side="top", fill="x", pady=10)
parent.grid_rowconfigure(0, weight = 1)
parent.grid_columnconfigure(0, weight = 1)
if __name__ == '__main__':
app = GUI()
app.mainloop()
[1]: http://stackoverflow.com/questions/41181809/how-to-open-and-close-another-window-with-scrollbar-in-tkinter-for-python-3-5/41182843?noredirect=1#comment69580999_41182843
Your class initializers are defined like this:
class StartPage(tk.Frame):
def __init__(self, parent, controller, lst):
In order to create an instance of this class it requires three arguments (plus self): parent, controller, and lst.
Now, let's look at how you're creating the instance:
frame = F(parent=container, controller=self,)
Notice how you have the parent and you have the controller, but you haven't passed in anything for lst. That is why the error states "missing 1 required positional argument: 'lst'" -- because you are literally missing one required argument named "lst".
To fix this problem, you simply need to provide this extra argument. For example:
frame = F(parent=container, controller=self, lst=self.lst)
HOWEVER, you probably shouldn't do that. The architecture of this little block of code you copied makes it possible to access values on the GUI class from any of the "page" classes without having to do any extra work.
Because this variable is an attribute of the GUI class, and you are passing a reference to the instance of the GUI class to each "page" (the controller attribute), you can access this data any time you want without having to pass it in at construction time. You can remove it from __init__ and from where you're creating the pages (ie: go back to the original code before your modifications), and then just use self.controller.lst whenever you need the value.
For example:
class SomePage(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def some_function(self):
print("The value of 'lst' is:", self.controller.lst)

Resources