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.
Related
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.
I am new to tkinter, I am trying to make a button that flashes green and silver until it is pressed, at which point it reverts to silver. I followed the code from this website, flash button example, it seemed to be closest to what I was trying to do.
%reset -f
import tkinter as tk
root = tk.Tk()
def stop_flash():
print('stop_flash')
root.after_cancel(flasher2)
root.after_cancel(flasher1)
button = tk.Button(root, text="Hello", command=stop_flash, background='silver', activebackground='red')
button.pack()
def flash():
button.configure(background = 'green')
flasher1 = root.after(500, lambda: button.configure(background = 'silver'))
flasher2 = root.after(1000, flash)
flasher1 = root.after(500, lambda: button.configure(background = 'silver'))
flasher2 = root.after(1000, flash)
root.mainloop()
I got the button to flash but I don't understand why it won't stop. I have tried making a separate switch button so I would only need to use 1 after() function but it gets even messier. Any help here would be greatly appreciated!!!!!
There have already been several topics on Python/Tkinter, but I did not find an answer in them for the issue described below.
The two Python scripts below are reduced to the bare essentials to keep it simple. The first one is a simple Tkinter window with a button, and the script needs to wait till the button is clicked:
from tkinter import *
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting.")
windowItem1.mainloop()
This works fine, and we see the printout “done waiting” when the button is clicked.
The second script adds one level: we first have a menu window, and when clicking the select button of the first presented item, we have a new window opening with the same as above. However, when clicking the submit button, I don’t get the “Done waiting”. I’m stuck on the wait_variable.
from tkinter import *
windowMenu = Tk()
windowMenu.title("Menu")
def SelectItem1():
windowItem1 = Tk()
windowItem1.title("Item1")
WaitState = IntVar()
def submit():
WaitState.set(1)
print("submitted")
button = Button(windowItem1, text="Submit", command=submit)
button.grid(column=0, row=1)
print("waiting...")
button.wait_variable(WaitState)
print("done waiting")
lblItem1 = Label(windowMenu, text="Item 1 : ")
lblItem1.grid(column=0, row=0)
btnItem1 = Button(windowMenu, text="Select", command=SelectItem1)
btnItem1.grid(column=1, row=0)
windowMenu.mainloop()
Can you explain it?
Inside your SelectItem1 function, you do windowItem1 = Tk(). You shouldn't use Tk() to initialize multiple windows in your application, the way to think about Tk() is that it creates a specialized tkinter.Toplevel window that is considered to be the main window of your entire application. Creating multiple windows using Tk() means multiple main windows, and each one would need its own mainloop() invokation, which is... yikes.
Try this instead:
windowItem1 = Toplevel()
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()
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")