Tkinter .pack() window not showing? - python

I've been working on a simple program that make a button output something. But when i run it,
this
(I got this from the internet btw) does not show up. Is somethoing wrong with the code or something?
Please help me so the window above can appear :)
Code:
from Tkinter import *
def asdf():
print('test')
tk = Tk()
b = Button(tk, text="test", command=asdf)
b.pack()

You forgot to call the Tk.mainloop method at the end of your program:
from Tkinter import *
def asdf():
print('test')
tk = Tk()
b = Button(tk, text="test", command=asdf)
b.pack()
##############
tk.mainloop()
##############
Doing so starts Tkinter's main event loop and creates the window.

It seems you are using Python3, as there are parentheses after print, so from Tkinter import * should be from tkinter import *. Python is case-sensitive. You also forgot to call root.mainloop() at the end of your code as #user2555451 mentioned, although a window should appear all the same, but stop responding when any event occurs (e.g., clicks, key presses, focus changes).

Related

Tkinter button issue

import tkinter
win=tkinter.Tk()
win.configure(background='grey')
k=False
def g():
k=True
v=tkinter.Button(win, text='click', command=g)
v.pack()
while k==True:
win.configure(background='black')
win.mainloop()
There's no reason why that while loop would run after the button is clicked, since (as you know) your program is run "from top to bottom", and control remains in win.mainloop() until the window is closed. (You can find that out by adding print("bye!") after that call.)
You might want to just directly call .configure(). (I gave the button some padding here so you can see the background change; otherwise the button may take up the entirety of the window and you won't see a change.)
import tkinter
win = tkinter.Tk()
def change_color():
win.configure(background='black')
button = tkinter.Button(win, text='click', command=change_color)
button.pack(padx=10, pady=10)
win.mainloop()

Tkinter mainloop() not quitting after closing window

This is NOT a duplicate of Python tkinter mainloop not quitting on closing the window
I have an app that builds on tkinter. I observed at sometimes after I close the window using the X button, the code will not execute past the mainloop() line. This happens completely randomly about 10% of chance. Rest of the time, it works like a charm. I would like to ask if there are any way to force it. As I said, the code blocks on the mainloop() line, thus calling sys.exit() after it does not help.
I am using Python 3.9.8.
This is not 100% reproducible, but here is something that might trigger the problem:
from tkinter import *
root = Tk()
Label(root, 'hi').pack()
mainloop()
print('exited')
My first thought is to use root.mainloop() instead of tkinter.mainloop(). This makes a difference if you are using several windows.
That said, I did see this a long time ago on some old OSes. Never figured out the reason, so I just wrote my own quit function, like this:
import tkinter as tk
def _quit():
root.quit()
root.destroy()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", _quit)
tk.Label(root, 'hi').pack()
root.mainloop()
print('exited')

Can I call a function by clicking 'OK' in messagebox.showinfo?

I have added a message box in my code and I want to call a function defined earlier when clicking the 'OK' button in the message box. Is there any way to achieve this? Thanks in advance.
messagebox.showinfo('Correct', 'Correct!\nClick OK to continue')
When you make a showinfo box, it actually halts your script until you press OK. All you will need to do is put the function you want right after it.
See this example:
import tkinter as tk
from tkinter import messagebox
def go():
messagebox.showinfo('Correct', 'Correct!\nClick OK to continue')
print('yeah')
root = tk.Tk()
btn = tk.Button(root, text='click', command=go)
btn.pack()
root.mainloop()
If you run that, you will see the print('yeah') only happens when the user clicks OK. So, you can do something similar and call the function where I have put the print.
I had a similar problem and looked for a simple code. You can use:
from tkinter import Tk,messagebox
root=Tk()
result=messagebox.showinfo('Correct','Correct!')
root.destroy()
go()
I used root.destroy() because sometimes I had problems closing the popup window.
In tkinter.messagebox.showinfo your only option is to press OK or close the tkinter popup window, both has the same effect and returns a string "ok".
If you instead want to call a function only when you press yes use tkinter.messagebox.askyesno:
from tkinter import Tk,messagebox
root=Tk()
result=messagebox.askyesno('Correct','Correct!')
root.destroy()
if result==True:
go()

tkinter.Button retains depressed appearance after exiting event handling

Trying this again...I have a Python programmed GUI in which the pressed Button retains a depressed look after the event handler exits. The event handler made use of a messagebox. Normally, this does not happen. Here is an example that recreates the problem:
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message(event):
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me")
b.bind("<Button-1>", func=post_message)
b.pack()
root.mainloop()
When you use the blind with the event Button-1, you aren't using the main event of the button. You can active the main event of the button with the argument command.
import tkinter as tk
from tkinter import messagebox
def post_message():
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me", command=post_message)
b.pack()
root.mainloop()
While I'm not sure why your code isn't working properly, since I'm fairly new to Py, I managed to rewrite it to work with minimal changes.
Solution 1
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message():
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me", command=post_message)
b.pack()
root.mainloop()
What I changed:
no more bind() as this caused problem, instead the function is called by adding command= option while declaring Button object,
also notice that command option doesn't provide function called with event parameter, so this had to be removed or else errors would occur.
Another workaround, this time it works with bind() just fine!
Solution 2
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message(event):
root.after(0, lambda: messagebox.showinfo\
("Sample Messgebox", "close this and look at button"))
root = tk.Tk()
b = tk.Button(root, text="Press Me")
b.bind("<Button-1>", post_message)
b.pack()
root.mainloop()
I used master.after(time_in_ms, callback_func) to tell the program that it should run a given func after the given time, here 0ms so ASAP.
Why is that lambda inside after? Lambda is a dynamic, not-named function. After takes a reference to the function you want called, so you can't directly give it parameters.
To do so, like in this example, set up a lambda that will be refrenced.
When it finally gets called, that lambda func will then call the actual function you wanted to call giving it the parameters it needs.
If you don't know yet how lambdas work, I know you're confused right now, so read more on them here, they're super-useful: Lambdas explained
For great source of info on tkinter, please visit effbot.org Events and Bindings

tkinter and time.sleep

I am trying to delete text inside a text box after waiting 5 seconds, but instead the program wont run and does sleep over everything else. Also is there a way for me to just make my textbox sleep so i can run other code while the text is frozen?
from time import time, sleep
from Tkinter import *
def empty_textbox():
textbox.insert(END, 'This is a test')
sleep(5)
textbox.delete("1.0", END)
root = Tk()
frame = Frame(root, width=300, height=100)
textbox = Text(frame)
frame.pack_propagate(0)
frame.pack()
textbox.pack()
empty_textbox()
root.mainloop()
You really should be using something like the Tkinter after method rather than time.sleep(...).
There's an example of using the after method at this other stackoverflow question.
Here's a modified version of your script that uses the after method:
from time import time, sleep
from Tkinter import *
def empty_textbox():
textbox.delete("1.0", END)
root = Tk()
frame = Frame(root, width=300, height=100)
textbox = Text(frame)
frame.pack_propagate(0)
frame.pack()
textbox.pack()
textbox.insert(END, 'This is a test')
textbox.after(5000, empty_textbox)
root.mainloop()
You can emulate time.sleep in tkinter. For this we still need to use the .after method to run our code alongside the mainloop, but we could add readability to our code with a sleep function. To add the desired behavior, tkinter provides another underestimated feature, wait_variable. wait_variable stops the codeblock till the variable is set and thus can be scheduled with after.
def tksleep(t):
'emulating time.sleep(seconds)'
ms = int(t*1000)
root = tk._get_default_root('sleep')
var = tk.IntVar(root)
root.after(ms, var.set, 1)
root.wait_variable(var)
Real world examples:
update a Label to display a clock while-loop
animated writing nested for-loops
Limitation:
tkinter does not quit while tksleep is used.
Make sure there is no pending tksleep by exiting the application.
Using tksleep casually can lead to unintended behavior
UPDATE
TheLizzard worked out something superior to the code above here. Instead of tkwait command he uses the mainloop and this overcomes the bug of not quitting the process as described above, but still can lead to unintended output, depending on what you expect:
import tkinter as tk
def tksleep(self, time:float) -> None:
"""
Emulating `time.sleep(seconds)`
Created by TheLizzard, inspired by Thingamabobs
"""
self.after(int(time*1000), self.quit)
self.mainloop()
tk.Misc.tksleep = tksleep
# Example
root = tk.Tk()
root.tksleep(2)

Categories