Advertisement
Uno-Dan

Treeviewz

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