tkinter: function not called when button pressed - python

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)

Related

How to swap between pages / windows in Tkinter

I have tried googling and everyone seems to have structured their code completely differently. I understand the base level tkinter, however I do not understand how people are using classes and def's to swap pages. How can I swap from my main window to my second one? (and not open the second one after the main is closed)
import tkinter as tk
main = tk.Tk()
main.title("Main Program")
firstlabel = tk.Label(main, text="This is a program!")
firstlabel.pack()
main.mainloop()
second = tk.Tk()
second.title("Second Program")
firstlabel = tk.Label(second, text="This is another program!")
firstlabel.pack()
second.mainloop()
EDIT: (solution)
import tkinter as tk
class Application(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, 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(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Start Page", font=("Consolas", 30))
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Visit Page 1", command=lambda: controller.show_frame(PageOne))
button.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=("Consolas", 30))
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home", command=lambda: controller.show_frame(StartPage))
button1.pack()
app = Application()
app.mainloop()
One way do this is by clearing everything (every widget), with this function:
def clear(app):
# Delete everything else in app
widget_list = app.winfo_children()
for item in widget_list:
if item.winfo_children():
widget_list.extend(item.winfo_children())
for item in widget_list:
item.pack_forget()
And then puting in the new window you want to swap to (every window should hav its own frame to make it simpler).
Try this::::
tk.Toplevel(main)

i want to make a new window in tkinter

I am a newbie in python Tkinter I want to a new window to appear after clicking a start button I have created a function of new_window my real problem that I created a class with three parameters of self, parent and controller I have tried to make the function new window to have two arguments self and controller but I could not and here is the last experiment i did thanks for any advice
class spariot(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,History_page):
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(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1=tk.Button(self,text="start",command =self.new_window)
button1.pack()
button2=tk.Button(self,text="history",command=lambda:controller.show_frame(History_page))
button2.pack()
def new_window(self):
self.newWindow = tk.Toplevel(self.master)
self.app = StartingPage(self.newWindow)
Start small, learn from there.
Here is an example that will spawn a new window from the root window, to study:
import tkinter as tk
def spawn():
top = tk.Toplevel()
tk.Label(top, text='this is a new bright\nand shiny\nnew window').pack()
root = tk.Tk()
btn = tk.Button(root, text='spawn new window', command=spawn)
btn.pack()
root.mainloop()

Python tkinter code - frames with imported image buttons

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.

How to bind same key action to buttons in different windows?

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()

Python tkinter callback

I have a container with a set of frames, that provide me with two pages (main page and an info page.) Buttons are used to navigate the pages.
All is working.
I have defined a function to remove folders on the OS. (shutil.rmtree) This function should be called by my button 2 on the main page, but it falls over and is not working.
import tkinter as tk
import shutil
from tkinter import *
TITLE_FONT = ("Helvetica", 18, "bold")
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# 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 (MainPage, InfoPage,):
frame = F(container, self)
self.frames[F] = 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(MainPage)
def show_frame(self, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
class MainPage(tk.Frame):
def callback(self):
shutil.rmtree('C:\Test', ignore_errors=False)
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
photo = PhotoImage(file='C:\Logo.gif')
label = Label(self, image=photo)
label.Image = photo
label.pack(side=TOP)
button1 = tk.Button(self, text="Info!",
command=lambda: controller.show_frame(InfoPage))
button1.pack(side=LEFT, padx=5, pady=5)
button2 = tk.Button(self, text="Clean!", command=callback)
button2.pack(side=RIGHT, padx=5, pady=5)
class InfoPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Info Page", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Back",
command=lambda: controller.show_frame(MainPage))
button.pack()
if __name__ == "__main__":
app = App()
app.mainloop()

Categories