0

I want to select multiple lines from a ttk.Treeview widget. The minimal code which follows produces this window:

ttk.Treeview screenshot

Simply clicking in the window produces correct results. The resulting tree selection is printed when treeview_callback is called.

However, <Cmd> clicking which should produce an extended selection does not work but only when the widget is first displayed. The callback function is not called by the virtual event <<TreeviewSelect>>. <Cmd> clicking can be made to work by a prior mouse selection without the <Cmd> key.

The failure is inconsistent. It happens when the treeview is first shown. After a number of <Cmd> clicks on the same color they start to register. The number of clicks varies but has always been less than twenty. I have not been able to detect any pattern that might explain when it starts working. Once it has started working correctly no relapse into the failure mode has been noticed.

"""Treeview selection error demo."""

import tkinter as tk
import tkinter.ttk as ttk


def treeview_callback(tree: ttk.Treeview):
   def print_color_selection(*args):
       print(f"{tree.selection()=}")
   return print_color_selection


gui = tk.Tk()

tree = ttk.Treeview(gui, columns=('colors',), height=6, selectmode='extended', 
                    show='tree')
tree.grid(column=0, row=0)
tree.tag_bind('colors', '<<TreeviewSelect>>', callback=treeview_callback(tree))

for color in ['blue', 'white', 'red', 'green', 'goldenrod']:
    tree.insert('', 'end', color, text=color, tags='colors')
tree.selection_add('white', 'red', 'green')

gui.mainloop()
lemi57ssss
  • 1,287
  • 4
  • 17
  • 36
  • 2
    [Why is Button parameter “command” executed when declared?](https://stackoverflow.com/questions/5767228/why-is-button-parameter-command-executed-when-declared) in your case `callback=`. Change to `tree.bind('<>', ...`. Relevant: [treeview-get-return-iid-of-selected-item](https://stackoverflow.com/questions/43673883/python-tkinter-treeview-get-return-iid-of-selected-item) – stovfl Jan 20 '20 at 13:23
  • 1
    @stovfl: that's not the problem here: the bound function is returning a function, which is a valid way to define a callback. – Bryan Oakley Jan 20 '20 at 13:59

1 Answers1

1

The workaround I found is to set the focus on the first item before setting the selection:

import tkinter as tk
import tkinter.ttk as ttk


def treeview_callback(tree: ttk.Treeview):
   def print_color_selection(*args):
       print(f"{tree.selection()}")
   return print_color_selection


gui = tk.Tk()

tree = ttk.Treeview(gui, columns=('colors',), height=6, selectmode='extended', 
                    show='tree')
tree.grid(column=0, row=0)
tree.tag_bind('colors', '<<TreeviewSelect>>', callback=treeview_callback(tree))

for color in ['blue', 'white', 'red', 'green', 'goldenrod']:
    tree.insert('', 'end', color, text=color, tags='colors')

tree.focus('blue') # <- This gives focus to the first item
tree.selection_set('white', 'red', 'green')

gui.mainloop()
Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75
  • According to the docu: *["virtual events are sent to the focus item"](http://www.tcl.tk/man/tcl8.6/TkCmd/ttk_treeview.htm#M60)*. Therefore, ***"gives focus to the first item"*** is a solution. – stovfl Jan 20 '20 at 14:45
  • 1
    @JacquesGaudin. This works. If one takes the view that the act of clicking on any GUI widget should set the focus before the GUI does anything further then one has to conclude that the Treeview widget does indeed have a bug - as you originally suggested. – lemi57ssss Jan 21 '20 at 11:47
  • 1
    @lemi57ssss That's the behaviour I was expecting too but didn't want to sound pedantic about it. I'm far from an expert with tk but got to use it a few times. Maybe using `tree.bind(...)` is a cleaner way to achieve the same goal? – Jacques Gaudin Jan 21 '20 at 11:55
  • 1
    @JacquesGaudin I'm guessing that tk's tag_bind was intended to support the need to have different callbacks for different lines of the tree view. In my case all lines have the same callback so I can indeed use `tree.bind(...)` and dispense with `tree.focus(...)`. – lemi57ssss Jan 21 '20 at 13:05