How to destroy widgets? - python

I want in my if statement for it to destroy the buttons on my tkinter. I have tried a couple of methods and looked up a few and some i don't understand/too complicated. I have tried making the function create a new window but it isn't displaying.
def greenwin():
global tkinter
global Tk
root = Tk()
root.title("GAME OVER")
root.geometry('387x387')
gamelabel=Label(root,text="GAME OVER!GREENS
WIN!",width=33,height=15).place(x=150,y=150)
root.mainloop
return
I want a clear method of destroying widgets.I would like a function that destroys all these buttons for my tic tac toe.
but1=Button(root,text="",bg="white",width=11,height=5,command=colour1).place(x=0,y=0)
but2=Button(root,text="",bg="white",width=11,height=5,command=colour2).place(x=0,y=150)
but3=Button(root,text="",bg="white",width=11,height=5,command=colour3).place(x=0,y=300)
but4=Button(root,text="",bg="white",width=11,height=5,command=colour4).place(x=150,y=0)
but5=Button(root,text="",bg="white",width=11,height=5,command=colour5).place(x=150,y=150)
but6=Button(root,text="",bg="white",width=11,height=5,command=colour6).place(x=150,y=300)
but7=Button(root,text="",bg="white",width=11,height=5,command=colour7).place(x=300,y=0)
but8=Button(root,text="",bg="white",width=11,height=5,command=colour8).place(x=300,y=150)
but9=Button(root,text="",bg="white",width=11,height=5,command=colour9).place(x=300,y=300)
root.mainloop

import tkinter as tk
root = tk.Tk()
any_widget = tk.Button(root, text="Press to destroy!")
any_widget['command'] = any_widget.destroy # pay special attention to the lack of ()
# call any_widget.destroy(), button widget's command option specifically needs a
# reference to the method instead of an actual call
any_widget.pack()
root.mainloop()

Try this:
import tkinter as tk
root = tk.Tk()
root.geometry("500x300+10+13")
root.title("Test")
b = tk.Button(root, text="click me")
def onclick(evt):
w = evt.widget
w.destroy()
b.bind("<Button-1>", onclick)
b.pack()
root.mainloop()

Related

How to display text on a new window Tkinter?

I've started learning Tkinter on Python few weeks ago and wanted to create a Guess Game. But unfortunately I ran into a problem, with this code the text for the rules ( in the function Rules; text='Here are the rules... ) doesn't appear on the window ( rule_window).
window = Tk()
window.title("Guessing Game")
welcome = Label(window,text="Welcome To The Guessing Game!",background="black",foreground="white")
welcome.grid(row=0,column=0,columnspan=3)
def Rules():
rule_window = Tk()
rule_window = rule_window.title("The Rules")
the_rules = Label(rule_window, text='Here are the rules...', foreground="black")
the_rules.grid(row=0,column=0,columnspan=3)
rule_window.mainloop()
rules = Button(window,text="Rules",command=Rules)
rules.grid(row=1,column=0,columnspan=1)
window.mainloop()
Does anyone know how to solve this problem?
In your code you reset whatever rule_window is to a string (in this line: rule_window = rule_window.title(...))
Try this:
from import tkinter *
window = Tk()
window.title("Guessing Game")
welcome = Label(window, text="Welcome To The Guessing Game!", background="black", foreground="white")
welcome.grid(row=0, column=0, columnspan=3)
def Rules():
rule_window = Toplevel(window)
rule_window.title("The Rules")
the_rules = Label(rule_window, text="Here are the rules...", foreground="black")
the_rules.grid(row=0, column=0, columnspan=3)
rules = Button(window, text="Rules", command=Rules)
rules.grid(row=1, column=0, columnspan=1)
window.mainloop()
When you want to have 2 windows that are responsive at the same time you can use tkinter.Toplevel().
In your code, you have initialized the new instances of the Tkinter frame so, instead of you can create a top-level Window. What TopLevel Window does, generally creates a popup window kind of thing in the application. You can also trigger the Tkinter button to open the new window.
from tkinter import *
from tkinter import ttk
#Create an instance of tkinter window
root= Tk()
root.geometry("600x450")
#Define a function
def open_new():
#Create a TopLevel window
new_win= Toplevel(root)
new_win.title("My New Window")
#Set the geometry
new_win.geometry("600x250")
Label(new_win, text="Hello There!",font=('Georgia 15 bold')).pack(pady=30)
#Create a Button in main Window
btn= ttk.Button(root, text="New Window",command=open_new)
btn.pack()
root.mainloop()

Why do I get an extra empty window in Tkinter?

Here is my code:
from tkinter import *
OPTIONS = ["Available","Busy","Invisible","Away"]
now = Toplevel()
variable = StringVar(now)
variable.set(OPTIONS[0]) # default value
details = {"U_status":""}
def verify():
global u_status
details["U_status"]=variable.get()
print ("value is:" + variable.get())
now.destroy()
def status():
w = OptionMenu(now, variable, *OPTIONS)
w.pack()
button = Button(now, text="OK", command=verify, relief='flat')
button.pack()
if __name__=='__main__':
status()
mainloop()
While running the above code, along with the window (I wanted) another empty window appears. Can anyone figure out what is wrong in this code?
Here now = Toplevel() should be replaced with Tk(), like:
now = Tk()
When you use Toplevel() a Tk() window is made in the background, if its not already made(your case), and that is the reason you are getting a blank new window. Actually that blank window is your main window.
Toplevel() is used to make child windows for the parent Tk() windows,ie, if you want sub windows within your main window(now), you will use Toplevel(). Because more than one Tk() in your code will cause some errors later on.
The blank window is actually the root window of your app that tkinter creates by default. You probably want to be explicit, and create a tk.Tk() root, and keep a reference to it.
New windows can be spawned and destroyed at leisure; your app will continue to exist as long as you keep the root active.
Maybe something like this:
import tkinter as tk
def verify():
now = tk.Toplevel(root)
details["U_status"] = variable.get()
txt = f'value is: {details["U_status"]}'
tk.Label(now, text=txt).pack()
now.after(3000, now.destroy)
def status():
tk.OptionMenu(root, variable, *OPTIONS).pack()
tk.Button(root, text="OK", command=verify, relief='flat').pack()
if __name__=='__main__':
OPTIONS = ["Available", "Busy", "Invisible", "Away"]
root = tk.Tk()
variable = tk.StringVar(root)
variable.set(OPTIONS[0])
details = {"U_status": ""}
status()
root.mainloop()

Tkinter combobox not working when focus_force is called

Am trying to select item in my combobox but it does not work after i call focus_force() on the toplevel window other widget in the window works but the combobox selection is not working.
When i remove focus_force() from my code it works alright but want to keep it for main code.
#!python3
import tkinter as tk
import tkinter.ttk as ttk
def toplevel_window():
global top, cb
top = tk.Toplevel(root)
top.title("TOP")
top.geometry("300x300")
cb = ttk.Combobox(top, values=["djdjd", "fjfjf"])
cb.pack()
BT = tk.Button(top, text="print", command=combo_select)
BT.pack(side=tk.BOTTOM)
top.bind("<FocusOut>", focusout_func)
top.bind("<<ComboboxSelected>>", combo_select)
def focusout_func(event):
print(cb.get())
top.focus_force()
top.bell()
def combo_select(event=None):
print(cb.get())
root = tk.Tk()
root.geometry("600x600")
root.title("root")
b = tk.Button(text="OPEN TOPLEVEL", command=toplevel_window)
b.pack()
root.mainloop()
The reason the Combobox selection is not working is because you're causing the funcion focusout_func() to be called when an attempt is made to select it in the window containing it and that shifts the focus back to the toplevel window itself, which prevents the selection from happening.
One solution is simply to not use a <FocusOut> callback function at all. Instead, first add a top.focus_force() call to the end of toplevel_window() function to switch the focus to to it immediately after it's created.
Secondly, to keep the focus on the windows with the Combobox and make a bell alert sound when attempts are made to switch focus away from the it, you can bind a <FocusIn> callback to the root window and check the status of the other window in it (and prevent the focus from being changed if it exists and is being displayed).
Here's what I'm suggesting:
import tkinter as tk
import tkinter.ttk as ttk
from _tkinter import TclError
def toplevel_window():
global top, cb
top = tk.Toplevel(root)
top.title("TOP")
top.geometry("300x300")
cb = ttk.Combobox(top, values=["djdjd", "fjfjf"])
cb.pack()
btn = tk.Button(top, text="print", command=combo_select)
btn.pack(side=tk.BOTTOM)
top.bind("<<ComboboxSelected>>", combo_select)
top.focus_force()
def combo_select(event=None):
print('cb.get():', cb.get())
def root_focusin_callback(event):
try:
top_state = top.state() # Check status of toplevel widget.
except (NameError, TclError) as exc:
pass # Ignore - widget doesn't exist or was closed.
else:
top.bell()
top.focus_force()
root = tk.Tk()
root.geometry("600x600")
root.title("root")
root.bind("<FocusIn>", root_focusin_callback)
b = tk.Button(text="OPEN TOPLEVEL", command=toplevel_window)
b.pack()
root.mainloop()

Tkinter: Updating progressbar when a function is called

Imagine the following simple example:
def doNothing():
sleep(0.5)
barVar.set(10)
sleep(0.5)
barVar.set(20)
sleep(0.5)
barVar.set(30)
mainWindow = Tk()
barVar = DoubleVar()
barVar.set(0)
bar = Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= Button(mainWindow, text='Click', command=doNothing)
button.grid(row=0, column=0)
mainWindow.mainloop()
What I get when I run this, the progressbar is already at 30% when clicking the button, no progress in front of me. Like attached:
What I need: I can see the progress in front of me (not hanging then suddenly 30%)
Update:
I upadted the code according to #Bernhard answer, but still I can not see the progress in front of me. Just a sudden jump of 30% after waiting 1.5 sec
Seocnd Update:
I'm only using sleep here as a simulation for a process that takes time, like connecting over ssh and grabing some info.
Do not use sleep() in tkinter. The entire reason for you problem is sleep() will freeze tkinter until it is done with its count so what you are seeing is a frozen program and when the program is finally released its already set to 30 percent on the next mainloop update.
Instead we need to use Tkinter's built in method called after() as after is specifically for this purpose.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+10)
mainWindow.after(500, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
If you want the bar to appear to move smoothly you will need to speed up the function call and reduce the addition to the DoubbleVar.
import tkinter as tk
import tkinter.ttk as ttk
mainWindow = tk.Tk()
def update_progress_bar():
x = barVar.get()
if x < 100:
barVar.set(x+0.5)
mainWindow.after(50, update_progress_bar)
else:
print("Complete")
barVar = tk.DoubleVar()
barVar.set(0)
bar = ttk.Progressbar(mainWindow, length=200, style='black.Horizontal.TProgressbar', variable=barVar, mode='determinate')
bar.grid(row=1, column=0)
button= tk.Button(mainWindow, text='Click', command=update_progress_bar)
button.grid(row=0, column=0)
mainWindow.mainloop()
Because you are calling the function when the buttion is initialized, you need to loose the '(barVar') in the command=(barVar)). This way you bind the function to the button and don't call it when initializing it.
button= Button(mainWindow, text='Click', command=doNothing)
If you need to pass an argument you need to bypass the calling by using lambda:
button= Button(mainWindow, text='Click', command= lambda: doNothing(barVar))
I think I find the solution.
simply add mainWindow.update() after each progress. So the final code would be:
def doNothing():
sleep(0.5)
barVar.set(10)
mainWindow.update()
sleep(0.5)
barVar.set(20)
mainWindow.update()
sleep(0.5)
barVar.set(30)
mainWindow.update()

How do you close a tkinter window in another function?

I want a button in my window to open a new window and close the previous one. Is it possible to have one button do both of these? I've tried in the following code, but it hasn't worked, just told me that window is not defined:
import tkinter
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = window2).pack()
window.mainloop()
def window2():
window.destroy() #This is where the error is
menu = tkinter.Tk()
etc, etc, etc
window1()
First, you need to return the window object from the first function:
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = lambda: window2(window)).pack()
window.mainloop()
return window
Then, you need to pass the window as an argument to your function:
def window2(window):
window.destroy()
menu = tkinter.Tk()
And then call window1 with:
window = window1()
and click the button to destroy it and do the rest
This is an example using Toplevels, which is usually a better choice than creating, destroying, re-creating Tk() instances. The unique Toplevel ID is passed to the close_it function using partial(). You would, of course, combine them or have the close function call the open function.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
id.destroy()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()
Yes. Is possible. But you'll need to def that:
def window1:
blablabla
blablabla
def window2:
window2.destroy() <-- Here where the error was
How you noticed, put your name of window what you want Destroy and it will work!
using Python3
You could use a "global" such as:
root = Tk()
root.title('This is the root window')
def window_create():
global window_one
window_one = Tk()
window_one.title('This is window 1')
Then, from any function (or elsewhere) when you want to destroy window_one, do:
def window_destroyer():
window_one.destroy()
You could call your window_destroyer function from a button anywhere such as root which the example shows:
kill_window_btn = Button(root, text="Destroy", command=window_destroyer).pack()
Of course, follow your own naming conventions. :)
It seems to me, just 'global window_one' would solve it.

Categories