I am currently working on Tkinter codes. I happen to need to create new windows exactly similar to root but my codes do not perfectly work well. The title does not appear on the new windows. This is an excerpt:
from tkinter import *
#New Window
def New_page():
window()
#Main Window
def window():
window = Tk()
window.resizable(0,0)
window.configure(background='grey')
window.state('zoomed')
window.geometry("2000x4000")
#Title Frame
TITLE_FRAME = Frame(window, relief = GROOVE, bg = "black", width=2000, height=160).grid(row=0, column=0, columnspan = 150, rowspan = 30, sticky=E+W)
Label(TITLE_FRAME, text= 'THIS IS THE TITLE PART', fg='sky blue', bg='black', font='none 40 bold',
borderwidth=5).grid(row=0,column=10)
#New Window Button
ENTRY_FRAME = Frame(window, bg='sky blue', relief = SUNKEN)
ENTRY_FRAME.grid(row=40, column=0, columnspan=20, padx=15, pady=15)
Label(ENTRY_FRAME, text= 'SELECT THE APPROPRIATE DETAILS:',
bg = 'sky blue', fg='black', font='none 10 bold', borderwidth=5).grid(row=0, column=0, columnspan=20)
NEW_WINDOW = Button(ENTRY_FRAME, text="NEW WINDOW", font='None 8 bold', width=30, command=New_page, fg= 'black', bg='white')
NEW_WINDOW.grid(row = 3, column = 0, columnspan = 3, padx = 10, pady = 10)
window.mainloop()
#Calling the Tkinter function
window()
Like in the comments, Toplevel is the way to go for this one. What I changed:
Moved making the window object globally
Renamed the function to make it makeWindow(master)
makeWindow(master) now takes in a master widget. This will make
all of the widgets made there be part of the master window.
New_page was modified to make a new Toplevel() widget
TITLE_FRAME is now made first and then grid is called on it
(EDIT) These edits fix the problems with the original window closing the program.
We want to remap the closing behaviour to act how we want. This is done with window.protocol("WM_DELETE_WINDOW",callback). We must define the callback function, in this case, deleteWindow(win).
What delete window does is take a window, and if it is the root window it hides it. Otherwise, it deletes the window. I used window.withdraw() in my code, but there's probably a better way to do it.
The way it knows if it should close the program is by keeping track of the number of active windows in the activeWindows variable. When a window is created, the number increases, when delete it decreases. If the number of active windows is 0, we can delete the main window to close the program cleanly.
The way we bind the deleteWindow(win) callback is through an anonymous function. Normally, the protocol mentioned above does not give any arguments, but we want to know which window called the function. To do this, whenever we bind the destruction of the window, we define a anonymous function using lambda that calls deleteWindow.
.
from tkinter import *
window = Tk()
window.resizable(0,0)
window.configure(background='grey')
window.state('zoomed')
window.geometry("2000x4000")
activeWindows = 1
def deleteWindow(win):
if win == window:
window.withdraw()
else:
win.destroy()
global activeWindows
activeWindows-=1
if activeWindows <= 0:
window.destroy()
#New Window
def New_page():
global activeWindows
activeWindows+=1
NEW_WINDOW=Toplevel(background='grey')
NEW_WINDOW.geometry("2000x4000")
NEW_WINDOW.protocol("WM_DELETE_WINDOW",lambda:deleteWindow(NEW_WINDOW))
makeWindow(NEW_WINDOW)
#Main Window
def makeWindow(master):
#Title Frame
TITLE_FRAME = Frame(master, relief = GROOVE, bg = "black", width=2000, height=160)
TITLE_FRAME.grid(row=0, column=0, columnspan = 150, rowspan = 30, sticky=E+W)
Label(TITLE_FRAME, text= 'THIS IS THE TITLE PART', fg='sky blue', bg='black', font='none 40 bold',
borderwidth=5).grid(row=0,column=10)
#New Window Button
ENTRY_FRAME = Frame(master, bg='sky blue', relief = SUNKEN)
ENTRY_FRAME.grid(row=40, column=0, columnspan=20, padx=15, pady=15)
Label(ENTRY_FRAME, text= 'SELECT THE APPROPRIATE DETAILS:',
bg = 'sky blue', fg='black', font='none 10 bold', borderwidth=5).grid(row=0, column=0, columnspan=20)
NEW_WINDOW = Button(ENTRY_FRAME, text="NEW WINDOW", font='None 8 bold', width=30, command=New_page, fg= 'black', bg='white')
NEW_WINDOW.grid(row = 3, column = 0, columnspan = 3, padx = 10, pady = 10)
window.protocol("WM_DELETE_WINDOW",lambda: deleteWindow(window))
#Calling the Tkinter function
makeWindow(window)
window.mainloop()
Related
im working on this tkinter project im close to finishing but i cant seem to find a way to change the button color when i hover on it so can you help here is my code
import tkinter as tk
window = tk.Tk()
img = tk.PhotoImage(file='C:\\Users\\laithmaree\\PycharmProjects\\create_apps_with_python\\brainicon.ico.png')
window.title("Quiz Game")
# i created an icon
# i made a title
window.geometry("800x600")
window.resizable(width=False, height=False)
window.iconphoto(False, img)
label1 = tk.Label(window, text='Quiz App', font=("Arial Bold", 25))
label1.pack()
txtbox = tk.Entry(window, width=50)
def playbuttonclicked():
label1.destroy()
playbtn.destroy()
quitbtn.destroy()
label2 = tk.Label(window, text='What is the short form of computer science', font=("Arial Bold", 25))
label2.pack()
txtbox.place(x=250, y=200, height=40)
def chkanswer():
useranswer = txtbox.get() # Get contents from Entry
if useranswer == 'cs':
lblcorrect = tk.Label(window, text='correct')
lblcorrect.pack()
def delete():
lblcorrect.destroy()
lblcorrect.after(1000, delete)
else:
lblwrong = tk.Label(window, text='Try Again')
lblwrong.pack()
def deletefunction():
lblwrong.destroy()
lblwrong.after(1000, deletefunction)
submitbtn = tk.Button(window, text='Submit', font=('Arial Bold', 30), command=chkanswer, bg='red')
submitbtn.place(x=305, y=400)
playbtn = tk.Button(window, text='Play', font=("Arial Bold", 90), bg='red', command=playbuttonclicked)
playbtn.place(x=10, y=200)
def quitbuttonclicked():
window.destroy()
quitbtn = tk.Button(window, text='Quit', font=("Arial Bold", 90), bg='red', command=quitbuttonclicked)
quitbtn.place(x=400, y=200)
window.mainloop()
the buttons are submitbtn,playbtn,quitbtn i want the hover over buttons to be black there already red i just want them to be black when i hover over them thx
Bind each of the buttons to entering and leaving events (when mouse enters and leaves the widget) and based on which one is triggered, change the background color of the button:
btn.bind('<Enter>', lambda e: e.widget.config(bg='black'))
btn.bind('<Leave>', lambda e: e.widget.config(bg='red'))
I am trying to display images in my game, which in time will correspond to rooms.
I have three main frames and I want to display images in the one called 'frame 3', but I am not unable to get the picture itself to display!
I made a method called RoomImages which is supposed to handle the roomImages. I have called this method in my constructormethod for the whole App class.
It displays no error messages and therefore I am unsure where the missing logic is.
I have attached a picture of what the output shows:
Output
import tkinter as tk
from Game import Game
from tkinter import messagebox
from PIL import ImageTk, Image
class App():
# Creates a Frame for the application
# and populates the GUI ...
def __init__(self, root):
#super().__init__(master=win)
self.game = Game()
#A menubar with the options Quit' which destroys the window
# and 'about' which displays a message box with the info from the method showAbout
menubar = tk.Menu()
menubar.add_command(label="Quit", command=root.destroy)
menubar.add_command(label="About", command=self.showAbout)
root.config(menu=menubar)
# Create two frames owned by the window root
# In order to use multiple layout managers, the frames
# cannot share a parent frame. Here both frames are owned
# by a top level instance root.
self.frame1 = tk.Frame(root, width=600, height=250, bg='WHITE', borderwidth=2)
self.frame1.pack_propagate(0) # Prevents resizing
self.frame2 = tk.Frame(root, width=600, height=150, bg='LIGHT GREY', borderwidth=2, padx=175)
self.frame2.grid_propagate(0) # Prevents resizing
self.frame3 = tk.Frame(root, width=600, height=250, bg='LIGHT BLUE', borderwidth=2, padx=275)
self.frame3.grid_propagate(0) # Prevents resizing
# This packs both frames into the root window ...
self.frame1.pack()
self.frame2.pack()
self.frame3.pack()
#image canvas for frame3 to display the current room
self.canvas_for_image = tk.Canvas(self.frame3, bg='LIGHT GREY', height=250, width=600, borderwidth=0, highlightthickness=0)
self.canvas_for_image.grid(row=0, column=0, sticky='nesw', padx=0, pady=0)
#self.canvas_for_image.pack(expand=YES, fill=BOTH)
self.frame3.columnconfigure(0, pad=5)
self.frame3.columnconfigure(1, pad=5)
self.frame3.rowconfigure(0, pad=5)
self.frame3.rowconfigure(1, pad=5)
#make a gridstructure for frame2 to be able to precisely place my buttons.
self.frame2.columnconfigure(0, pad=5)
self.frame2.columnconfigure(1, pad=5)
self.frame2.columnconfigure(2, pad=5)
self.frame2.columnconfigure(3, pad=5)
self.frame2.columnconfigure(4, pad=5)
self.frame2.columnconfigure(5, pad=5)
self.frame2.rowconfigure(0, pad=5)
self.frame2.rowconfigure(1, pad=5)
self.frame2.rowconfigure(2, pad=5)
self.frame2.rowconfigure(3, pad=5)
# Now add some useful widgets ...
self.textArea1 = tk.Label(self.frame1, text='')
self.textArea1.pack()
self.cmdArea = tk.Entry(self.frame2, text='')
self.cmdArea.grid(row = 3, column = 1)
self.buildGUI()
self.roomImages()
def showAbout(self):
"""uses the messagebox import to display info about the game"""
messagebox.showinfo("About", "A classic whodunnit where you have to find out who the murder is!")
def buildGUI(self):
self.doCmd = tk.Button(self.frame2, text='Run command',
fg='black', bg='blue',
command=self.doCommand)
self.doCmd.grid(row = 1, column = 1)
self.goDown = tk.Button(self.frame2, text='Down',
fg='black', bg='red',
command=self.processDownCommand)
self.goDown.grid(row=2, column=1)
self.goUp = tk.Button(self.frame2, text='Up',
fg='black', bg='red',
command=self.processUpCommand)
self.goUp.grid(row =0, column=1)
self.goLeft = tk.Button(self.frame2, text='Left',
fg='black', bg='red',
command=self.processLeftCommand)
self.goLeft.grid(row=1,column = 0)
self.goRight = tk.Button(self.frame2, text='Right',
fg='black', bg='red',
command=self.processRightCommand)
self.goRight.grid(row=1, column =2)
self.help = tk.Button(self.frame2, text='Help',
fg='black', bg='green',
command=self.processHelpCommand)
self.help.grid(row=1, column=4)
self.textArea1.configure(text=self.game.printWelcome())
def roomImages(self):
self.imageDict = {'apple': 'apple.png', 'banana': 'banana.png', 'bar': 'bar.png', 'cherries': 'cherries.png',
'grapes': 'grapes.png', 'lemon': 'lemon.png', 'melon': 'melon.png', 'orange': 'orange.png'}
self.apple = 'apple'
self.apple = f'images/{self.imageDict[self.apple]}'
#adding images
self.r1 = ImageTk.PhotoImage(Image.open(self.apple))
self.r1Label = tk.Label(self.frame3, image=self.r1)
def main():
win = tk.Tk() # Create a window
win.title("Adventure World with GUI") # Set window title
win.geometry("700x400") # Set window size
win.resizable(False, False) # Both x and y dimensions ...
# Create the GUI as a Frame
# and attach it to the window ...
myApp = App(win)
# Call the GUI mainloop ...
win.mainloop()
if __name__ == "__main__":
main()
how do I position my label which says "Question One" in my def new_window() function. As you run it the label is being positioned at the bottom, And i want it to be applied on the top.
from tkinter import *
from tkinter import ttk
#User Interface Code
root = Tk() # Creates the window
root.title("Quiz Game")
def new_window():
newWindow = Toplevel(root)
display = Label(newWindow, width=150, height=40)
message = Label(newWindow, text="Question One", font = ("Arial", "24"))
display.pack()
message.pack()
display2 = Label(root, width=100, height=30, bg='green')
button1 = Button(root, text ="Continue", command=new_window, width=16,
bg="red")
message_label1 = Label(text="A Quiz Game", font = ("Arial", "24"), padx=40,
pady=20)
message_label2 = Label(root, text="Click 'Continue' to begin.",
wraplength=250)
display2.pack()
button1.pack()
message_label1.pack()
message_label2.pack()
root.mainloop() # Runs the main window loop
You are packing in the wrong order. Do not pack display before your message. So just swapping the order will fix the issue.
Here is the code. Replace your def new_window(): with this
def new_window():
newWindow = Toplevel()
message = Label(newWindow, text="Question One", font = ("Arial", "24"))
display = Label(newWindow, width=150, height=40)
message.pack()
display.pack()
pack method just blindly packs the widget into the window. And the next pack will be done below it if there is space. So take care of the order while packing widgets :)
from Tkinter import *
import Tkinter as ttk
from ttk import *
master = Tk()
master.title("Learn Spanish")
canvas = Canvas(master, width = 600, height = 600, bg = 'orange')
canvas.grid(row=0, column=0, rowspan=2, columnspan=2)
title = canvas.create_text(300, 100, text = 'Learn To Speak Spanish', font = ('Freestyle Script', 60), fill = 'firebrick')
text = canvas.create_text(300, 250, text = 'Welcome! What are you ready \n to learn Spanish today?', font = ('Freestyle Script', 35), fill = 'firebrick')
def nextScreen():
canvas.delete("all")
canvas.create_text(300, 125, text = 'Select one of the activities below to get started.', font = ('Freestyle Script', 30), fill = 'firebrick')
btn4 = ttk.Button(master, bg='white', command=wordStudy, text='Word Study', font=('Freestyle Script', 35))
canvas.create_window(300, 300, width=300, height=90, window=btn4)
btn5 = ttk.Button(master, bg='white', command=matchGame, text='Matching Game', font=('Freestyle Script', 35))
canvas.create_window(300, 400, width=300, height=90, window=btn5)
btn6 = ttk.Button(master, bg='white', command=quiz, text='Quiz Yourself', font=('Freestyle Script', 35))
canvas.create_window(300, 500, width=300, height=90, window=btn6)
def quiz():
canvas.delete("all") # haven't worked on this one yet
def matchGame():
canvas.delete("all") # haven't worked on this one yet either
def wordStudy(): #i'm right here
canvas.delete("all")
Canvas(master, width = 600, height = 600, bg = 'orange')
canvas.create_text(300, 50, text = 'Study the terms below.', font = ('Freestyle Script', 30), fill = 'firebrick')
btn = ttk.Button(master, bg='white', command=back, text='Back', font=('Freestyle Script', 15))
canvas.create_window(100, 575, width=40, height=20, window=btn)
def textvariable(text): # translation of english pop up when buttons pressed
entry.delete(0,END)
entry.insert(0,text)
return
b1=Button(canvas, text="Good morning.", command=lambda:textvariable("Buenos dias.")).grid(row=1, column=1, pady=(100,50), padx=100)
b2=Button(canvas, text="Good night.", command=lambda:textvariable("Buenas noches.")).grid(row=1, column=2, pady=(100,50), padx=100)
b3=Button(canvas, text="Goodbye.", command=lambda:textvariable("Adiós.")).grid(row=2, column=1, pady=50, padx=100)
b4=Button(canvas, text="Hello.", command=lambda:textvariable("Hola.")).grid(row=2, column=2, pady=50, padx=100)
b5=Button(canvas, text="What is your name?", command=lambda:textvariable("¿Cómo te llamas?")).grid(row=3, column=1, pady=50, padx=100)
b6=Button(canvas, text="How are you?", command=lambda:textvariable("¿Cómo estás?")).grid(row=3, column=2, pady=50, padx=100)
b7=Button(canvas, text="Please.", command=lambda:textvariable("Por favor.")).grid(row=4, column=1, pady=50, padx=100)
b8=Button(canvas, text="Thank you.", command=lambda:textvariable("Gracias.")).grid(row=4, column=2, pady=50, padx=100)
entry = Entry(canvas, textvariable=textvariable, width=30)
entry.grid(column=2, row=8, padx=10, pady=(10, 50))
canvas.create_text(295, 570, text = 'Translation:', font = ('Freestyle Script', 25), fill = 'firebrick')
btnStart = ttk.Button(master, bg='white', command=nextScreen, text='Start', font=('Freestyle Script', 35))
canvas.create_window(295, 400, width=400, height=90, window=btnStart)
def back(): # i'm not sure how to make back button or make this work!
canvas.delete("all")
mainloop()
I am working on a Spanish app. I want to know how I can effectively create a back button or delete everything on my canvas, including widgets. I saw some posts that had created a class, and use self. and so on, but I am not familiar with all that stuff. I'm a beginner.
In the first pages you are placing widgets on canvas with:
canvas.create_window(..., window=btn)
In wordStudy you are using grid:
b1=Button(canvas, text=... )).grid(row=1,...
To delete widgets placed with grid use either of:
b1.grid_remove()
b1.grid_forget()
I can't remember the exact difference so you have to google it.
Response to comment
When you delete all windows from canvas in the function nextScreen(),
it works because canvas is defined in the main script. By rules for
name scope a function will use variables from main if they are not
defined inside the function. As canvas is mutable you can change it
without declaring it global.
The buttons b1-b8 is created inside the function wordStudy() and
the references (names: b1, b2 etc.) are garbage collected when the
function exits. For declaring them global to work they must be
created in the main script outside the function; all the global
statement does is include variables from the main script into the
function scope.
I recommend that you use the same method of putting widgets on the
canvas as in the function nextScreen() throughout the script, as it
works just fine. The code is also easier to read and maintain if you
solve the same task in the same way all the time.
If you want to experiment with grid_forget() here is some example
code to play with. When you do it, be sure to pay attention to the
scope.
from tkinter import *
root = Tk()
root.geometry('200x100')
field = Canvas(root, bg='white')
field.pack(expand='yes', fill='both')
def start_function(event=None):
print('Start button pressed.')
start_button = Button(field, text='start', command=start_function)
start_button.grid(row=0, column=0) # Place the button on the canvas
def remove_button(event=None): # Forget the button
start_button.grid_forget()
def show_button(event=None): # Remember the button
start_button.grid()
root.bind('a', remove_button) # Press "a" on the keyboard to forget
root.bind('s', show_button) # Press "s" on the keyboard to remember
In my Python GUI script, I have a pop up window, and there is text area widget on the pop-up window, users can input some content inside, and then click one button on the pop-up window to get the input text.
But it seems that in the defined function, the widget on the pop-up window can not be accessed. the code goes as following:
from Tkinter import *
def Add_Content():
content = ent_new.get("1.0","end")
print content
def Add_Task():
task_index = 1
new_window = Toplevel()
label1 = Label(new_window, text="New Goal:")
label1.grid(row = 0, column = 0)
ent_new = Text(new_window, bg= "white", height=5, width= 30)
ent_new.grid(row=0,column =1,padx=5, pady=5)
bu_new = Button( new_window,text="Add", command = Add_Content)
bu_new.grid(row=0, column =2)
new_window.focus_force()
master = Tk()
group = LabelFrame(master, text="Operation", padx=5, pady=5, relief = RAISED)
group.grid(row=0,column= 0, padx=10, pady=10, sticky=N)
bu_add = Button(group, text = "Add Task",width = 15, command = Add_Task)
bu_add.grid(row=0,column=0)
mainloop()
in the above script, the ent_new can not be found in function Add_Content
The problem is that ent_new is in another namespace. You can solve it by making Add_Content recieve ent_new in the arguments like that,
def Add_Content(my_ent):
content = my_ent.get("1.0","end")
print content
and then using a wrapper function (lambda) when passing it to Button
bu_new = Button( new_window,text="Add", command = lambda: Add_Content(ent_new))
Without adding a class and the concept of self and parent, you can use lambda given in the first answer or you can use a global variable.
Note: In python circles globals are rather frowned upon but they work and get the job done.
from Tkinter import *
global ent_new
def Add_Content():
content = ent_new.get("1.0","end")
print content
def Add_Task():
global ent_new
task_index = 1
new_window = Toplevel()
label1 = Label(new_window, text="New Goal:")
label1.grid(row = 0, column = 0)
ent_new = Text(new_window, bg= "white", height=5, width= 30)
ent_new.grid(row=0,column =1,padx=5, pady=5)
bu_new = Button( new_window,text="Add", command = Add_Content)
bu_new.grid(row=0, column =2)
new_window.focus_force()
master = Tk()
group = LabelFrame(master, text="Operation", padx=5, pady=5, relief = RAISED)
group.grid(row=0,column= 0, padx=10, pady=10, sticky=N)
bu_add = Button(group, text = "Add Task",width = 15, command = Add_Task)
bu_add.grid(row=0,column=0)
mainloop()