Reputation: 1083
I'm using tk 8.6 with python 3.5.
I'm writing an application where I need to act on both single and double clicks, and sometimes double clicks can occur very close together.
The tk documentation states that when you bind to both single and double click events, the first click generates a Button-1 event, and the second a Double-Button-1 event. And this is what I observe when I double click once.
When I repeat the double click, I would expect to see the following events - single, double, single, double. This is what I see when I leave a significant pause between the two double clicks. When I repeat one quickly after the other, I see single, double, double, double. As I slow the operation down, the threshold for seeing one or the other is roughly the inter-click delay for discriminating a double click from two singles.
It appears that tkinter treats any click following quickly on from another as a double click. I would have expected it to qualify the first click with whether it was the second click of a double, and if it was, to treat the 'third' click as a single click. This behaviour means that the sequence of events generated by a pair of double clicks varies depending on the inter-double delay.
My workaround to get consistent behaviour has been to stop binding to double, time the singles, and do my own delay thresholding for double clicks. This I figured was cleaner than trying to interpret whether doubles were 'really' double clicks.
Is this behaviour a bug in tkinter?
What do other GUI managers do under these circumstances?
Is there a definition of what should happen under these circumstances?
/edit/ I've just had it brought to my attention that triple and quadruple clicks are also supported by tk. However, I still think there's something unexpected in tk's behaviour. If I bind to only single click, then my 'two double clicks', basically a quadruple click, generates four single events. Therefore I would expect on binding to single and double only, it should interpret a quadruple in terms of single and double clicks only as single, double, single, double. I guess my eventual program documentation should mention that I don't use triple or quad clicks. But hey, I've got my workaround. //edit/
/edit2/ I've bound to triple and quadruple clicks to see what happens, and it gets more buggy. I never get a quad event with a quad click. I do get a sequence of single, double, triple, double events delivered. I've also had a single, double, triple, triple, with AFAICS the same input timing. I'll stick to binding to singles only, and doing my own timing! //edit2/
Upvotes: 1
Views: 1207
Reputation: 8037
Out of the docs:
The Double, Triple and Quadruple modifiers are a convenience for specifying double mouse clicks and other repeated events. They cause a particular event pattern to be repeated 2, 3 or 4 times, and also place a time and space requirement on the sequence: for a sequence of events to match a Double, Triple or Quadruple pattern, all of the events must occur close together in time and without substantial mouse motion in between. For example,
<Double-Button-1>
is equivalent to<Button-1><Button-1>
with the extra time and space requirement.
-> Section Multiple matches
If more than one binding matches a particular event and they have the same tag, then the most specific binding is chosen and its script is evaluated. The following tests are applied, in order, to determine which of several matching sequences is more specific:
(c) if the modifiers specified in one pattern are a subset of the modifiers in another pattern, then the pattern with more modifiers is more specific.
So this should work:
def func2(event):
print('double')
widget.unbind('<Double-1>')
def func1(event):
print('single')
widget.bind('<Double-1>', func2)
widget.bind('<Button-1>', func1)
Edit
and it works, try:
import tkinter as tk
root = tk.Tk()
up = tk.Frame(root)
up.grid(column=0, row=0,columnspan=3,sticky='n')
s1 = tk.Label(up, text='spacer')
s1.pack(fill='both',expand=1)
def func4(event):
print('Quadruple')
s1.unbind('<Quadruple-1>')
def func3(event):
print('triple')
s1.unbind('<Triple-1>')
s1.bind('<Quadruple-1>', func4)
def func2(event):
print('double')
s1.unbind('<Double-1>')
s1.bind('<Triple-1>', func3)
def func1(event):
print('single')
s1.bind('<Double-1>', func2)
s1.bind('<Button-1>', func1)
root.mainloop()
Upvotes: 0
Reputation: 1083
Following the lead of Atlas435, I've now got another way to implement my 'two double clicks' as asked for in the question. My workaround involved binding to only single events and manually timing them, which was a bit impure as it duplicated the double-click timing settings set via the OS. A distributed case of DRY.
I'm not sure if dynamically binding and unbinding was really what I was after, but it is straightforward, and it does work. It's a good expression of exactly what I want, which is 'don't respond double click when the previous click was the second one of a double'.
import tkinter as tki
root = tki.Tk()
s1 = tki.Label(root, text='click here', width=15, height=3)
s1.pack()
def func2(event):
print('double')
s1.unbind('<Double-1>')
def func1(event):
print('single')
s1.bind('<Double-1>', func2)
s1.bind('<Button-1>', func1)
root.mainloop()
Upvotes: 1