In Python, is there a way to bind every event generated by an object (perhaps a tkinter widget) to a single function, without explicitly naming them? The motivation here is for learning, debugging and development purposes.
(This question has arisen whilst trying to find a solution to this.)
You can bind to all of the keys like this:
def callback(event):
print(event.char, event.keysym, event.keycode)
root = Tk()
root.bind('<Key>', callback)
root.mainloop()
Related
I am working on my gui with tkinter, and I have to place stuff in precise places, i can't just use pack. Currently i write almost random numbers for width and height in place(), then run, adjust the numbers, repeat the program and check the position until the button or label fits just perfectly.
Isn't there a simpler way? Is there an option of the python launcher that allows me to point somewhere in the window and find out the coordinates?
Or maybe an extention for vcscode or a setting for pycharm?
This is about the simplest way of getting mouse coordinates
from tkinter import Tk
root = Tk()
root.bind('<Button-1>', lambda e: print(e.x, e.y))
root.mainloop()
basically .bind() binds an event (as the name suggests) to the widget and when the event is triggered .bind() method calls the given function while also passing an event argument so that has to be dealt with but it can also be used as in this case (and in most cases actually)
P.S. e is just a preference of mine when using lambda in this case and if I were to define a function as usually I would use event as argument name
As #Matiiss stated in his answer, using can use mouse coordinates.
Also, there is another event called <Motion> which you can use.
from tkinter import *
root=Tk()
lbl=Label(root,text='')
lbl.pack()
lbl1=Label(root,text='')
lbl1.pack()
root.bind("<Motion>",lambda e: [lbl.config(text=f"X pixel: {e.x}"),lbl1.config(text=f"Y pixel: {e.y}")])
root.mainloop()
I'm making a tkinter app and I need to know if my windows had focus, because I will send notifications only if the windows don't have focus. I check the root protocols but I didn't find something suitable.
This question has already been answered, but the accepted answer is more complicated than it should be.
I think the cleanest solution to this problem would be the following:
def has_focus(window):
return window.focus_displayof() # returns None if the window does not have focus
if not has_focus(root):
# do something
There can be several methods of doing this depending upon how you what the function to trigger.
Let's say you want the notification to go when the window loses focus then with the help of <FocusOut> bind we can do so
...
def send_notification(*args):
"""triggers when the window loses focus."""
...
root = tk.Tk()
root.bind('<FocusOut>', send_notification)
...
Or Let us the notification function trigger different times even if the window has focus or not then we can check in the function like so
def send_notification(*args):
"""triggers when the window loses focus."""
if not focus_check.get():
...
root = tk.Tk()
focus_check = tk.BooleanVar()
root.bind('<FocusIn>', lambda _: focus_check.set(True))
root.bind('<FocusOut>', lambda _: focus_check.set(False))
I found a StackOverflow question that seems fairly similar to yours. In essence, you need to add a binding to your Tkinter application. This will only function if your application has focus. You can also use the focus_get command to figure out if your obejct has focus if you apply it to the root object.
Here's the code the answer from 2009 used: e1.bind("<Return>", handleReturn)
I'd like to assign a key combination to do the same as an existing standard binding.
Specifically, for a tkinter.Listbox, make Ctrl-A select all (same as Ctrl-/) and also do the same for Ctrl+<the keys corresponding to those two in my national keyboard layout>.
Since the subroutines doing that exist at Tk level and not on Python level, I cannot just .bind() to them as I would do for a Python function.
If you want to duplicate any existing binding, the first thing you need to do is understand what the original sequence is bound to. In this case, the binding <Control-/> is bound to the binding tag "Listbox" (the name of the internal widget class)
The first step is to get the existing binding by making a raw call to the Tcl interpreter:
func = root.call("bind", "Listbox", "<Control-/>")
The second step is to associate that original function to the new key combination:
root.call("bind", "Listbox", "<Control-A>", func)
Note: the above will associate the binding with all Listbox widgets rather than just a specific listbox.
You could find out from the source code what tcl command is used for the specific binding.
For Listbox's specific case, Control-/ event first activates a virtual event, <<SelectAll>>. Which then calls the Tcl command for listbox'es Tcl procedure tk::ListboxSelectAll.
Let's assign Control-A to mimic Control-/.
Generate <<SelectAll>> event, so that it calls whatever it's supposed to call:
lb.bind('<Control-Key-a>', lambda event: lb.event_generate('<<SelectAll>>'))
Or you could go directly call what <<SelectAll>> eventually calls:
lb.bind('<Control-Key-a>', lambda event: lb.tk.call('tk::ListboxSelectAll', lb))
You may want to bind for all Listbox objects:
lb.bind_class(lb.winfo_class(), '<Control-Key-a>',
lambda event: lb.tk.call('tk::ListboxSelectAll', lb))
A complete example:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
if __name__ == '__main__':
root = tk.Tk()
lb = tk.Listbox(root)
for i in range(4):
lb.insert('end', i)
lb.bind_class(lb.winfo_class(), '<Control-Key-a>',
lambda event: lb.tk.call('tk::ListboxSelectAll', lb))
# assign anything but "single" or "browse"
# so that the effect is obvious
lb['selectmode'] = 'asd'
lb.pack()
tk.mainloop()
Finally, note that you may want to bind to <Control-Key-A>, too, so the binding works even with Caps Lock on.
This will effectively bind Ctrl-Shift-A, too, which you may or may not want. Conversely, with Caps Lock on, Tk would interpret Ctrl-Shift-A as Ctrl-A.
I've asked a very similar question myself.
You can simply generate the event that you want to mimic.
Generating Control-/ event so that it handles everything from that point onward:
lb.bind('<Control-Key-a>',
lambda event:lb.event_generate('<Control-Key-slash>'))
A complete example:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
if __name__ == '__main__':
root = tk.Tk()
lb = tk.Listbox(root)
for i in range(4):
lb.insert('end', i)
lb.bind('<Control-Key-a>',
lambda event:lb.event_generate('<Control-Key-slash>'))
# assign anything but "single" or "browse"
# so that the effect is obvious
lb['selectmode'] = 'asd'
lb.pack()
tk.mainloop()
Using the textvariable attribute when creating a combobox in tkinter seems completely useless. Can someone please explain what the purpose is? I looked in the Tcl documentation and it says textvariable is used to set a default value, but it looks like in tkinter you would just use the .set method to do that.
Example showing what I mean:
This doesn't work...
from Tkinter import *
import ttk
master = Tk()
test = StringVar()
country = ttk.Combobox(master, textvariable=test)
country['values'] = ('USA', 'Canada', 'Australia')
country.pack()
# This does not set a default value...
test="hello"
mainloop()
This does work.
from Tkinter import *
import ttk
master = Tk()
country = ttk.Combobox(master)
country['values'] = ('USA', 'Canada', 'Australia')
country.pack()
# This does set a default value.
country.set("hello")
mainloop()
If you are supposed to just use the .set and .get methods, what is the point of assigning anything to textvariable? Every example online seems to use textvariable, but why? It seems completely pointless.
Since Python has no type safety, you're overwriting the reference to the StringVar object with a string. To set the value, call the set method:
test = StringVar()
country = ttk.Combobox(master, textvariable=test)
#...
test.set("hello")
Under normal circumstances there is no reason to use a StringVar. I have no idea why most tutorials show it. It adds overhead but provides no extra value. As you observe, you can directly get and set the value of the combobox via the combobox object itself.
The advantage to using a StringVar comes when you want to either a) have two widgets share the same variable so that one is updated when the other is changed, or b) attach one or more traces to the StringVar. Both of those are uncommon, but are sometimes quite useful.
As Squall pointed out, to change the StringVar value you need to use its set() method.
On the other hand, the purpose of Tk Variables is to keep track of the values of the widgets they are connected to. The reason why you should prefer using a StringVar over reading the widget's value directly is that the latter is thread-safe.
Thread safety is a bit of a thorny topic for tkinter, but technically you should not be allowed to make modification to GUI elements in a thread different from the one that created the window (i.e. the original thread blocked in mainloop() plus the callbacks that you defined).
If you have an extra background thread that elaborates data and changes the UI accordingly using the country.set("hello") instruction might crash your application. Some systems forbid this entirely.
Meanwhile, setting the value in a StringVar is perfectly safe.
If you never use other threads and can work from callbacks alone, you will never face this problem and you might consider StringVar as a useless overhead. However, you will need to use variables for any kind of serious work.
This was my first go at this method and I found myself here as well.
LOL I was curious as to why I must do that. I agreed that the .set method works a lot nicer in the code block. It's esthetically pleasing. It's just that sensible default.
I tried the above codes both failed. Language deprecation?
import tkinter as tk
from tkinter import ttk
master = tk.Tk()
country = ttk.Combobox(master)
country['values'] = ('USA', 'Canada', 'Australia')
country.pack()
This does set a default value.
country.set("hello")
master.mainloop()
So I have this very simple thing I wrote and it's killing me trying to figure out why it won't work. All it does it print a statement when you click.
So for the first example I had a button and assigned the function printName1 directly to it, which worked perfectly fine.
Then the next thing was to bind it using the .bind() function. So in this case we just have a frame that prints out certain things based on which button you press. But unfortunately whenever I use bind, it throws the error show above. References tkinter\__init__.py for the error, so it's not something directly in my code but maybe it needs to be done differently? Thanks guys.
from tkinter import *
root = Tk()
def printName1():
print('Jack')
def printName2():
print('John')
def printName3():
print('Jill')
frame = Frame(root, width=300, height=250)
frame.bind("<Button-1>", printName1)
frame.bind("<Button-2>", printName2)
frame.bind("<Button-3>", printName3)
frame.pack()
root.mainloop()
EDIT: The error is confusing because it made it seem like there was an extra argument when there should be 0. But actually I needed to add an argument to the functions and that was event. so it should be def printName1(event) and so on. Just figured I would let you guys know what worked for me in case anyone stumbles upon this.
If you refer to the documentation regarding tkinter events and bindings, you will see that when an event is triggered, the associated event object will be passed as the first (and only) argument to the bounded function (being printName1 and friends in your case).
So what you need to do is to modify those printName* functions to accept the event argument.
def printName1(event):
print('Jack')
Then what you desired to achieve should work.
Naturally, you could make the event argument optional as #TigerhawkT3 suggested.
Events, such as from the keyboard/mouse, are all sent to the application with information about the event: which key was it, where was the mouse when you clicked, that sort of thing. This means that any callback bound to such an event needs to take an argument. If you want to also bind it to a Tkinter Button, which doesn't take an event, you can handle that as well. Just define your functions with a default argument:
def printName1(event=None):
...