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()
Related
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()
how can I get the value and placeholder in "optMenFunc" function from each iteration as value changes as I am working on below mentioned code, which has four classes, SampleApp, StartPage, PageOne, PageTwo. Page one has optionmenu with for loop. I am following the answer of this: Switch between two frames in tkinter
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 = {}
for F in (StartPage, PageOne):
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="This is the start page")
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button2 = tk.Button(self, text="Go to Page Two",
command=lambda: controller.show_frame("PageTwo"))
button1.pack()
button2.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")
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()
placeHolders = ['one', 'two']
for placeHolder in placeHolders:
self.options = tk.StringVar()
self.options.set('select')
self.menu = tk.OptionMenu(self, self.options, "miss", "mr", "mrs", command = self.optMenFunc)
self.menu.pack()
def optMenFunc(self, value):
print("i need to print here value with placeholder")
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
You can use a lambda similar to how you did in the other commands.
self.menu = tk.OptionMenu(self, self.options, "miss", "mr", "mrs", command=lambda v, ph=placeHolder: self.optMenFunc(v, ph))
and
def optMenFunc(self, value, placeholder):
print(f"value:{value} and placeholder:{placeholder}")
Note:
A: command=lambda v: self.optMenFunc(v, placeHolder)
B: command=lambda v, ph=placeHolder: self.optMenFunc(v, ph)
You have to set the argument in the lambda, as (B) is. If you do it the (A) way, placeHolder will equal whatever it's final iteration value is, regardless of which OptionMenu you select from. By making it an argument of the lambda you are saving it's current iteration value to ph.
I have two frames to switch around. In each frame, there is an Entry box. How do I set my input in the first Entry box in the first frame as the default text for the second Entry box in the second frame?
from tkinter import *
class Root(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 (PageOne, 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("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def quit(self):
self.destroy()
text = ''
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
global text
entry_box_1 = Entry(self, width=40)
entry_box_1.pack()
text = entry_box_1.get()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame('PageTwo'))
next_button.pack()
quit_button.pack()
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
entry_box_2 = Entry(self, width=40)
entry_box_2.insert(END, text)
entry_box_2.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
back_button.pack()
quit_button.pack()
if __name__ == "__main__":
root = Root()
root.mainloop()
My second Entry box keeps being blank when I switch to the second frame.
You are initializing the PageTwo frame when text variable is an empty string and while you are switching to this frame you are not inserting any value to entry_box_2.
I suggest you to invoke the insert method every time you are switching to the PageTwo frame. You can do this by creating a new switching method that can be similar to show_frame_with_default_text method in the following code.
from tkinter import *
class Root(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 (PageOne, 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("PageOne")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def show_frame_with_default_text(self, page_name, text):
frame = self.frames[page_name]
frame.entry_box.delete(0, END)
frame.entry_box.insert(0, text)
frame.tkraise()
def quit(self):
self.destroy()
class PageOne(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
entry_box_1 = Entry(self, width=40)
entry_box_1.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
next_button = Button(self, text="Next",
command=lambda: controller.show_frame_with_default_text('PageTwo', entry_box_1.get()))
next_button.pack()
quit_button.pack()
class PageTwo(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.controller = controller
self.entry_box = Entry(self, width=40)
self.entry_box.pack()
quit_button = Button(self, text="Quit Program",
command=lambda: controller.quit())
back_button = Button(self, text="Back",
command=lambda: controller.show_frame("PageOne"))
back_button.pack()
quit_button.pack()
if __name__ == "__main__":
root = Root()
root.mainloop()
This is a minimal example I made to show the problem, and it is extracted from a large project so please forgive the naming. So basically I have an GUI that looks like this:
the connect button and BE\RP... buttons belongs to a frame (control_container), which is like a navigator or tab selector that should always show up, and the info button belongs to another frame (container), which, when you click on BE\RP... buttons, should change to those corresponding frame class, and it does. what confused me is that when clicking the connect button, it should call function connect and do a print. However, it doesn't work: when you click on it, simply nothing happened. But I do know that the program recognize the connect function since it would complain if you delete the function. For contrast, if you click on info on StartPage, it works just fine and print. This is really strange to me.
import tkinter as tk
from tkinter import ttk
from tkinter import *
class emcAutoApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.control_container = tk.Frame(self)
self.control_container.pack(side="top", fill="both", expand = True)
container = tk.Frame(self, width=768, height=576, bg="")
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, BE, RP, PreScan, RSE):
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):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.frame_controller = controller
#control frame starts here
control_frame = ttk.Frame(self.frame_controller.control_container)
control_frame.pack(side='top')
chamber_frame = Frame(control_frame,
borderwidth=5,
relief=RIDGE,
width=200
)
chamber_frame.pack(side=TOP, expand=YES, fill=X)
chamber_frame_1 = Frame(chamber_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
chamber_frame_1.pack(side=LEFT, expand=YES, fill=X)
chamber_frame_2 = Frame(chamber_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
chamber_frame_2.pack(side=LEFT, expand=YES, fill=X)
connect_button = ttk.Button(chamber_frame_2, text="connect", command=lambda: self.connect)
connect_button.pack()
tab_frame = Frame(control_frame,
borderwidth=5,
relief=RIDGE,
width=500
)
tab_frame.pack(side=TOP, expand=YES, fill=X)
tab_frame_1 = Frame(tab_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
tab_frame_1.pack(side=LEFT, expand=YES, fill=X)
tab_frame_2 = Frame(tab_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
tab_frame_2.pack(side=LEFT, expand=YES, fill=X)
tab_frame_3 = Frame(tab_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
tab_frame_3.pack(side=LEFT, expand=YES, fill=X)
tab_frame_4 = Frame(tab_frame,
borderwidth=1,
relief=RIDGE,
width=100
)
tab_frame_4.pack(side=LEFT, expand=YES, fill=X)
BE_button = ttk.Button(tab_frame_1, text="BE",
command=lambda: self.frame_controller.show_frame(BE))
BE_button.pack()
RP_button = ttk.Button(tab_frame_2, text="RP",
command=lambda: self.frame_controller.show_frame(RP))
RP_button.pack()
PreScan_button = ttk.Button(tab_frame_3, text="PreScan",
command=lambda: self.frame_controller.show_frame(PreScan))
PreScan_button.pack()
RSE_button = ttk.Button(tab_frame_4, text="RSE",
command=lambda: self.frame_controller.show_frame(RSE))
RSE_button.pack()
infobutton = ttk.Button(self, text = "info", command = self.info)
infobutton.pack()
def info(self):
print("info")
def connect(self):
print("connected")
class BE(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.frame_controller = controller
class RP(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.frame_controller = controller
class PreScan(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.frame_controller = controller
class RSE(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.frame_controller = controller
if __name__ == "__main__":
#=== GUI ===#
LARGE_FONT = ("Verdana", 12)
NORM_FRONT = ("Verdana", 10)
app = emcAutoApp()
app.mainloop()
lambda: self.connect doesn't call connect. In this case there is no need for lambda, just directly reference the function. As a general rule of thumb, buttons should always be tied directly to functions rather than using lambda
connect_button = ttk.Button(..., command=self.connect)
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