Switching Frames In PyQt4 - python

I would like to know how I would go about doing the following in PyQt4:
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):
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="Home", font=LARGE_FONT)
label.pack(pady=10,padx=10)
btn = tk.Button(self, text="Page One", command= lambda: controller.show_frame(PageOne))
btn.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)
btn = tk.Button(self, text="Home", command= lambda: controller.show_frame(StartPage))
btn.pack()
app = SeaofBTCapp()
app.mainloop()
I am going to have multiple pages and would like to have it easily defined. I am still very new to python coding and I'm still learning how PyQt4 works but my project will have atleast 4 pages that I need to switch between and I don't want the code to be too long by defining the same thing on every page's class. All help will be appreciated. Thank you in advance.

Take a look at QStackedWidget.
From the docs:
The QStackedWidget class provides a stack of widgets where only one widget is visible at a time.
Depending on what you are doing exactly, QWizard may be suited even better.
Both also exist for Qt4, but you should use Qt5 if you can.

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)

Parameter 'event' value is not used

I'm developing a program in python 2.7 and have run into an issue on Pycharm. In the program, I want the user to be able to navigate through different screens by either clicking buttons, or by pressing the "enter" key. I tried implementing it into my program, and it works but Pycharm is giving the error
import Tkinter as Tk
class MemoryGameApp(Tk.Tk):
def __init__(self, *args, **kwargs):
Tk.Tk.__init__(self, *args, **kwargs)
Tk.Tk.wm_title(self, "2 screens")
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):
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="Page 1")
label.pack()
global button
button = Tk.Button(self, text="Next", width=5, command=lambda: controller.show_frame(PageTwo))
button.pack(pady=100, padx=100)
button.focus_set()
def press_enter(event):
controller.show_frame(PageTwo)
button2.focus_set()
button.bind("<Return>", press_enter)
class PageTwo(Tk.Frame):
def __init__(self, parent, controller):
Tk.Frame.__init__(self, parent)
label_title2 = Tk.Label(self, text="Page 2")
label_title2.pack()
global button2
button2 = Tk.Button(self, text="Back", width=5, command=lambda: controller.show_frame(StartPage))
button2.pack(pady=100, padx=100)
def press_enter(event):
controller.show_frame(StartPage)
button.focus_set()
button2.bind("<Return>", press_enter)
app = MemoryGameApp()
app.mainloop()
It has given the error in def press_enter(event):
It claims that event is not used, but if I remove it from the program, the program does not function correctly
I know that it works when it is implemented, I'm just interested in seeing if there's anyway that I can remove this issue.
Thanks
To remove this warning, you could replace the argument 'event' by '_', or just do something with the object, like printing it to console.
Even though the argument of the function seems useless, it cannot be removed: It belongs to the signature of a valid event function.
It is sufficient to change event to _event.

Function to switch between two frames in tkinter

I'm looking through the code at passing-functions-parameters-tkinter-using-lambda, and needed a tad more functionality inside his class PageOne(tk.Frame). Instead of using lambda commands below (as he did):
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))`
I'd like to be able to create a function that had an if/then hierarchy inside of it... specifically to check if all other inputs on PageOne had been fulfilled first (which I then know how to do) before allowing a frame change.
If this can be done individually using lambda, even better. Can anyone help me out?
Update: Using Bryan's advice and reformatting for the original code he linked, I now have:
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "APP") #window heading
self.title_font = tkfont.Font(family='Helvetica', size=12) #options: weight="bold",slant="italic"
container = tk.Frame(self) #container = stack of frames; one on top is visible
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 #puts all pages in stacked order
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", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to Page One",
command=lambda: controller.show_frame("PageOne"))
button1.pack()
button2.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
####FIX PART 1####
self.next1 = tk.Button(self,text="Next",padx=18,highlightbackground="black",
command=lambda: self.maybe_switch("PageTwo"))
self.next1.grid(row=10,column=1,sticky='E')
####FIX PART 2####
def maybe_switch(self, page_name):
if ###SOMETHING###:
self.controller.show_frame(page_name)
if __name__ == "__main__":
app = App()
app.mainloop()
You shouldn't put any logic in a lambda. Just create a normal function that has any logic you want, and call it from the button. It's really no more complicated that that.
class SomePage(...):
def __init__(...):
...
button1 = tk.Button(self, text="Back to Home",
command=lambda: self.maybe_switch_page(StartPage))
...
def maybe_switch_page(self, destination_page):
if ...:
self.controller.show_frame(destination_page)
else:
...
If you want a more general purpose solution, move the logic to show_frame, and have it call a method on the current page to verify that it is OK to switch.
For example:
class Controller(...):
...
def show_frame(self, destination):
if self.current_page is None or self.current_page.ok_to_switch():
# switch the page
else:
# don't switch the page
Then, it's just a matter of implementing ok_to_switch in every page class.

Tkinter GUI frames - autoloading frame using 'after'?

I've been experimenting with some code I found here which lets you navigate between multiple tkinter GUI frames:
Navigating between multiple Tkinter GUI frames
And I'm trying to see if I can autoload a specific frame after a certain amount of time passes, (similar to a sleep screen / screensaver).
I've used the .after method and beginning right after frame.tkraise(). It calls a new class called Autoload, which is currently just a copy of the main App Class SeaofBTCapp.
It works, but the obvious problem with this is that it's popping out the frame into a new window because it's calling this again:
tk.Tk.__init__(self, *args, **kwargs)
I'm a total newb, so I apologize for asking a very obvious question, but what is the best way to keep it from popping out? If I copy the code from one of the Frame classes, it tells me that __init__ is not getting enough arguments. I have tried various (obvious) things, but having trouble wrapping my head around this because it ultimately would have to be a passive function operating in the back of the main app, without hindering other functionality (calling new frames). I'm probably just overthinking this and have missed a very simple and elegant solution. Any thoughts?
Here's the full code, thanks for help!
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()
frame.after(2000, Autoload) #####
class Autoload(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()
frame.after(2000, Autoload) #####
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()

Expand Tkinter Frame

I have a basic application using tkinter with python (3.5). I want the application to be run in full screen, and have multiple windows to switch through. So far this is what I have.
import tkinter as tk
class Window(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Movie Kiosk")
self.attributes("-fullscreen", True)
self.resizable(width=False, height=False)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=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 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="Main Page", font=("Verdana",48))
label.place(relx=0.5, y=0, anchor=tk.N)
button = tk.Button(self, text="Go to page 1",
command=lambda: controller.show_frame(PageOne))
button.place(relx=1, rely=1, anchor=tk.SE)
exitButton = tk.Button(self, text="Close Program", command=exit)
exitButton.place(relx=0, rely=1, anchor=tk.SW)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Page 1")
label.pack()
button = tk.Button(self, text="Back to home",
command=lambda: controller.show_frame(StartPage))
button.pack()
app = Window()
app.mainloop()
When I run the application, the program loads in full screen mode, however the frame and all its widgets are packed tightly in the top left corner of the screen. Not sure why this is happening, I have messed around changing properties of my my "app" and my frames. If someone could tell me whats wrong or direct me to a place where I can find an answer it would be very much so appreciated. Thanks.
Not sure why this fixes it...
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
Adding this code after packing the container fixes the problem.

Categories