On 11/09/2023 21:25, Mirko via Python-list wrote:
Am 11.09.23 um 14:30 schrieb John O'Hagan via Python-list:
I was surprised that the code below prints 'called' three times.


from tkinter import *
from tkinter.ttk import *

root=Tk()

def callback(*e):
     print('called')

tree = Treeview(root)
tree.pack()

iid = tree.insert('', 0, text='test')

tree.selection_set(iid)
tree.selection_remove(iid)
tree.selection_set(iid)

tree.bind('<<TreeviewSelect>>', callback)

mainloop()

In other words, selection events that occurred _before_ the callback
function was bound to the Treeview selections are triggering the
function upon binding. AFAIK, no other tk widget/binding combination
behaves this way (although I haven't tried all of them).

This was a problem because I wanted to reset the contents of the
Treeview without triggering a relatively expensive bound function, but
found that temporarily unbinding didn't prevent the calls.

I've worked around this by using a regular button-click binding for
selection instead, but I'm curious if anyone can cast any light on
this.

Cheers

John


AFAIK (it's been quite some time, since I used Tk/Tkinter):

These selection events are not triggered upon binding, but after the mainloop has startet. Tk's eventloop is queue-driven, so the tree.selection_{set,remove}() calls just place the events on the queue. After that, you setup a callback and when the mainloop starts, it processes the events from the queue, executing the registered callback.

I seem to remember, that I solved a similar issue by deferring the callback installation using root.after().


from tkinter import *
from tkinter.ttk import *

root=Tk()

def callback(*e):
    print('called')

tree = Treeview(root)
tree.pack()

iid = tree.insert('', 0, text='test')

tree.selection_set(iid)
tree.selection_remove(iid)
tree.selection_set(iid)

root.after(100, lambda: tree.bind('<<TreeviewSelect>>', callback))

mainloop()



This does not print "called" at all after startup (but still selects the entry), because the callback has not been installed when the mainloop starts. But any subsequent interaction with the list (clicking) will print it (since the callback is then setup).

HTH
Indeed.  And you don't need to specify a delay of 100 milliseconds. 0 will work (I'm guessing that's because queued actions are performed in the order that they were queued). I have also found that after() is a cure for some ills, though I avoid using it more than I have to because it feels ... a bit fragile, perhaps. E.g. suppose the mouse is clicked on a widget and tk responds by giving that widget the focus, but I don't want that to happen.
I can't AFAIK prevent the focus change, but I can immediately cancel it with
    X.after(0, SomeOtherWidget.focus_set)
where X is any convenient object with the "after" method (just about any widget, or the root).
Best wishes
Rob Cliffe

--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to