Advertisement
Uno-Dan

Treeview Demo Completely Classed.

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