Advertisement
Uno-Dan

Treeview selection

Apr 25th, 2020
622
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.49 KB | None | 0 0
  1.  
  2. import tkinter as tk
  3. import tkinter.ttk as ttk
  4. import tkinter.font as tkfont
  5.  
  6. TITLE_BAR_HEIGHT = 37
  7.  
  8.  
  9. class App(tk.Tk):
  10.     def __init__(self):
  11.         super().__init__()
  12.         self.title('Treeview Demo')
  13.         self.geometry('275x650')
  14.         self.rowconfigure(0, weight=1)
  15.         self.columnconfigure(0, weight=1)
  16.  
  17.         tv = self.tv = ttk.Treeview(self)
  18.         tv.heading('#0', text='Name')
  19.         tv.tag_configure('odd', background='#aaaaaa')
  20.         tv.tag_configure('even', background='#ffffff')
  21.         tv.tag_configure('selected_odd', background='#25a625')
  22.         tv.tag_configure('selected_even', background='#b0eab2')
  23.  
  24.         tag = 'odd'
  25.         # Populate tree with test data.
  26.         for idx in range(0, 4):
  27.             tag = 'even' if tag == 'odd' else 'odd'
  28.             tv.insert('', idx, f'!{idx}', text=f'Item {idx+1}', open=1, tags=(tag,))
  29.             tag = 'even' if tag == 'odd' else 'odd'
  30.             iid = f'!{idx}_!{0}'
  31.             tv.insert(f'!{idx}', '0', iid, text=f'Menu {idx+1}', open=1, tags=(tag,))
  32.             for i in range(0, 5):
  33.                 tag = 'even' if tag == 'odd' else 'odd'
  34.                 tv.insert(iid, i, f'{iid}_!{i}', text=f'Sub item {i+1}', tags=(tag,))
  35.  
  36.             tag = 'even' if tag == 'odd' else 'odd'
  37.             tv.insert(iid, 5, f'{iid}_!{5}', text=f'Another Menu {idx+1}', open=1, tags=(tag,))
  38.             iid = f'{iid}_!{5}'
  39.             for i in range(0, 3):
  40.                 tag = 'even' if tag == 'odd' else 'odd'
  41.                 tv.insert(iid, i, f'{iid}_!{i}', text=f'Sub item {i+1}', tags=(tag,))
  42.  
  43.         tv.config(selectmode="none")
  44.         tv.grid(sticky='NSEW')
  45.  
  46.         dw = tk.Toplevel()
  47.         dw.overrideredirect(True)
  48.         dw.wait_visibility(self)
  49.         dw.wm_attributes('-alpha', 0.2)
  50.         dw.wm_attributes("-topmost", True)
  51.         dw.config(bg='#00aaff')
  52.         dw.withdraw()
  53.  
  54.         self.active_item = ''
  55.         font = tkfont.nametofont('TkTextFont')
  56.         linespace = font.metrics('linespace') + 5
  57.  
  58.         def set_row_colors(event):
  59.             def get_selected():
  60.                 self.unbind('<Motion>')
  61.                 pos_y = int(self.geometry().rsplit('+', 1)[-1])
  62.                 current_y = dw.winfo_rooty() - TITLE_BAR_HEIGHT - pos_y
  63.                 end = dw.winfo_rooty() + dw.winfo_height() - TITLE_BAR_HEIGHT - pos_y
  64.                 _root = tv.identify('item', event.x, event.y)
  65.  
  66.                 _selected = []
  67.                 if current_y < end:
  68.                     while current_y < end:
  69.                         current_y += 1
  70.                         _item = tv.identify('item', event.x, current_y)
  71.                         if not _item or _item in _selected:
  72.                             continue
  73.                         _selected.append(_item)
  74.  
  75.                 if _root not in _selected and _root:
  76.                     _selected.append(_root)
  77.                 self.bind('<Motion>', selection_window)
  78.  
  79.                 return sorted(_selected)
  80.  
  81.             item = tv.identify('item', event.x, event.y)
  82.             if item == self.active_item:
  83.                 return
  84.  
  85.             selected = get_selected()
  86.  
  87.             for item in selected:
  88.                 if not tv.tag_has('selected', item):
  89.                     if tv.tag_has('odd', item):
  90.                         tag_replace(item, 'odd', 'selected_odd')
  91.                         tags_add(item, '_selected')
  92.                         self.active_item = item
  93.                     elif tv.tag_has('even', item):
  94.                         tag_replace(item, 'even', 'selected_even')
  95.                         tags_add(item, '_selected')
  96.                         self.active_item = item
  97.                 else:
  98.                     if item == self.anchor_item:
  99.                         continue
  100.                     if tv.tag_has('selected_odd', item):
  101.                         tag_replace(item, 'selected_odd', 'odd')
  102.                         tags_add(item, '_selected')
  103.                     elif tv.tag_has('selected_even', item):
  104.                         tag_replace(item, 'selected_even', 'even')
  105.                         tags_add(item, '_selected')
  106.  
  107.             for item in tv.tag_has('_selected'):
  108.                 if item not in selected:
  109.                     tags_remove(item, '_selected')
  110.                     if tv.tag_has('odd', item):
  111.                         tag_replace(item, 'odd', 'selected_odd')
  112.                     elif tv.tag_has('even', item):
  113.                         tag_replace(item, 'even', 'selected_even')
  114.                     elif tv.tag_has('selected_odd', item):
  115.                         tag_replace(item, 'selected_odd', 'odd')
  116.                     elif tv.tag_has('selected_even', item):
  117.                         tag_replace(item, 'selected_even', 'even')
  118.  
  119.         def tag_replace(item, old, new):
  120.             tags_remove(item, old)
  121.             tags_add(item, new)
  122.  
  123.         def tags_add(item, tags):
  124.             if isinstance(tags, str):
  125.                 tags = (tags,)
  126.  
  127.             _tags = list(tv.item(item)['tags'])
  128.             _tags = [] if not _tags else _tags
  129.  
  130.             for _tag in tags:
  131.                 if _tag not in _tags:
  132.                     _tags.append(_tag)
  133.             tv.item(item, tags=_tags)
  134.  
  135.         def tags_remove(item, tags):
  136.             if isinstance(tags, str):
  137.                 tags = (tags,)
  138.  
  139.             _tags = list(tv.item(item)['tags'])
  140.             for _tag in tags:
  141.                 if _tag in _tags:
  142.                     _tags.pop(_tags.index(_tag))
  143.             tv.item(item, tags=_tags)
  144.  
  145.         def tags_remove_all(*tags):
  146.             def do_remove(node):
  147.                 tags_remove(node, tags)
  148.                 for node in tv.get_children(node):
  149.                     do_remove(node)
  150.  
  151.             for child in tv.get_children():
  152.                 do_remove(child)
  153.  
  154.         def tags_refresh(event):
  155.             for item in tv.tag_has('selected_odd'):
  156.                 tags_add(item, 'odd')
  157.                 tags_remove(item, 'selected_odd')
  158.  
  159.             for item in tv.tag_has('selected_even'):
  160.                 tags_add(item, 'even')
  161.                 tags_remove(item, 'selected_even')
  162.  
  163.         def escape(event):
  164.             tags_remove_all('selected', '_selected')
  165.             tags_refresh(event)
  166.  
  167.         def button_press(event):
  168.             self.selected = []
  169.             self.anchor_x, self.anchor_y = event.x, event.y
  170.             item = self.anchor_item = self.active_item = tv.identify('item', event.x, event.y)
  171.  
  172.             if not item:
  173.                 return
  174.  
  175.             if event.state == 20:
  176.                 tags_add(item, 'selected')
  177.  
  178.                 if tv.tag_has('odd', item):
  179.                     tag_replace(item, 'odd', 'selected_odd')
  180.                 elif tv.tag_has('selected_odd', item):
  181.                     tag_replace(item, 'selected_odd', 'odd')
  182.                 elif tv.tag_has('even', item):
  183.                     tag_replace(item, 'even', 'selected_even')
  184.                 elif tv.tag_has('selected_even', item):
  185.                     tag_replace(item, 'selected_even', 'even')
  186.             else:
  187.                 tags_remove_all('selected', '_selected')
  188.                 tags_refresh(event)
  189.                 tags_add(item, 'selected')
  190.                 if tv.tag_has('odd', item):
  191.                     tag_replace(item, 'odd', 'selected_odd')
  192.                 elif tv.tag_has('even', item):
  193.                     tag_replace(item, 'even', 'selected_even')
  194.  
  195.             dw.geometry('0x0+0+0')
  196.             dw.deiconify()
  197.             self.bind('<Motion>', selection_window)
  198.  
  199.         def button_release(event):
  200.             dw.withdraw()
  201.             self.unbind('<Motion>')
  202.  
  203.             for item in tv.tag_has('_selected'):
  204.                 tags_add(item, ('selected', ))
  205.             tags_add(tv.identify('item', event.x, event.y), ('selected', ))
  206.             tags_remove_all('_selected')
  207.  
  208.         def selection_window(event):
  209.             root_x = self.winfo_rootx()
  210.             if event.x < self.anchor_x:
  211.                 width = self.anchor_x - event.x
  212.                 coord_x = root_x + event.x
  213.             else:
  214.                 width = event.x - self.anchor_x
  215.                 coord_x = root_x + self.anchor_x
  216.  
  217.             if coord_x+width > root_x+self.winfo_width():
  218.                 width -= (coord_x+width)-(root_x+self.winfo_width())
  219.             elif self.winfo_pointerx() < root_x:
  220.                 width -= (root_x - self.winfo_pointerx())
  221.                 coord_x = root_x
  222.  
  223.             root_y = self.winfo_rooty()
  224.             if event.y < self.anchor_y:
  225.                 height = self.anchor_y - event.y
  226.                 coord_y = root_y + event.y
  227.             else:
  228.                 height = event.y - self.anchor_y
  229.                 coord_y = root_y + self.anchor_y
  230.  
  231.             if coord_y+height > root_y+self.winfo_height():
  232.                 height -= (coord_y+height)-(root_y+self.winfo_height())
  233.             elif self.winfo_pointery() < root_y + linespace:
  234.                 height -= (root_y - self.winfo_pointery() + linespace)
  235.                 coord_y = root_y + linespace
  236.                 if height < 0:
  237.                     height = tv.winfo_rooty() + self.anchor_y
  238.  
  239.             dw.geometry(f'{width}x{height}+{coord_x}+{coord_y}')
  240.             dw.update_idletasks()
  241.  
  242.             set_row_colors(event)
  243.  
  244.         self.update_idletasks()
  245.         self.bind('<Escape>', escape)
  246.         self.bind('<Button-1>', button_press)
  247.         self.bind('<ButtonRelease-1>', button_release)
  248.  
  249.  
  250. def main():
  251.     app = App()
  252.     app.mainloop()
  253.  
  254.  
  255. if __name__ == '__main__':
  256.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement