The code below shows part of my program and the issue im facing.
def checkAnswer():
mainAnswer = answer01.get()
if len(mainAnswer) == 0:
messagebox.showwarning(message='Please answer the question!')
return
if int(mainAnswer) != answer:
messagebox.showwarning(message='Incorrect! The correct answer is: ' + str(answer))
else:
nxtquest.config(state=NORMAL)
messagebox.showinfo(message='Correct! :)')question01 = Label(easy)
question01.grid(row=2, column=0)
answer01 = Entry(easy)
answer01.grid(row=3, column=2)
answer01.bind('<Return>', func=lambda e:checkAnswer())
start = Button(easy, text = "Start!", command=ask, bg='green', fg='white')
start.grid(row=3, column=3)
nxtquest = Button(easy, text='Next Question', command=ask)
nxtquest.grid(row=5, column=2)
checkbut = Button(easy, text='Check', command=checkAnswer)
checkbut.grid(row=4, column=2)
#check button and answer01 enabled after start pressed
launch = 1
if launch == 1:
answer01.config(state=DISABLED)
checkbut.config(state=DISABLED)
nxtquest.config(state=DISABLED)
The issue which im struggling here is that whenever i run the program everything is okay. When the window is displayed checkbut, nxtquest and label answer01 are greyed out (disabled).
The start button enables only checkbut and answer01 and then is destroyed. (So far so good)
So nxtquest will enable once the input is correct as seen in the
else:
nxtquest.config(state=NORMAL)
But when I reach another question the nxtquest button is already enabled, this is the problem!
How could I make it so the button will enable itself only after the warning message box is displayed?
Could I ask for some help with this and possibly suggestions if you see any rookie mistakes ?
Whilst I don't know of any way you could do this with a messagebox widget (although I'm sure there's an event you could use as the trigger) you can most certainly do this by substituting the messagebox with a Toplevel widget and using .protocol("WM_DELETE_WINDOW", callback()) on the widget.
This would mean that whenever the Toplevel widget was "closed" we would actually be overwriting the action taken when the event was raised and would manually handle the closing of the widget as well as whatever else we wanted it to do.
This would look something like the below:
from tkinter import *
root = Tk()
button = Button(root, text="Ok", state="disabled")
button.pack()
top = Toplevel(root)
def close():
top.destroy()
button.configure(state="active")
top.protocol("WM_DELETE_WINDOW", close)
root.mainloop()
If you close the Toplevel widget you will see that the button is now active instead. This would equally work if we added a Button to the Toplevel widget which called the function close().
Related
Is there a simple way to get the right click menu to open on texty only and not the whole window?
This was a quick mashup to illustrate my question. Inheriting from texty on line 25 was a shot in the dark, which didnt work, but it's close to a simple solution, like I am seeking. I was hoping to avoid programming a whole class each time I want to set a right click menu.
from tkinter import *
from tkinter import ttk
def menu_popup(event):
try:
popup.tk_popup(event.x_root, event.y_root, 0)
finally:
popup.grab_release()
win = Tk()
win.geometry("600x550+125+125")
e = Entry(win, width=50, font=('Helvetica', 11))
e.pack()
e.insert(0, "Some text....")
label = Label(win, text="Right-click to see a menu", font= ('Helvetica 18'))
label.pack(pady= 40)
texty=Text(win, height=10)
texty.pack()
popup = Menu(texty, tearoff=0)
popup.add_command(label="New")
popup.add_separator()
popup.add_command(label="Open")
popup.add_separator()
popup.add_command(label="Close")
win.bind("<Button-3>", menu_popup)
button = ttk.Button(win, text="Quit", command=win.destroy)
button.pack()
mainloop()
The widget on which the callback should be executed for the respective event is determined by the widget you call bind on(and the level of bind too*). So if you want the event to be identified within texty, then apply binding to it.
texty.bind("<Button-3>", menu_popup)
* There is bind_all which executes no matter which widget has focus or is called upon. Read 54.1. Levels of binding for more info.
Use the Python Tkinter , create a sub-panel (TopLevel) to show something and get user input, after user inputed, clicked the "EXIT" found the whole GUI (main panel) also destory.
How to only close the toplevel window?
from tkinter import *
lay=[]
root = Tk()
root.geometry('300x400+100+50')
def exit_btn():
top = lay[0]
top.quit()
top.destroy()
def create():
top = Toplevel()
lay.append(top)
top.title("Main Panel")
top.geometry('500x500+100+450')
msg = Message(top, text="Show on Sub-panel",width=100)
msg.pack()
btn = Button(top,text='EXIT',command=exit_btn)
btn.pack()
Button(root, text="Click me,Create a sub-panel", command=create).pack()
mainloop()
This seemed to work for me:
from tkinter import *
lay=[]
root = Tk()
root.geometry('300x400+100+50')
def create():
top = Toplevel()
lay.append(top)
top.title("Main Panel")
top.geometry('500x500+100+450')
msg = Message(top, text="Show on Sub-panel",width=100)
msg.pack()
def exit_btn():
top.destroy()
top.update()
btn = Button(top,text='EXIT',command=exit_btn)
btn.pack()
Button(root, text="Click me,Create a sub-panel", command=create).pack()
mainloop()
Your only mistake is that you're calling top.quit() in addition to calling top.destroy(). You just need to call top.destroy(). top.quit() will kill mainloop, causing the program to exit.
You can't close to root window. When you will close root window, it is close all window. Because all sub window connected to root window.
You can do hide root window.
Hide method name is withdraw(), you can use show method for deiconify()
# Hide/Unvisible
root.withdraw()
# Show/Visible
root.deiconify()
you can use lambda function with the command it's better than the normal function for your work
ex)
btn = Button(top,text='EXIT',command=exit_btn)
change the exit_btn to lambda :top.destroy()
In my case, I passed a callback function from the parent class, and once the submit button is clicked it will the callback function passing the return values.
The callback function will call the destroy method on the top-level object, thus in that way you'll close the frame and have the return value.
I have following python code in Tkinter.
import tkinter as tk
def main_gui(login, s):
login.quit() # close login window
win = tk.Tk()
win.geometry('300x150')
name = tk.Label(win, text='Hello' + s.get()) # Hello David
name.pack()
win.mainloop()
# initial Tkinter frame
login = tk.Tk()
login.title('Login')
login.geometry('300x150')
# input user name
user_name_var = tk.StringVar()
user_name_var.set('David')
tk.Label(login, text='User name').place(x=10, y=50)
user_name = tk.Entry(login, textvariable=user_name_var)
user_name.place(x=100, y=50)
input_ok = tk.Button(win_login, command=lambda: main_gui(login, user_name), text='OK', width=15)
input_ok.place(x=100, y=90)
win_login.mainloop()
I want to close login window, but my code can not close it. How to solve it.
You are almost there - only two details you have to adapt:
The method to remove a widget in Tkinter is destroy, so login.quit() should be login.destroy().
Once login is destroyed, the user_name Entry will also be destroyed, and you will not be able to get the name from it anymore. You should get the name earlier, e.g., directly in the lambda:
... lambda: main_gui(login, user_name.get()), ...
you can use the
root.withdraw()
function, this will close the window without completely destroying all of the root.after functions
I hope you can help me with a problem I have in python 2.7. I couldn't find a solution online, but I'm honestly unsure what keywords to search for, so I'm sorry if this is redundant.
The code below is an example of my problem.
import Tkinter as tk
root = tk.Tk()
#Widgets.
btn1 = tk.Label(root, text="btn1", bg="gray80")
btn2 = tk.Label(root, text="btn2", bg="gray80")
btn1.pack(side=tk.TOP, fill=tk.X)
btn2.pack(side=tk.TOP, fill=tk.X)
#Widget events.
def onClick1(event):
print "Clicked button 1."
def onRelease1(event):
print "Released button 1."
def onClick2(event):
print "Clicked button 2."
def onRelease2(event):
print "Released button 2."
#Bindings.
btn1.bind("<Button-1>", onClick1, add="+")
btn1.bind("<ButtonRelease-1>", onRelease1, add="+")
btn2.bind("<Button-1>", onClick2, add="+")
btn2.bind("<ButtonRelease-1>", onRelease2, add="+")
root.mainloop()
Whenever I click one button (technically a label) and hold it, the onClick event for it fires, but if I drag the mouse over to the other and release it, I get the same onRelease as the one I clicked, and not the one for the label I have my mouse over currently. This has held me back some time now, and I'd hate to scrap the whole feature in my program I need this for, so any help would be greatly appreciated.
The release event always fires on the same widget that got the press event. Within your handler you can ask tkinter what widget is under the cursor.
Example:
import Tkinter as tk
root = tk.Tk()
btn1 = tk.Label(root, text="btn1", bg="gray80")
btn2 = tk.Label(root, text="btn2", bg="gray80")
btn1.pack(side=tk.TOP, fill=tk.X)
btn2.pack(side=tk.TOP, fill=tk.X)
def onRelease(event):
x,y = event.widget.winfo_pointerxy()
widget = event.widget.winfo_containing(x, y)
print("widget:", widget.cget("text"))
btn1.bind("<ButtonRelease-1>", onRelease)
btn2.bind("<ButtonRelease-1>", onRelease)
root.mainloop()
I use the tkinter function to create a new window, it works fine.
When I link from this window to another window, the button moves to the first window. I don't understand why it moves.
Here is the code for the first window,
import tkinter
window = tkinter.Tk()
window.title ("Login")
window.geometry ("300x150")
username = "Gurdip"
password = "1234"
def login():
if txtUser.get() == username and txtPass.get() == password:
import NewWindow
lblUser = tkinter.Label(window, text="Username:")
lblUser.pack()
txtUser = tkinter.Entry(window)
txtUser.pack()
lblPass = tkinter.Label(window, text="Password:")
lblPass.pack()
txtPass = tkinter.Entry(window)
txtPass.pack()
btnenter = tkinter.Button(window, text="Enter", command=login)
btnenter.pack()
And for the second window
import tkinter
window = tkinter.Tk()
window.title ("The Royal Liberty School")
window.geometry ("300x150")
def webpage():
import webbrowser
webbrowser.open("http://www.royalliberty.org.uk/")
lblRlib = tkinter.Label(window, text="Welcome to the Royal Liberty School\n\nClick the link to go to our website")
lblRlib.pack()
def button():
webbutton = tkinter.Button(text ="Royal Liberty School", command = webpage)
webbutton.pack()
button()
My guess is that you're reporting that the "Royal Liberty School" button is appearing on the wrong window, rather than actually moving. I've never heard of a button moving before.
if that guess is correct, it's probably because you aren't giving it an explicit parent, so it's defaulting to the root window.
If all of that code belongs to a single program, you have another problem. You should always only ever create a single instance of Tk. If you need more than one window, create instances of Toplevel.
You are calling both by the name window. This means that there are two windows on the screen both acsessed by the name window. It is more conventional to use tkinter's Toplevel as follows
NewWindow = Toplevel(window)
Then, any items you want to place on this NewWindow, just use it in the place of window
MyButton = Button(NewWindow, text=hi)
As the other answer said, it is incorrect to have to Tk() in one program so you must use the Toplevel.