Swaping between frames on tkinter Python - python

I want to create an app that starts on a main window and by pressing a button it should change to a diferent tkinter frame so the main frame should not be visible. The problem I have is that one frame overlaps another and I can see labels which shouldnt appear (labels of the main frame)
This is what I see on the main window:
Main window
and this is what I see when I press the button:
I just want to have the "Win 2" label
This is the code I have:
import tkinter
root = tkinter.Tk()
root.title("My Program")
root.geometry("700x350")
def swap_window():
win2 = tkinter.Frame(root).grid()
tag2 = tkinter.Label(win2, text="Win 2").grid(row=0, column=0)
main_window = tkinter.Frame(root).grid()
main_tag = tkinter.Label(main_window, text="Main window").grid(row=0, column=0)
btn_swap_window = tkinter.Button(main_window, text="Window 2", command=swap_window).grid(row=1, column=0)
root.grid()
root.mainloop()

Related

Unable to display buttons on the right hand side of the tkinter GUI

I'm trying to create a 800x800 GUI where on left hand side I need a treeview to later display data from MySQL, and on right hand side, I am struggling to display five buttons "Read Excel", "Invoice Per Order", "Save PDF", and "Close". Treeview is showing but no one button is visible at the moment. What should I do?
Here is my code:
import tkinter as tk
from tkinter import ttk
# Create the root window
root = tk.Tk()
root.geometry("800x800")
# Create the treeview
treeview = ttk.Treeview(root)
treeview.pack(side="left", fill="both", expand=True)
# Create the buttons
read_excel_button = tk.Button(root, text="Read Excel")
invoice_per_order_button = tk.Button(root, text="Invoice per Order")
save_pdf_button = tk.Button(root, text="Save PDF")
close_button = tk.Button(root, text="Close")
# Place the buttons in a frame and pack the frame to the right of the root window
button_frame = tk.Frame(root)
button_frame.pack(side="right", fill="both")
read_excel_button.pack(side="top", in_=button_frame)
invoice_per_order_button.pack(side="top", in_=button_frame)
save_pdf_button.pack(side="top", in_=button_frame)
close_button.pack(side="top", in_=button_frame)
# Run the Tkinter event loop
root.mainloop()
You are using duplicated between line 18-25. You don't needed frame for pack()
Easier for you:
import tkinter as tk
from tkinter import ttk
# Create the root window
root = tk.Tk()
root.geometry("800x800")
# Create the treeview
treeview = ttk.Treeview(root)
treeview.pack(side="left", fill="both", expand=True)
# Create the buttons
read_excel_button = tk.Button(root, text="Read Excel").pack()
invoice_per_order_button = tk.Button(root, text="Invoice per Order").pack()
save_pdf_button = tk.Button(root, text="Save PDF").pack()
close_button = tk.Button(root, text="Close").pack()
# Run the Tkinter event loop
root.mainloop()
Output:
it works by removing parameters from .pack() methods on each button.
button_frame = tk.Frame(root)
button_frame.pack(side="right", fill="both")
read_excel_button.pack()
invoice_per_order_button.pack()
save_pdf_button.pack()
close_button.pack()

Tkinter opens second GUI first

I'm trying to build a login screen for practice and I'm having trouble with it. The general idea is that pressing the Login button closes the current window and opens another in a separate file. However, when I run the main file it opens the GUI window created in the second file. I'm not sure what would be causing it to do this.
import tkinter as tk
import loginEntry
HEIGHT = 200
WIDTH = 500
def login_function():
root.destroy()
loginEntry.NewScreen()
def register_function():
print("Register!")
root = tk.Tk()
root.title("Login Screen")
root.resizable(False, False)
canvas = tk.Canvas(root, height = HEIGHT, width = WIDTH)
canvas.pack()
frame = tk.Frame(root, bg='grey')
frame.place(relx=0.1, rely=0.25, relwidth=0.8, relheight=0.5)
login = tk.Button(frame, text="Login", command=login_function)
login.place(relx=0.05, rely=0.25, relwidth=0.425, relheight=0.5,)
register = tk.Button(frame, text="Register", command=register_function)
register.place(relx=0.525, rely=0.25, relwidth=0.425, relheight=0.5,)
introduction = tk.Label(root, text="Hello and welcome to DogNet, please login below.", font='bold 12')
introduction.place(relx=0.5, anchor='center', rely=0.1)
root.mainloop()
and then the second file
import tkinter as tk
def NewScreen():
root = tk.Tk()
canvas = tk.Canvas(root, bg='black')
canvas.pack()
root.mainloop()
NewScreen()
This is because, in your second file, you are calling the definition when it is imported.
import tkinter as tk
def NewScreen():
root = tk.Tk()
canvas = tk.Canvas(root, bg='black')
canvas.pack()
root.mainloop()
NewScreen() #< here you call the definition
This means that the series of events that happen in your program are as follows.
First file starts
Second file is imported
Definition "NewScreen" is called
tkinter mainloop starts which puts your program into a loop and stops the program from going on to any new lines until the loop is closed

Reset Tkinter Window, Restore Widgets

I am hoping to create a button to completely reset a Tkinter window as if the program has been run from scratch. Here is my current way of doing this. However it is not working as hoped.
from tkinter import *
master = Tk()
def do_something_():
#*performing a function on widget*
DoThing = Button(master, text='Do Something',command=do_something_).pack(pady=10)
clearall = Button(master, text='reset', command=resetAll).pack(pady=10)
def resetAll():
master.destroy()
master = Tk()
mainloop()
Is there any way to completely reset the window?
You can create function which create Frame and put widgets in this frame. And then you can put frame in window.
When you press button then you can destroy() this frame to remove all widgets and you can run the same function to create widgets again. Or you can run different function to create different frame with widgets - so you can replace content in window.
from tkinter import *
# --- functions ---
def create_frame(master):
print("create frame")
frame = Frame(master)
b = Button(frame, text='Do Something')
b.pack(pady=10)
clearall = Button(frame, text='reset', command=reset_all)
clearall.pack(pady=10)
return frame
def reset_all():
global frame
frame.destroy()
frame = create_frame(master)
#frame = create_different_frame(master)
frame.pack()
# --- main ---
master = Tk()
frame = create_frame(master)
frame.pack()
mainloop()
BTW: if you do var = Widget().pack() then you assign None to var and you have no access to Widget - ie. you can't detroy it. You have to do it in two steps
var = Widget()
var.pack()
if you don't need access to widget then you don't need variable
Widget().pack()
And when you have access to all widgets then you can change settings (ie. clear text) in every widget instead of destroying all widgets.
You can create a canvas (or frame) and then make the DoThing and clearall buttons have the canvas (or frame) as their master widget. You can then make the resetAll subroutine destroy the canvas (or frame). This will then destroy all of the canvas's child-widgets as well.
Note: I also fixed some syntax errors in your code (e.g. you defined the resetALL subroutine after referencing it.)
The code:
from tkinter import *
master = Tk()
def do_something_():
print('do something') #I added this so that i can run the code with no errors
#*performing a function on widget*
def resetAll():
canvas.destroy() #destroys the canvas and therefore all of its child-widgets too
canvas = Canvas(master)
canvas.pack()
#creates the cnvas
DoThing = Button(canvas, text='Do Something',command=do_something_).pack(pady=10)
#its master widget is now the canvas
clearall = Button(canvas, text='reset', command=resetAll).pack(pady=10)
#its master widget is now the canvas
master.mainloop()
I ran this code.
This was the GUI before i clicked the 'reset' button:
This was the GUI after i clicked the 'reset' button:
As you can see it worked. The canvas's child-widgets (the buttons) were destroyed because the canvas was destroyed.

How to implement lift method?

I am trying to move my function NEW_FRAME in front of the Frame for the root window so that old frame and the other widget in it will be behind for the function NEW_FRAME to only display its widget. So I searched and discovered that tkinter has lift method to achieve that but I can't implement it correctly, have tried positioning it at different places inside the function.
this link :explanation of the lift method with an example
from tkinter import *
root = Tk()
root.geometry("1300x600")
welcome = Frame(root, bg="yellow")
welcome.pack( fill=BOTH, expand=True)
label = Label(welcome, text="welcome bro to page one")
label.grid(row=45, column=50)
b = Label(welcome, text="you can select menu bar to switch page")
b.grid(row=100, column=500)
def NEWS_Frame():
new = Frame(root, bg="red")
new.pack(fill=BOTH, expand=True)
l1 = Label(new, text="Have been waiting for")
l1.grid(row=49, column=80)
l2 = Label(new, text="hello dude how be things")
l2.grid(row=0, column=0)
new.lift() # have position it to lift the new frame to the top of Frame
# menu bar start here
MAIN_MENU = Menu(root)
root.config(menu=MAIN_MENU)
File_menu = Menu(MAIN_MENU)
MAIN_MENU.add_cascade(label="NEW PAGE", menu=File_menu, underline=0)
File_menu.add_command(label="NEWS", command=NEWS_Frame)
root.mainloop()
First of all, your Minimal, Complete, and Verifiable example should only include the least amount of code as possible to reproduce the specific issue. The code below reproduces exactly the behavior you're having an issue with, but nothing more:
import tkinter as tk
def swap():
button2.pack()
button2.lift()
if __name__ == '__main__':
root = tk.Tk()
button1 = tk.Button(root, text="Swap with button 2", command=swap)
button2 = tk.Button(root, text="Swap with button 1")
button1.pack()
root.mainloop()
A workaround using pack:
You can't put widgets over another widget using pack. pack is literally for stacking in the 2-d, as in it is for stacking horizontally or vertically but not for piling widgets in the depth dimension. However, a bad workaround would be to simply hide the widget while displaying the other, which doesn't require lift at all.
In the below code each time swap is called it hides one button while displaying the other:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except:
import Tkinter as tk
def swap():
global is_button1_lifted
if is_button1_lifted:
button1.pack_forget()
button2.pack()
else:
button2.pack_forget()
button1.pack()
is_button1_lifted = not is_button1_lifted
if __name__ == '__main__':
root = tk.Tk()
is_button1_lifted = True
button1 = tk.Button(root, text="Swap with button 2", command=swap)
button2 = tk.Button(root, text="Swap with button 1", command=swap)
swap()
root.mainloop()
Answer using grid:
This is the way of using lift in the OP's case. The way this works is that both widgets displayed in the same node of a grid. The widget lift method is used on simply comes over the other(s).
In the below example both buttons are displayed, while one(button2 in this case) simply blocks the other by being in front of the other. When the lift is called it simply makes its object come to the front:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except:
import Tkinter as tk
def swap():
global is_button1_lifted
if is_button1_lifted:
button2.lift()
else:
button1.lift()
is_button1_lifted = not is_button1_lifted
if __name__ == '__main__':
root = tk.Tk()
is_button1_lifted = False
button1 = tk.Button(root, text="Swap with button 2", command=swap)
button2 = tk.Button(root, text="Swap with button 1", command=swap)
button1.grid(row=0, column=0)
button2.grid(row=0, column=0)
root.mainloop()
Answer using place:
This works almost the same way as the answer with grid, place simply has a more direct layout control:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except:
import Tkinter as tk
def swap():
global is_button1_lifted
if is_button1_lifted:
button2.lift()
else:
button1.lift()
is_button1_lifted = not is_button1_lifted
if __name__ == '__main__':
root = tk.Tk()
is_button1_lifted = False
button1 = tk.Button(root, text="Swap with button 2", command=swap)
button2 = tk.Button(root, text="Swap with button 1", command=swap)
button1.place(x=23, y=87)
button2.place(x=23, y=87)
root.mainloop()
lift moves widgets in the Z axis, pack arranges widgets in the X and Y axis. lift is incapable of changing the ordering of widget arranged with pack

How to make a button open a new window?

I am making a simple GUI that starts with a main menu them the user can click a button to proceed to a new window which has a picture of a keyboard and the user can press key on their keyboard to play the paino. Right now I cant figure out how to make a button that when pressed closes the main menu (labeled mainMenu()) and open the game menu (playGame).
import tkinter
from tkinter import *
class mainMenu:
def _init_(self, master):
frame = Frame(master)
frame.pack()
self.quitButton = Button(frame, text = "Quit", command = frame.quit)
self.quitButton.pack(side = LEFT)
self.proceedButton = Button(frame, text = "Play", command = playGame)
self.proceedButton.pack(side = LEFT)
def playGame(self):
frame.quit
gameMenu()
def gameMenu(self):
root = Tk()
b = mainMenu(root)
topFrame = Frame(root)
topFrame.pack()
bottomFrame = Frame(root)
bottomeFrame.pack(side = BOTTOM)
photo = PhotoImage(file = "piano.png")
label = Label(root, image = photo)
label.pack()
root.mainloop()
You'll have to forgive me for removing your class but I've never personally worked with classes in python before. However I seem to have you code working to some degree.
import tkinter
from tkinter import *
def playGame():
frame.quit
gameMenu()
def gameMenu():
b = mainMenu(root)
topFrame = Frame(root)
topFrame.pack()
bottomFrame = Frame(root)
bottomFrame.pack(side = BOTTOM)
photo = PhotoImage(file = "piano.png")
label = Label(root, image = photo)
label.pack()
root=Tk()
frame = Frame(root)
frame.pack()
quitButton = Button(frame, text = "Quit", command = frame.quit)
quitButton.pack(side = LEFT)
proceedButton = Button(frame, text = "Play", command = playGame)
proceedButton.pack(side = LEFT)
root.mainloop()
The main problem you had was that you were using both root and master. When declaring the main window in tkinter you normally use either root = Tk() or master = Tk() either one is acceptable, personally I use master. This variable contains the main window that everything else is placed into. You also hadn't put Tk() into any variable, meaning that when you hit root.mainloop() there was nothing to enter the main loop, this was because you were trying to declare root = Tk() inside gameMenu, which wasn't getting called in your program.
If you want to open windows within tkinter it's probably easier to write something like this:
from tkinter import *
master = Tk() #Declaring of main window
def ProceedButtonCommand(mainframe, master): #Command to attach to proceed button
mainframe.destroy()
DrawSecondScreen(master) #This line is what lets the command tied to the button call up the second screen
def QuitButtonCommand(master):
master.destroy()
def DrawFirstScreen(master):
mainframe = Frame(master) #This is a way to semi-cheat when drawing new screens, destroying a frame below master frame clears everything from the screen without having to redraw the window, giving the illusion of one seamless transition
ProceedButton = Button(mainframe, text="Proceed", command=lambda: ProceedButtonCommand(mainframe, master)) #Lambda just allows you to pass variables with the command
QuitButton = Button(mainframe, text = "Quit", command=lambda: QuitButtonCommand(master))
mainframe.pack()
ProceedButton.pack()
QuitButton.pack()
def DrawSecondScreen(master):
mainframe = Frame(master)
Label1 = Label(mainframe, text="Temp")
mainframe.pack()
Label1.pack()
DrawFirstScreen(master)
master.mainloop() #The mainloop handles all the events that occur in a tkinter window, from button pressing to the commands that a button runs, very important
This little script just draws a screen with two buttons, one draws a new screen with the text "temp" on it and the other button closes the master window.
In the future it's probably a better idea to ask a friend who is experienced in programming to help with this kind of stuff. Get talking on some computing forums, I'm sure you'll find a group of sharing and fixing code quickly.

Categories