how to open a new window from menu widget in tkinter - python

i'm trying to develop a simple Gui that display different pages when the button in the menu is clicked and i have been able to make other buttons work but the menu is giving me errors.
just like other apps that from the menu person can navicate the app.
when i run the code and click on newtest in file menu, i get this error
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "/home/pi/Documents/py script/helium1/test.py", line 34, in <lambda>
command=lambda: controller.show_frame(PageOne))
AttributeError: 'Frame' object has no attribute 'show_frame'
here is the code
import tkinter as tk
from tkinter import *
import datetime
import time
import paho.mqtt.client as mqtt
LARGE_FONT= ("Verdana", 12)
def messageFunction (client, userdata, message):
topic = str(message.topic)
message = str(message.payload.decode("utf-8"))
print(topic + " " + message)
HeliumClient = mqtt.Client("xokaxvwt")
HeliumClient.username_pw_set(username = "xokaxvwt", password="MGlEiIwOHM9-")
HeliumClient.connect("m16.cloudmqtt.com", 15998)
HeliumClient.subscribe("Switch_1")
HeliumClient.subscribe("Switch_2")
HeliumClient.on_message = messageFunction
HeliumClient.loop_start()
class MenuBar(tk.Menu):
def __init__(self, parent, controller):
tk.Menu.__init__(self, controller)
self.controller = controller
fileMenu = tk.Menu(self, tearoff=0)
self.add_cascade(label="File", underline=0, menu=fileMenu)
fileMenu.add_command(label="New Test",
command=lambda: controller.show_frame(PageOne))
fileMenu.add_separator()
fileMenu.add_command(label="Exit")
class Helium(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)
self.controller = controller
self.menubar = MenuBar(self, parent)
self.controller.config(menu=self.menubar)
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Visit Page 1", command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = tk.Button(self, text="Visit Page 2", command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home", command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page Two", command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, con):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home", command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One", command=lambda: controller.show_frame(PageOne))
button2.pack()
app = Helium()
#creating Date/time
def display_time():
Date = datetime.datetime.now()
Date_Label['text'] = Date
app.after(1000, display_time)
Date_Frame = LabelFrame(app, text= "Date/time")
Date_Frame.place(x=790, y=5, height=35, width=200)
Date_Label = Label(Date_Frame)
Date_Label.grid(column=0, row=0)
app.geometry('1000x450')
app.title("Helium")
app.resizable(False, False)
display_time()
app.mainloop()

You are passing parent argument to MenuBar but it's expecting a controller. You also need to pass the parent to the superclass, not controller. You need to create MenuBar like this:
class MenuBar(tk.Menu):
def __init__(self, parent, controller):
tk.Menu.__init__(self, parent)
...
class StartPage(tk.Frame):
def __init__(self, parent, controller):
...
self.menubar = MenuBar(self)
...

Related

How can I make the tkinter window fixed?

This is my current code, and I am struggling to understand where I need to put additional code to make sure that my tkinter window is fixed. Code taken from: https://pythonprogramming.net/change-show-new-frame-tkinter/
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = False)
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="NC's Ice-Cream Shop", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text=">>",
command=lambda: controller.show_frame(PageOne))
button.pack()
#button2 = tk.Button(self, text="Visit Page 2",
# command=lambda: controller.show_frame(PageTwo))
#button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Order Details", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="<<",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text=">>",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Receipt", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="<<",
command=lambda: controller.show_frame(PageOne))
button1.pack()
button2 = tk.Button(self, text="New",
command=lambda: controller.show_frame(StartPage))
button2.pack()
app = SeaofBTCapp()
app.mainloop()
As mentioned in the comments, you simply need to set your root window resizable method to false. In your case it is the SeaofBTCapp class.
For Example:
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.resizable(False,False) # <------------------- added this
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = False)
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="NC's Ice-Cream Shop", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text=">>",
command=lambda: controller.show_frame(PageOne))
button.pack()
#button2 = tk.Button(self, text="Visit Page 2",
# command=lambda: controller.show_frame(PageTwo))
#button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Order Details", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="<<",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text=">>",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Receipt", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="<<",
command=lambda: controller.show_frame(PageOne))
button1.pack()
button2 = tk.Button(self, text="New",
command=lambda: controller.show_frame(StartPage))
button2.pack()
app = SeaofBTCapp()
app.mainloop()

Cannot call show_frame function in tkinter outise of its class

With the code below (that I took from sentdex) I am trying to raise PageOne window when the correct password("123") is inserted in the first page. However, I get the error: TypeError: show_frame() missing 1 required positional argument: 'cont'. Isn't PageOne the argument ? Why is not working ? Could you also please explain what "controller" variable does ?
Thank you very much in advance.
import tkinter as tk
import tkinter as tk
from PIL import Image,ImageTk
LARGE_FONT= ("Verdana", 12)
class Root(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)
self.entry=tk.Entry(self, width = 35)
self.entry.insert(0, 'Enter password')
self.entry.config(fg = "grey")
# entry.bind('<FocusIn>', self.EntryFieldClicked)
# entry.bind("<Return>", (lambda event: self.SubmitPass()))
self.entry.place(relx=.5, rely=.3,anchor = tk.CENTER)
button=tk.Button( self,text="Show Password", width = 20,command = self.ShowPass).place(relx=.5, rely=.4,anchor = tk.CENTER)
button=tk.Button(self,text="Submit",width = 20, command=self.SubmitPass).place(relx=.5, rely=.5,anchor = tk.CENTER)
self.entry.config(fg = "grey")
self.entry.bind('<FocusIn>', self.EntryFieldClicked)
self.entry.bind("<Return>", (lambda event: self.SubmitPass ))
label=tk.Label(self,text="Log in to continue")
label.pack()
button = tk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
def EntryFieldClicked(self,event):
if self.entry.get() == 'Enter password':
self.entry.delete(0, tk.END)
self.entry.insert(0, '')
self.entry.config(fg = 'black', show = "*")
def ShowPass(self):
self.entry.config(fg = 'black', show = "")
def SubmitPass(self):
global Password
Password = self.entry.get()
if Password == "123":
Root.show_frame(PageOne)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
self.controller = controller
button1 = tk.Button(self, text="Back to Home",
command=lambda: self.controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = Root()
app.mainloop()
Blockquote
The problem is this line:
Root.show_frame(PageOne)
You are calling the method on the class rather than the instance, so the first argument is passes to the self parameter.
Your class needs to keep a reference to the controller, so that you can call the method via the controller.
class StartPage(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
...
def SubmitPass(self):
global Password
Password = self.entry.get()
if Password == "123":
self.controller.show_frame(PageOne)

How to reload the frame in tkinter everytime switching the frame?

I am using something similar to bellow code to navigation between windows in my tkinter desktop app. However the problem in bellow approach is that since all the frames are loaded at once, even though when i switch windows, it still returns the previous page. however in my use case i am loading metadata from database(picklist values) so i want to reload each frame whenever i switch the windows? How do i modify this code in such a way that every time i switch to a different frame, it reload the frame again.
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(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=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = tk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
app = SeaofBTCapp()
app.mainloop()
If you want to recreate each page when it is shown, you have complete control in the show_frame method to do exactly that.
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.container = tk.Frame(self)
self.container.pack(fill="both", expand=True)
self.current_frame = None
self.show_frame(StartPage)
def show_frame(self, new_frame_class):
if self.current_frame:
self.current_frame.destroy()
self.current_frame = new_frame_class(self.container, controller=self)
self.current_frame.pack(fill="both", expand=True)

Starting another script using a button in tkinter

I am trying to run another script within a script using a button in tkinter
I have tried two methods one being
import os
class SeaofBTCapp(tk.Tk):
#initilization
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Battery Life Calculator")
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, PageThree):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
#raise each page to the front
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def helloCallBack():
os.system('python test2.py')
#the start page
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = ttk.Button(self, text="Visit Page 1",command=lambda:controller.show_frame(PageOne))
button.pack()
button1 = ttk.Button(self, text="Visit Page 2",command=lambda:controller.show_frame(PageTwo))
button1.pack()
button2 = ttk.Button(self, text="Visit Graph page",command=lambda:controller.show_frame(PageThree))
button2.pack()
button3 = ttk.Button(self, text="execute test",command=lambda:controller.helloCallBack)
button3.pack()
No errors are given but when I hit execute nothing happens
The other method i tried was to
import test2
but it runs the script automatically and it prints "i am a test"
I should note the script i am trying to call is simply a test and just prints "i am a test"
any help is appreciated!
thanks
****edit****
import tkinter as tk
from tkinter import ttk
import test2
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
#initilization
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Battery Life Calculator")
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, PageThree):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
#raise each page to the front
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
#the start page
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button = ttk.Button(self, text="Visit Page 1",command=lambda:controller.show_frame(PageOne))
button.pack()
button1 = ttk.Button(self, text="Visit Page 2",command=lambda:controller.show_frame(PageTwo))
button1.pack()
button2 = ttk.Button(self, text="Visit Graph page",command=lambda:controller.show_frame(PageThree))
button2.pack()
button3 = ttk.Button(self, text="execute test",command=test2.main)
button3.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Page One", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to start page",command=lambda:controller.show_frame(StartPage))
button1.pack()
button1 = ttk.Button(self, text="Visit Page 2",command=lambda:controller.show_frame(PageTwo))
button1.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Page One", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Start Page",command=lambda:controller.show_frame(StartPage))
button1.pack()
button1 = ttk.Button(self, text="Back to Page One",command=lambda:controller.show_frame(PageOne))
button1.pack()
class PageThree(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Graph Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Start Page",command=lambda:controller.show_frame(StartPage))
button1.pack()
app = SeaofBTCapp()
app.mainloop()
test2.p is as follows
def main():
if __name__ == "__main__":
print("Executing as main program")
print("Value of __name__ is: ", __name__)
main()
Your first method is not recommended for that. If you have another python script you should definitely import it.
About the second method:
My guess is that the script test2.py is written without
if __name__ == "__main__":
main()
And that's why it shoot you message when you import it.

Navigating between multiple Tkinter GUI frames

I found some code online to create a tkinter GUI with multiple frames. I tried to modify the code to include a frame with navigation buttons to be displayed on each frame, rather than repeating the code each time. I keep getting error messages and cannot work out how to connect the navigation buttons to the corresponding frame.
This is the code for the closest I have got:
import Tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(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=LARGE_FONT)
label.pack(pady=10,padx=10)
button = tk.Button(self, text="Visit Page 1",
command=lambda: controller.show_frame(PageOne))
button.pack()
button2 = tk.Button(self, text="Visit Page 2",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
button2.pack()
btns = MainButtonFrame(self,SeaofBTCapp)
btns.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page Two!!!", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
button2 = tk.Button(self, text="Page One",
command=lambda: controller.show_frame(PageOne))
button2.pack()
btns = MainButtonFrame(self,SeaofBTCapp)
btns.pack()
class MainButtonFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
Pages = [StartPage,PageOne,PageTwo ]
for button in Pages:
NewButton = tk.Button(self, text=str(button),
command=lambda: controller.show_frame(button))
NewButton.pack()
app = SeaofBTCapp()
app.mainloop()
and the error message I got is:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1536, in __call__
return self.func(*args)
File "<ipython-input-13-dcc5fdbf1581>", line 103, in <lambda>
command=lambda: controller.show_frame(button))
TypeError: unbound method show_frame() must be called with SeaofBTCapp
instance as first argument (got classobj instance instead)
Any help would be much appreciated in suggesting what I am doing wrong in the button commands. If I can get this working I will use it as a framework to build a bigger GUI on. Thanks!
The initializer of MainButtonFrame needs a parent and a controller. The controller must be the instance of the main app, not the class. Change how you create MainButtonFrame to be this:
btns = MainButtonFrame(self,controller)

Categories

Resources