Tkinter bind problem - python

I have something like this:
from Tkinter import *
root = Tk()
root.title("Test")
def _quit():
root.destroy()
m = Menu(root)
root.config(menu=m)
fm = Menu(m, tearoff=0)
m.add_cascade(label="File", menu=fm)
fm.add_command(label="Quit", command=_quit, accelerator='Ctrl+Q')
root.bind('<Control-Q>', _quit())
root.bind('<Control-q>', _quit())
root.mainloop()
My question is:
"Why _quit() always is being called?"

When you are binding with Tkinter you typically do not call the function you wish to bind.
You're supposed to use the line
root.bind('<Control-Q>', _quit)
instead of
root.bind('<Control-Q>', _quit())
Take note of the lack of parentheses behind _quit.
This code below should work.
from Tkinter import *
root = Tk()
root.title("Test")
def _quit(event):
root.destroy()
m = Menu(root)
root.config(menu=m)
fm = Menu(m, tearoff=0)
m.add_cascade(label="File", menu=fm)
fm.add_command(label="Quit", command=lambda: _quit(None), accelerator='Ctrl+Q')
root.bind('<Control-Q>', _quit)
root.bind('<Control-q>', _quit)
root.mainloop()
EDIT:
Oops sorry, I only ran the code testing the keyword command for quit in the menu bar. Not the bound key commands. When doing bindings for Tkinter and I'm pretty sure most GUI toolkits, the binding inserts and event argument when the function is called. However the Tkinter command keyword argument does not typicaly insert an event. So you have to compromise by having the command keyword argument "artificially" insert an event argument of None (lambda: _quit(None)). This allows you to use one function in both scenarios.

Because you're calling it. Don't call it:
root.bind('<Control-Q>', _quit)

Related

problem with passing parameter to the function assigned with the default exit window button in Tkinter, using "root.protocol"

I would love some help, thanks!
I am trying to use lambdas to assign functionality to window events. It already worked for assigning the "enter" button to a function.
But, for some reason, it doesn't work for the default exit button on the window.
As you can see in the create_entry_window function, I used a lambda twice, and it worked only for the "Return".
The problem occurs with this line:
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
Here is the code:
from tkinter import Tk, Frame, BOTTOM, Entry, Text, END
def clear_and_get_entry(root, entry):
"""
"""
global entered_text
entry_text = entry.get()
entry.delete(0, END)
entry.insert(0, "")
entered_text = entry_text
root.destroy()
def exit_entry(root):
"""
"""
global entered_text
entered_text = False
print "here at exit"
root.destroy()
def create_entry_window(text):
"""
"""
root = Tk()
root.title("One picture is worth a thousand sounds!")
root.geometry("500x200")
root.resizable(width=False, height=False)
bottom_frame = Frame(root)
bottom_frame.pack(side=BOTTOM)
entry = Entry(root)
entry.pack(side=BOTTOM)
description_text = Text(root, height=50, width=100)
description_text.insert(END, text)
description_text.tag_configure("center", justify='center')
description_text.tag_add("center", "1.0", "end")
description_text.pack()
root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root)))
entry.bind("<Return>", (lambda event: clear_and_get_entry(root, entry)))
return root
if __name__ == '__main__':
root = create_entry_window("Some text")
root.mainloop()
When trying to exit the window, I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Heights\PortableApps\PortablePython2.7.6.1\App\lib\lib-tk\Tkinter.py", line 1470, in __call__
return self.func(*args)
TypeError: <lambda>() takes exactly 1 argument (0 given)
The quick-fix to your problem is quite easy:
Change the line reading root.protocol("WM_DELETE_WINDOW", (lambda event: exit_entry(root))) into
root.protocol("WM_DELETE_WINDOW", lambda: exit_entry(root))
The root.protocol does not provide an argument when triggered. But your lambda expects one. Removing the argument event fixes the problem for me.
Please note that there are some other issues with your code.
You are still using Python 2. Unless there are very compelling reasons to do so, I suggest you move forward to Python 3. I had not problems running your code with Python 3. All I needed to do was to add brackets to the print statement:
print "here at exit" -> print("here at exit")
Secondly: You are using global in order for your functions to communicate with each other. This is considered bad-practice. It leads to confusing code that is very hard to debug. I suggest that you have a closer look at some tkinter examples and how they are using an object oriented approach to deal with this issue. A possible starting point could be Introduction_to_GUI_Programming. The Calculator class looks like a good starting-point to me.

How to use a <ComboboxSelected> virtual event with tkinter

I am using a tkk.Combobox themed widget in Python 3.5.2. I want an action to happen when a value is selected.
In the Python docs, it says:
The combobox widgets generates a <<ComboboxSelected>> virtual event when the user selects an element from the list of values.
Here on the Stack, there are a number of answers (1, 2, etc) that show how to bind the event:
cbox.bind("<<ComboboxSelected>>", function)
However, I can't make it work. Here's a very simple example demonstrating my non-functioning attempt:
import tkinter as tk
from tkinter import ttk
tkwindow = tk.Tk()
cbox = ttk.Combobox(tkwindow, values=[1,2,3], state='readonly')
cbox.grid(column=0, row=0)
cbox.bind("<<ComboboxSelected>>", print("Selected!"))
tkwindow.mainloop()
I get one instance of "Selected!" immediately when I run this code, even without clicking anything. But nothing happens when I actually select something in the combobox.
I'm using IDLE in Windows 7, in case it makes a difference.
What am I missing?
The problem is not with the event <<ComboboxSelected>>, but the fact that bind function requires a callback as second argument.
When you do:
cbox.bind("<<ComboboxSelected>>", print("Selected!"))
you're basically assigning the result of the call to print("Selected!") as callback.
To solve your problem, you can either simply assign a function object to call whenever the event occurs (option 1, which is the advisable one) or use lambda functions (option 2).
Here's the option 1:
import tkinter as tk
from tkinter import ttk
tkwindow = tk.Tk()
cbox = ttk.Combobox(tkwindow, values=[1,2,3], state='readonly')
cbox.grid(column=0, row=0)
def callback(eventObject):
print(eventObject)
cbox.bind("<<ComboboxSelected>>", callback)
tkwindow.mainloop()
Note the absence of () after callback in: cbox.bind("<<ComboboxSelected>>", callback).
Here's option 2:
import tkinter as tk
from tkinter import ttk
tkwindow = tk.Tk()
cbox = ttk.Combobox(tkwindow, values=[1,2,3], state='readonly')
cbox.grid(column=0, row=0)
cbox.bind("<<ComboboxSelected>>", lambda _ : print("Selected!"))
tkwindow.mainloop()
Check what are lambda functions and how to use them!
Check this article to know more about events and bindings:
http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
Thanks you for the posts. I tried *args and it workes with bind and button as well:
import tkinter as tk
from tkinter import ttk
tkwindow = tk.Tk()
cbox = ttk.Combobox(tkwindow, values=[1,2,3], state='readonly')
def callback(*args):
print(eventObject)
cbox.bind("<<ComboboxSelected>>", callback)
btn = ttk.Button(tkwindow, text="Call Callback", command=callback);
tkwindow.mainloop()

Tkinter exit error [duplicate]

How do I end a Tkinter program? Let's say I have this code:
from Tkinter import *
def quit():
# code to exit
root = Tk()
Button(root, text="Quit", command=quit).pack()
root.mainloop()
How should I define the quit function to exit my application?
You should use destroy() to close a Tkinter window.
from Tkinter import *
#use tkinter instead of Tkinter (small, not capital T) if it doesn't work
#as it was changed to tkinter in newer Python versions
root = Tk()
Button(root, text="Quit", command=root.destroy).pack() #button to close the window
root.mainloop()
Explanation:
root.quit()
The above line just bypasses the root.mainloop(), i.e., root.mainloop() will still be running in the background if quit() command is executed.
root.destroy()
While destroy() command vanishes out root.mainloop(), i.e., root.mainloop() stops. <window>.destroy() completely destroys and closes the window.
So, if you want to exit and close the program completely, you should use root.destroy(), as it stops the mainloop() and destroys the window and all its widgets.
But if you want to run some infinite loop and don't want to destroy your Tkinter window and want to execute some code after the root.mainloop() line, you should use root.quit(). Example:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()
#do something
See What is the difference between root.destroy() and root.quit()?.
def quit()
root.quit()
or
def quit()
root.destroy()
import tkinter as tk
def quit(root):
root.destroy()
root = tk.Tk()
tk.Button(root, text="Quit", command=lambda root=root:quit(root)).pack()
root.mainloop()
Illumination in case of confusion...
def quit(self):
self.destroy()
exit()
A) destroy() stops the mainloop and kills the window, but leaves python running
B) exit() stops the whole process
Just to clarify in case someone missed what destroy() was doing, and the OP also asked how to "end" a tkinter program.
I think you wrongly understood the quit function of Tkinter. This function does not require you to define.
First, you should modify your function as follows:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.quit).pack()
root.mainloop()
Then, you should use '.pyw' suffix to save this files and double-click the '.pyw' file to run your GUI, this time, you can end the GUI with a click of the Button, and you can also find that there will be no unpleasant DOS window. (If you run the '.py' file, the quit function will fail.)
The usual method to exit a Python program:
sys.exit()
(to which you can also pass an exit status) or
raise SystemExit
will work fine in a Tkinter program.
In case anyone wants to bind their Escape button to closing the entire GUI:
master = Tk()
master.title("Python")
def close(event):
sys.exit()
master.bind('<Escape>',close)
master.mainloop()
The easiest way would be to click the red button (leftmost on macOS and rightmost on Windows).
If you want to bind a specific function to a button widget, you can do this:
class App:
def __init__(self, master)
frame = Tkinter.Frame(master)
frame.pack()
self.quit_button = Tkinter.Button(frame, text = 'Quit', command = frame.quit)
self.quit_button.pack()
Or, to make things a little more complex, use protocol handlers and the destroy() method.
import tkMessageBox
def confirmExit():
if tkMessageBox.askokcancel('Quit', 'Are you sure you want to exit?'):
root.destroy()
root = Tk()
root.protocol('WM_DELETE_WINDOW', confirmExit)
root.mainloop()
you only need to type this:
root.destroy()
and you don't even need the quit() function cause when you set that as commmand it will quit the entire program.
you dont have to open up a function to close you window, unless you're doing something more complicated:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
In idlelib.PyShell module, root variable of type Tk is defined to be global
At the end of PyShell.main() function it calls root.mainloop() function which is an infinite loop and it runs till the loop is interrupted by root.quit() function. Hence, root.quit() will only interrupt the execution of mainloop
In order to destroy all widgets pertaining to that idlelib window, root.destroy() needs to be called, which is the last line of idlelib.PyShell.main() function.
I normally use the default tkinter quit function, but you can do your own, like this:
from tkinter import *
from tkinter.ttk import *
window = Tk()
window.geometry('700x700') # 700p x 700p screen
def quit(self):
proceed = messagebox.askyesno('Quit', 'Quit?')
proceed = bool(proceed) # So it is a bool
if proceed:
window.quit()
else:
# You don't really need to do this
pass
btn1 = Button(window, text='Quit', command=lambda: quit(None))
window.mainloop()
For menu bars:
def quit():
root.destroy()
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="menubarname", menu=filemenu)
root.config(menu=menubar)
root.mainloop()
I use below codes for the exit of Tkinter window:
from tkinter import*
root=Tk()
root.bind("<Escape>",lambda q:root.destroy())
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=root.destroy).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="quit",command=quit).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=exit).pack()
root.mainloop()
Code snippet below. I'm providing a small scenario.
import tkinter as tk
from tkinter import *
root = Tk()
def exit():
if askokcancel("Quit", "Do you really want to quit?"):
root.destroy()
menubar = Menu(root, background='#000099', foreground='white',
activebackground='#004c99', activeforeground='white')
fileMenu = Menu(menubar, tearoff=0, background="grey", foreground='black',
activebackground='#004c99', activeforeground='white')
menubar.add_cascade(label='File', menu=fileMenu)
fileMenu.add_command(label='Exit', command=exit)
root.config(bg='#2A2C2B',menu=menubar)
if __name__ == '__main__':
root.mainloop()
I have created a blank window here & add file menu option on the same window(root window), where I only add one option exit.
Then simply run mainloop for root.
Try to do it once
Of course you can assign the command to the button as follows, however, if you are making a UI, it is recommended to assign the same command to the "X" button:
def quit(self): # Your exit routine
self.root.destroy()
self.root.protocol("WM_DELETE_WINDOW", self.quit) # Sets the command for the "X" button
Button(text="Quit", command=self.quit) # No ()
There is a simple one-line answer:
Write - exit() in the command
That's it!

Tkinter multi GUI windows [duplicate]

So for some reason the scale function in tkinter doesn't want to output the number on the scale. All i receive is either 0.0 or nothing. It seems to be to do with the GUI and calling functions through the GUI. Written and run in python 3.4.
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
iterations=30
def settings():
global itervar, iterscale
sGui = Tk()
itervar = DoubleVar()
iterscale = Scale(sGui, orient="horizontal", from_=1, to=1000, variable=itervar)
iterscale.pack()
iterbutton = Button(sGui, text="Save Settings", command=saveSettings)
iterbutton.pack()
sGui.mainloop()
def saveSettings():
global iterations
iterations = itervar.get()
print(iterations)
def doNothing():
pass
def main():
global root, version
root= Tk()
menu = Menu(root)
root.config(menu=menu)
fileMenu = Menu(menu)
menu.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Quit", command=quit)
benchmarkMenu = Menu(menu)
menu.add_cascade(label="Benchmark", menu=benchmarkMenu)
benchmarkMenu.add_command(label="Run [All]", command=doNothing)
benchmarkMenu.add_separator()
benchmarkMenu.add_command(label="Settings", command=settings)
root.mainloop()
#Main
main()
I have tried the functions settings and saveSettings on their own and they work fine, but when i call it through the GUI it doesn't seem to work.
Any ideas on the problem, my only solution would be have the settings function and saveSettings function in a different file and then run that file externally with os.startfile("etc...")
Minimal fix: change this
itervar = DoubleVar()
to this:
itervar = DoubleVar(sGui)
Because you have two root applications (root and sGui are both instances of Tk) the implied parent widget for itervar is the first one created, being root so tkinter gets confused when you specify it as the variable for a completely different application.
But I would highly recommend using a Toplevel instance to keep the windows a part of the same program:
sGui = Toplevel(root)
...
#sGui.mainloop() #no longer need this
although if you want to be able to run the setting window without the main one you might consider making all your visible windows Toplevels and make the actual root hidden:
# I'm not sure if you want to call it this
abs_root = Tk() # but you are already using root
abs_root.withdraw() #hide the window
Then make root = Toplevel(abs_root)
You coud phase out the variable all together by using .geting the scale directly:
iterations = iterscale.get()

How to close a Tkinter window by pressing a Button?

Write a GUI application with a button labeled "Good-bye". When the
Button is clicked, the window closes.
This is my code so far, but it is not working. Can anyone help me out with my code?
from Tkinter import *
window = Tk()
def close_window (root):
root.destroy()
frame = Frame(window)
frame.pack()
button = Button (frame, text = "Good-bye.", command = close_window)
button.pack()
window.mainloop()
With minimal editing to your code (Not sure if they've taught classes or not in your course), change:
def close_window(root):
root.destroy()
to
def close_window():
window.destroy()
and it should work.
Explanation:
Your version of close_window is defined to expect a single argument, namely root. Subsequently, any calls to your version of close_window need to have that argument, or Python will give you a run-time error.
When you created a Button, you told the button to run close_window when it is clicked. However, the source code for Button widget is something like:
# class constructor
def __init__(self, some_args, command, more_args):
#...
self.command = command
#...
# this method is called when the user clicks the button
def clicked(self):
#...
self.command() # Button calls your function with no arguments.
#...
As my code states, the Button class will call your function with no arguments. However your function is expecting an argument. Thus you had an error. So, if we take out that argument, so that the function call will execute inside the Button class, we're left with:
def close_window():
root.destroy()
That's not right, though, either, because root is never assigned a value. It would be like typing in print(x) when you haven't defined x, yet.
Looking at your code, I figured you wanted to call destroy on window, so I changed root to window.
You could create a class that extends the Tkinter Button class, that will be specialised to close your window by associating the destroy method to its command attribute:
from tkinter import *
class quitButton(Button):
def __init__(self, parent):
Button.__init__(self, parent)
self['text'] = 'Good Bye'
# Command to close the window (the destory method)
self['command'] = parent.destroy
self.pack(side=BOTTOM)
root = Tk()
quitButton(root)
mainloop()
This is the output:
And the reason why your code did not work before:
def close_window ():
# root.destroy()
window.destroy()
I have a slight feeling you might got the root from some other place, since you did window = tk().
When you call the destroy on the window in the Tkinter means destroying the whole application, as your window (root window) is the main window for the application. IMHO, I think you should change your window to root.
from tkinter import *
def close_window():
root.destroy() # destroying the main window
root = Tk()
frame = Frame(root)
frame.pack()
button = Button(frame)
button['text'] ="Good-bye."
button['command'] = close_window
button.pack()
mainloop()
You can associate directly the function object window.destroy to the command attribute of your button:
button = Button (frame, text="Good-bye.", command=window.destroy)
This way you will not need the function close_window to close the window for you.
from tkinter import *
window = tk()
window.geometry("300x300")
def close_window ():
window.destroy()
button = Button ( text = "Good-bye", command = close_window)
button.pack()
window.mainloop()
You can use lambda to pass a reference to the window object as argument to close_window function:
button = Button (frame, text="Good-bye.", command = lambda: close_window(window))
This works because the command attribute is expecting a callable, or callable like object.
A lambda is a callable, but in this case it is essentially the result of calling a given function with set parameters.
In essence, you're calling the lambda wrapper of the function which has no args, not the function itself.
from tkinter import *
def close_window():
import sys
sys.exit()
root = Tk()
frame = Frame (root)
frame.pack()
button = Button (frame, text="Good-bye", command=close_window)
button.pack()
mainloop()

Categories

Resources