Python 3 Tkinter Menu widget's callback seems out of sequence - python

I'm having trouble with the Tkinter Menu widget (no menu button), whereby the callback seems to run out of sequence. Here is a very minimal example:
# Python 3.6.5. Windows 7 x64.
from tkinter import *
root = Tk()
popup = Menu(root, tearoff=0)
popup.add_command(label="test", command=lambda: print("clicked 'test'"))
print("Before post")
popup.post(200,200) # Expecting print output from this (when clicked)
print("After post")
root.mainloop()
print("end of program")
Expected output:
Before post
clicked 'test'
After post
end of program
Actual output:
Before post
After post
clicked 'test' <--- Shouldn't this appear BEFORE previous line?
end of program
I've tried numerous things, without success, such as: popup.wait_window(), popup.update_idletasks(), popup.grab_release(), popup.unpost(), popup.destroy(), tk_popup (instead of Menu), etc.
Any advice would be appreciated.

clicked 'test' <--- Shouldn't this appear BEFORE previous line?
No, it shouldn't. The post only makes the menu appear, it will not wait for the user to select something from the menu. That's just not how tkinter menus are designed to work.
If you need your code to pause until the user makes a selection, you probably need to wait on a variable, and then make sure that all of the menu items set that variable.

I don't get that result on Linux, although apparently the command is supposed to execute. From the docs:
If a -command option is specified for a cascade entry then it is evaluated as a Tcl command whenever the entry is invoked.
My advice is don't try to trigger an event using another trigger. Instead, point both the menu command and whatever you are trying to do programmatically to the same target.
from tkinter import *
def func():
print("clicked 'test'")
root = Tk()
popup = Menu(root, tearoff=0)
popup.add_command(label="test", command=func)
root['menu'] = popup
print("Before post")
func()
print("After post")
root.mainloop()
print("end of program")

Related

Threading in Tkinter with Button Command

I'm building a tkinter app and as a part of it the user has to upload a file, write a message and then press a Button whose command runs in another thread.
self.sidebar_button = Button(self.sidebar_frame, text="send your message",
command=threading.Thread(target=send_msg).start)
If the Button is pressed in the right condition then everything is fine.
However, if the user doesn't upload the file and write the message before pressing the Button then i show the user an error message. The problem here is that since the first Button press has started the thread, it can't start again.
Can you think of a workaround for this problem?
Is it possible to disable the Button before the right conditions are met?
Look at this:
import tkinter as tk
# This is called each time there is a change in the entry
def check_conditions(*args):
message_text = message_var.get()
# Here we check the conditions:
if message_text == "":
button.config(state="disabled")
else:
button.config(state="normal")
def send():
message_text = message_var.get()
print(f"Send: {message_text!r}")
root = tk.Tk()
message_var = tk.StringVar(root)
# When the value of the `message_var` has changed, call `check_conditions`
message_var.trace("w", check_conditions)
# Create the entry and attach `message_var`
message = tk.Entry(root, textvariable=message_var)
message.pack()
button = tk.Button(root, text="Send", command=send, state="disabled")
button.pack()
root.mainloop()
It uses <tk.StringVar>.trace("w", <function>) to call check_conditions each time the user changes the entry. For more info, read this.
tkinter doesn't always like being called from other threads, so avoid using threading when using tkinter.

Why my button in tkinter don't works normal [duplicate]

This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed last year.
I wanted to make button in tkinter, but when I started program, the command always calls when code just starts.
Here is example code:
import tkinter as tk
from tkinter import messagebox
window = tk.Tk()
window.title("Why this don't works???")
window.wm_geometry("100x100")
def message():
messagebox.showinfo("Hi there")
button = tk.Button(text="Hello", command=message())
button.grid(column=0, row=0)
while True:
window.update()
And then, button didn't worked. (When you press it, it don't works.)
I don't know what I'm doing wrong, so I need help.
The command should be a pointer to a function
In the code you wrote, the command gets the return value from the function.
command=message()
The correct way is
command = message
The problem is you are requesting a return value from the fucnction. Try using this.
from tkinter import *
# import messagebox from tkinter module
import tkinter.messagebox
# create a tkinter root window
root = tkinter.Tk()
# root window title and dimension
root.title("When you press a button the message will pop up")
root.geometry('75x50')
# Create a messagebox showinfo
def onClick():
tkinter.messagebox.showinfo("Hello World!.", "Hi I'm your message")
# Create a Button
button = Button(root, text="Click Me", command=onClick, height=5, width=10)
# Set the position of button on the top of window.
button.pack(side='top')
root.mainloop()
You have 2 errors:
first:
It must be command=message
second:
You must give a message argument too, you entered a title only.
Or, what you can do is.
Add another variable.
command = message()
Before this line,
button = tk.Button(text="Hello", command=message())
And chande this line to,
button = tk.Button(text="Hello", command=command)

How to properly destroy a Toplevel widget in tkinter

I'm somewhat new to python and I'm trying to write my first GUI. I'm having problems when destroying a Toplevel widget. somehow my script gets stuck in a loop.
Basically, I have a button (in this case Hours left) when I press it, it should take me to a function that executes some code. If one condition is met (no credentials found) then it needs to open a Toplevel window where the user enters their credentials. Once they click on Log in the Toplevel window must be destroyed and the code should carry on with the rest of the commands in my function.
The problem is that when I destroy the Toplevel window my script enters a loop and it doesn't execute anything else. Here is a simplified version of my code
from tkinter import *
def loginSequence():
print("loginSequence")
def logoutSequence():
print("logoutSequence")
def hoursLeft():
print("hoursLeft")
input("Do something and press enter")
credentialsWindow()
print("Why is this message not printing?")
"""Rest of the code goes here"""
def credentialsWindow():
global credentials
credentials = Toplevel()
Label(credentials, text='Opss... I Could not find your credentials.\nPlease, log back in', font=40).pack()
Button(credentials, text='Log in', command=doSomethingandClose).pack()
Button(credentials, text='Cancel', command=destroyNewWindow).pack()
credentials.mainloop()
def doSomethingandClose():
input("Do something and press enter 2 destroy")
destroyNewWindow()
def destroyNewWindow():
credentials.destroy()
if __name__ == "__main__":
# Define the main screen for Ultipro Logger
logger_Root = Tk()
header = Label(logger_Root, text="Welcome to UltiPro Logger", font=40).pack()
# Login button
login_PB = Button(logger_Root, text=" Clock in ", font=40, bg="gray", command=loginSequence).pack()
# Logout button
logout_PB = Button(logger_Root, text=" Clock out ", font=40, bg="gray", command=logoutSequence).pack()
# Hours left alarm button
ResetAlarm_PB = Button(logger_Root, text=" Hours left ", font=40, bg="gray", command=hoursLeft).pack()
# Exit button
exit_PB = Button(logger_Root, text=" Exit ", anchor='se', justify='right', bg="gray", command=logger_Root.quit).pack()
logger_Root.mainloop()
If you run it and click "hours left" and then either cancel or log in it doesn't execute
print("Why is this message not printing?")
"""Rest of the code goes here"""
What's even weirder is that if you go back to the main menu and click exit, that piece of code will get executed. if you click exit again then the main window will close.
I have no clue why this is happening. I've been stuck for 3 days trying to figure it out but all the tkinter tutorials I've seen are pretty simple.

Is there a way to make a Button perform multiple commands

I'm doing a school project. I designed a welcome page for it on tkinter and put a button 'ok' which when pressed moves the code forward but the welcome page doesnt close itself once pressed.
i have tried defining another function to close it but that does not work.
welcome = Tk()
okbutton = Button(welcome, text='ok', command=R)
okbutton.pack()
welcome.mainloop()
and the code moves forward but welcome page remains open...Is there a method to resolve this?
Window never closes automatically when you create new window. You have to use welcome.destroy() for this. You can run it in function which creates new window.
import tkinter as tk
def welcome_page():
global welcome
welcome = tk.Tk()
tk.Label(welcome, text='Welcome').pack()
button = tk.Button(welcome, text='OK', command=other_page)
button.pack()
welcome.mainloop()
def other_page():
global welcome
global other
welcome.destroy() # close previous window
other = tk.Tk()
tk.Label(other, text='Other').pack()
button = tk.Button(other, text='OK', command=end)
button.pack()
welcome.mainloop()
def end():
global other
other.destroy() # close previous window
welcome_page()
To perform the two commands call one command inside the other (sounds like it should be towards the end) and assign the first command to the button.
A button can only call a single function, but that single function can do anything you want.
def do_ok():
print("hello!")
welcome.destroy()
welcome = Tk()
okbutton = Button(welcome, text='ok', command=do_ok)
okbutton.pack()
welcome.mainloop()

How can I reprogram the 'Ok' button in the Tkinter Messagebox module

I'm working on my very first python GUI, and I want to close all the previous windows from the code after clicking on the 'OK' button of the Message
messagebox.showinfo('Access Granted', 'Your data has been retrieved.')
The tkinter dialogs return a string representing what the user clicked on, so it's just a matter of saving that value and checking it afterwards. However, since showinfo only gives the user one option it's always going to return "ok", so there's no need to check the value. Just call your function after the dialog has been displayed:
def some_function():
messagebox.showinfo('Access Granted', 'Your data has been retrieved.')
root.destroy()
...
button = tk.Button(root, text="Quit", command=some_function)
So, say if your window was called root you would want to first define a function to 'destroy' the window
def closeWindow():
root.destroy()
Then you'd want to add that command to the button -
btn = tkinter.Button(text="Click Me!" command=closeWindow)
If you get any more errors, let me know!

Categories