I have a hard time understanding Object Oriented Programming with tkinter..
For this example I want the user to input some data then show it in a new frame. But the problem is the input seems to get passed before the "Check" button is pressed.
How can I wait to pass the data to PageOne until the "Check" button is pressed?
This is the code I have written so far (with the help of some example code):
import tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self) # master
container.grid()
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
class StartPage(tk.Frame): # inherit from tk.Frame
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
tk.Label(self, text="Floor Time: ", font='helvetica 30').grid(row=0, sticky="w")
tk.Label(self, text="MSL: ", font='helvetica 30').grid(row=1, sticky="w")
self.e1 = tk.Entry(self, width=52)
self.e2 = tk.Entry(self, width=52)
self.e1.grid(row=0, column=1, sticky="w", pady=5, ipady=15)
self.e2.grid(row=1, column=1, sticky="w", pady=5, ipady=15)
self.e1.insert(0, "<format = hours>")
button1 = tk.Button(self, text='Check', width="20", height="3", font='helvetica 20', bg="green",
command=lambda: controller.show_frame(PageOne))
button1.grid(row=2, column=0, sticky="w", pady=4, padx=4)
button2 = tk.Button(self, text='Quit', width="20", height="3", font='helvetica 20', bg="red",
command=self.quit)
button2.grid(row=2, column=1, sticky="e", pady=4, padx=4)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
x = self.controller.get_page(StartPage)
print(x.e1.get())
print(x.e2.get())
if __name__ == '__main__':
b = Window()
b.title("SMD Condition Tool")
b.mainloop()
One way is to override the tkraise method:
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.x = self.controller.get_page(StartPage)
def tkraise(self):
print(self.x.e1.get())
print(self.x.e2.get())
tk.Frame.tkraise(self) # call superclass
Related
I tried to create an exit button to close Tkinter application. So far everything works as planned, but if I press on "EXIT" button, I receive an error. I don't know, how can I improve that. I tried to use function way to solve this problem, but it doesn't work as well (Unambiguously by my mistake). Can anyone advise how best to integrate this button?
My code:
from tkinter import *
import tkinter.ttk as ttk
class CollegeApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (StartPage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.startMenu()
def startMenu(self):
heading = Label(self, text="College Tournament Points\n Count Software",
font=('Arial', 25))
heading.grid(row=0, column=0, columnspan=2, padx=240, pady=40)
start_Btn = Button(self, text="Start", font="Arial 16", width=8,
command=lambda: self.controller.show_frame(PageTwo))
start_Btn.grid(row=1, column=0, padx=30)
exit_Btn = Button(self, text="EXIT", font="Arial 16", width=8,
command=exitButton)
exit_Btn.grid(row=1, column=1, padx=30)
def starting_Program():
pass
class exitButton(Button):
def __init__(self, parent):
Button.__init__(self, parent)
self[Button] = parent.destroy
self.pack(BOTTOM)
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
ttk.Label(self, text='This is page two').grid(padx=(20, 20), pady=(20, 20))
button1 = ttk.Button(self, text='Previous Page',
command=lambda: self.controller.show_frame(StartPage))
button1.grid()
if __name__ == '__main__':
app = CollegeApp()
app.geometry("800x500")
app.title('Points Counter')
app.mainloop()
Error:
/Users/aleks/PycharmProjects/PointCounter/venv/bin/python /Users/aleks/PycharmProjects/PointCounter/main.py
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 1892, in __call__
return self.func(*args)
TypeError: __init__() missing 1 required positional argument: 'parent'
Process finished with exit code 0
try doing this
from tkinter import *
import tkinter.ttk as ttk
class CollegeApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (StartPage, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.startMenu()
def startMenu(self):
heading = Label(self, text="College Tournament Points\n Count Software",
font=('Arial', 25))
heading.grid(row=0, column=0, columnspan=2, padx=240, pady=40)
start_Btn = Button(self, text="Start", font="Arial 16", width=8,
command=lambda: self.controller.show_frame(PageTwo))
start_Btn.grid(row=1, column=0, padx=30)
exit_Btn = Button(self, text="EXIT", font="Arial 16", width=8,
command=self.controller.destroy)
exit_Btn.grid(row=1, column=1, padx=30)
def starting_Program():
pass
class PageTwo(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.make_widget()
def make_widget(self):
ttk.Label(self, text='This is page two').grid(padx=(20, 20), pady=(20, 20))
button1 = ttk.Button(self, text='Previous Page',
command=lambda: self.controller.show_frame(StartPage))
button1.grid()
if __name__ == '__main__':
app = CollegeApp()
app.geometry("800x500")
app.title('Points Counter')
app.mainloop()
I had a problem with passing a variable from one page to the other, its works now, but if I want to use it on a label, it writes PA_VAR0. I read that ".get()" should be used in that case, but it still doesn't work that way. (with .get() it don't even passes the variable). I tried to set a new variable with tk.StringVar() function, but it still didn't work
import tkinter as tk
from tkinter import ttk
class example(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.shared_data = {
"variable": tk.StringVar()
}
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 (page1, page2):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(page1)
def get_page(self, page_class):
return self.frames[page_class]
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class page1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, text="first page")
label.grid(row=0, column=4, padx=10, pady=10)
button1 = ttk.Button(self, text="turn page",
command=lambda: self.pageturn())
button1.grid(row=3, column=1, padx=10, pady=10)
def pageturn(self):
self.controller.shared_data["variable"] = 'string i wanna pass'
print("variable set here: ", self.controller.shared_data["variable"])
self.controller.show_frame(page2)
class page2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, text=self.controller.shared_data["variable"])
label.grid(row=0, column=4, padx=10, pady=10)
label = ttk.Label(self, text=self.controller.shared_data["variable"].get())
label.grid(row=1, column=4, padx=10, pady=10)
button3 = ttk.Button(self, text=self.controller.shared_data["variable"],
command=lambda: print(self.controller.shared_data["variable"]))
button3.grid(row=8, column=10, padx=10, pady=10)
button2 = ttk.Button(self, text="if fuction sees variable",
command=lambda: self.ifok())
button2.grid(row=9, column=10, padx=10, pady=10)
def ifok(self):
if self.controller.shared_data["variable"] == 'string i wanna pass':
print("ok")
app = example()
app.mainloop()
You overwrite self.controller.shared_data["variable"] by a string inside pageturn():
self.controller.shared_data["variable"] = 'string i wanna pass'
You should use .set() instead:
self.controller.shared_data["variable"].set('string i wanna pass')
Full updated code:
import tkinter as tk
from tkinter import ttk
class example(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.shared_data = {
"variable": tk.StringVar()
}
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 (page1, page2):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(page1)
def get_page(self, page_class):
return self.frames[page_class]
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class page1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, text="first page")
label.grid(row=0, column=4, padx=10, pady=10)
button1 = ttk.Button(self, text="turn page",
command=lambda: self.pageturn())
button1.grid(row=3, column=1, padx=10, pady=10)
def pageturn(self):
self.controller.shared_data["variable"].set('string i wanna pass') ### changed = to .set()
print("variable set here: ", self.controller.shared_data["variable"].get()) ### called .get()
self.controller.show_frame(page2)
class page2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = ttk.Label(self, textvariable=self.controller.shared_data["variable"]) ### changed text to textvariable
label.grid(row=0, column=4, padx=10, pady=10)
label = ttk.Label(self, text=self.controller.shared_data["variable"].get())
label.grid(row=1, column=4, padx=10, pady=10)
button3 = ttk.Button(self, textvariable=self.controller.shared_data["variable"], ### changed text to textvariable
command=lambda: print(self.controller.shared_data["variable"].get()))
button3.grid(row=8, column=10, padx=10, pady=10)
button2 = ttk.Button(self, text="if fuction sees variable",
command=lambda: self.ifok())
button2.grid(row=9, column=10, padx=10, pady=10)
def ifok(self):
if self.controller.shared_data["variable"].get() == 'string i wanna pass': ### called .get()
print("ok")
app = example()
app.mainloop()
Currently the program opens up an empty blank window when trying to import images as buttons. Without trying to import the image as buttons, the code works fine however. Can anyone help fix this code up or point me in the right direction?
from tkinter import*
root = Tk()
class app(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = 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, SignIn, SignUp):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = Label(self, text="Start Page")
label.pack(pady=10,padx=10)
img1 = PhotoImage(file="signinbtn.png")
button = Button(self, image=img1,
command=lambda: controller.show_frame(SignIn))
button.pack()
img2 = PhotoImage(file="signupbtn.png")
button2 = Button(self, image=img2, text="Sign Up",
command=lambda: controller.show_frame(SignUp))
button2.pack()
class SignIn(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = Label(self, text="Sign In")
label.pack(pady=10,padx=10)
button1 = Button(self, text="Back to Start Page",
command=lambda: controller.show_frame(StartPage))
button1.pack()
class SignUp(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
label = Label(self, text="Sign Up")
label.pack(pady=10,padx=10)
button1 = Button(self, text="Back to Start Page",
command=lambda: controller.show_frame(StartPage))
button1.pack()
root.mainloop()
The empty window is created by the second code line:
root = Tk()
Apart from this line you have just class definitions. As you never instantiate any of the classes no code from any class is run.
I have been searching a lot and I still don't know, what is the problem. I have two windows asking for some values and as a result different functions are executed. I tried to bind an "Enter" key to buttons of different windows (so different functions should be executed), but this doesn't work.
Only thing that i could do is to bind key to the whole Frame, then unbind it and bind one more time. It works, but i'm curious why i can't link "Enter" key to separate buttons while no error appear. Thanks in advance!
Here is my code:
import tkinter as tk
class Page(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.shared_data = {
"number_of_frequencies": tk.StringVar(),
"frequencies": []}
container = tk.Frame(self)
container.grid()
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def create_entry_widget(self, i):
self.number = tk.StringVar()
self.number.set('')
self.shared_data["frequencies"].append(self.number)
new_widget = tk.Entry(self.master, textvariable=self.number)
return new_widget
def show_frame(self, c):
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[c]
frame.grid()
global number_of_frequencies
try:
number_of_frequencies = int(self.shared_data["number_of_frequencies"].get())
for i in range(number_of_frequencies):
tk.Label(self.master, text="Frequency "+str(i+1)).grid(row=i, column=0, padx=10, pady=5)
self.entry_widgets = self.create_entry_widget(i)
self.entry_widgets.grid(row=i, column=1, padx=10, pady=5)
page1 = self.get_page(PageOne)
tk.Button(self, text="Exit",
command=lambda: self.quitALL()).grid(row=i+1, column=0, padx=10, pady=10)
self.button1 = tk.Button(self, text = " Done ",
command=lambda: [page1.enter_freqs(), self.quitALL()])
self.unbind_all('<Return>') # work
self.bind('<Return>', (lambda a: [page1.enter_freqs(), self.quitALL()])) # work
# self.button1.bind('<Return>', (lambda a: [page1.enter_freqs(), self.quitALL()])) #doesn't work
self.button1.grid(row=i+1, column=1, padx=10, pady=10)
frame.tkraise()
except:
frame.tkraise()
def get_page(self, page_class):
return self.frames[page_class]
def quitALL(self):
self.destroy()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.text = tk.Text(self, bg='gray93', bd=0, height=1, width=30, font=('Helvetica', 14))
self.text.insert(tk.INSERT, "Please enter number of frequencies:")
self.text.grid(padx=10, pady=10)
entry = tk.Entry(self, textvariable=self.controller.shared_data["number_of_frequencies"])
entry.grid(padx=10)
self.controller.bind('<Return>', (lambda a: controller.show_frame(PageOne))) # work
self.button = tk.Button(self, text=" Done ",
command=lambda: controller.show_frame(PageOne))
# self.button.bind('<Return>', (lambda a: controller.show_frame(PageOne))) # doesn't work
self.button.grid(padx=10, pady=10)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.freq_list = self.controller.shared_data["frequencies"]
self.freq_list_int = []
def enter_freqs(self):
for freq in self.freq_list:
self.freq_list_int.append(int(freq.get()))
self.func(self.freq_list_int)
def func(self, freq_list):
print('some stuff')
if __name__ == "__main__":
app = Page()
app.mainloop()
So I've simplified my code down to the basics to try and get this to work. The mainpage, which you press 'play' on takes you to the page 'MathsQ1', this is the first question. The page has a question and four button, one of them being the right button. I've been able to make two different commands for the right and wrong buttons, that take you to the next question. But also I want the right function to add 1 to a counter which is called 'correct'. Once you finish the last question, it will take you the page 'MathsEnd' which will then display your score as "congrats you got (correct)/2" and I would really appreciate it if someone told me what I'm doing wrong. Thanks.
The code:
import tkinter as tk
from tkinter import ttk
#INITIALIZING
class MegaQuiz(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "THE MEGA POP QUIZ")
self.geometry("930x2000")
self.highlightbackground="#FF846B"
self.correct = 0
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 (MainPage, MathsQ1, MathsQ2, MathsEnd):
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("MainPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.configure(background="#FFC2B5")
#Play BUTTON
tk.Button(self, text="PLAY", width=10,
command = lambda: controller.show_frame("MathsQ1")) .grid(column=1, row=11, columnspan=8, pady=20, sticky="nesw")
class MathsQ1(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.configure(background="#FFC2B5")
tk.Label(self, text="Maths Quiz") .grid(column=2, row=0)
tk.Label(self, text="Question 1") .grid(column=4, row=1, columnspan=5, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=4, row=3, sticky="nesw")
tk.Button(self, text="right",
command=self.right) .grid(column=4, row=5, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=6, row=3, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=6, row=5, sticky="nesw")
def wrong(self):
self.controller.show_frame("MathsQ2")
def right(self):
self.controller.show_frame("MathsQ2")
self.controller.correct = self.controller.correct + 1
class MathsQ2(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.configure(background="#FFC2B5")
tk.Label(self, text="Maths Quiz") .grid(column=2, row=0)
tk.Label(self, text="Question 2") .grid(column=4, row=1, columnspan=5, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=4, row=3, sticky="nesw")
tk.Button(self, text="right",
command=self.right) .grid(column=4, row=5, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=6, row=3, sticky="nesw")
tk.Button(self, text="wrong",
command=self.wrong) .grid(column=6, row=5, sticky="nesw")
def wrong(self):
self.controller.show_frame("MathsEnd")
def right(self):
self.controller.show_frame("MathsEnd")
self.controller.correct = self.controller.correct + 1
class MathsEnd(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.configure(background="#FFC2B5")
tk.Label(self, text=("congrats you got " + str(self.controller.correct) + "/2")) .grid(column=1, row=1)
tk.Button(self, text="Back To Home",
command = lambda: controller.show_frame("MainPage")) .grid(column=6, row=10, sticky="nesw", pady=10)
#RUNNING PROGRAM
app = MegaQuiz()
app.mainloop()
You initialise MathsEnd when you initialise the app, so the label that shows the score uses the value of controller.correct at the start of the program, which is 0.
You need to create a method in your MathsEnd class which updates the label:
class MathsEnd(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.configure(background="#FFC2B5")
# create label, pack seperately so it can be configured later
self.score = tk.Label(self)
self.score.grid(column=1, row=1)
# initalise using method
self.update_label()
tk.Button(self, text="Back To Home",
command = lambda: controller.show_frame("MainPage"))
.grid(column=6, row=10, sticky="nesw", pady=10)
# new method to update label contents
def update_label(self):
self.score.config(text='Congrats you got %s/2' % self.controller.correct)
(I had to make some changes to how you create the label as well).
You then need to call this method to update the score whenever it is changed.
def right(self):
self.controller.correct += 1
# update score label
self.controller.frames['MathsEnd'].update_label()
self.controller.show_frame('MathsQ2')