Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import tkinter as tk
- import tkinter.ttk as ttk
- import tkinter.font as tkfont
- class Treeview(ttk.Treeview):
- def __init__(self, parent, **kwargs):
- super().__init__(parent, **kwargs)
- self.root = parent.winfo_toplevel()
- self.parent = parent
- self.active_item = None
- self.selected_items = []
- sw = self.select_window = tk.Toplevel(self.parent)
- sw.wait_visibility(self.parent)
- sw.withdraw()
- sw.config(bg='#00aaff')
- sw.overrideredirect(True)
- sw.wm_attributes('-alpha', 0.3)
- sw.wm_attributes("-topmost", True)
- def fixed_map(self, option):
- style = self.parent.style
- return [elm for elm in style.map("Treeview", query_opt=option) if elm[:2] != ("!disabled", "!selected")]
- def tag_add(self, tags, item):
- self.tags_update('add', tags, item)
- def tag_remove(self, tags, item=None):
- self.tags_update('remove', tags, item)
- def tag_replace(self, old, new, item=None):
- for item in (item,) if item else self.tag_has(old):
- self.tags_update('add', new, item)
- self.tags_update('remove', old, item)
- def tags_reset(self, _=None):
- self.tag_remove(('selected', '_selected'))
- self.tag_replace('selected_odd', 'odd')
- self.tag_replace('selected_even', 'even')
- def tags_update(self, opt, tags, item):
- def get_items(node):
- items.append(node)
- for node in self.get_children(node):
- get_items(node)
- if not tags:
- return
- elif isinstance(tags, str):
- tags = (tags,)
- if not item:
- items = []
- for child in self.get_children():
- get_items(child)
- else:
- items = [item]
- for item in items:
- _tags = list(self.item(item, 'tags'))
- for _tag in tags:
- if opt == 'add':
- if _tag not in _tags:
- _tags.append(_tag)
- elif opt == 'remove':
- if _tag in _tags:
- _tags.pop(_tags.index(_tag))
- self.item(item, tags=_tags)
- def button_press(self, event):
- self.root.anchor_x, self.root.anchor_y = event.x, event.y
- item = self.root.anchor_item = self.active_item = self.identify('item', event.x, event.y)
- sw = self.select_window
- sw.geometry('0x0+0+0')
- sw.deiconify()
- self.bind('<Motion>', self.set_selected)
- if not item:
- if not event.state & 1 << 2:
- self.tags_reset()
- return
- if event.state & 1 << 2:
- if self.tag_has('odd', item):
- self.tag_add('selected', item)
- self.tag_replace('odd', 'selected_odd', item)
- elif self.tag_has('even', item):
- self.tag_add('selected', item)
- self.tag_replace('even', 'selected_even', item)
- elif self.tag_has('selected_odd', item):
- self.tag_replace('selected_odd', 'odd', item)
- elif self.tag_has('selected_even', item):
- self.tag_replace('selected_even', 'even', item)
- else:
- self.tags_reset()
- self.tag_add('selected', item)
- if self.tag_has('odd', item):
- self.tag_replace('odd', 'selected_odd', item)
- elif self.tag_has('even', item):
- self.tag_replace('even', 'selected_even', item)
- def button_release(self, _):
- self.select_window.withdraw()
- self.unbind('<Motion>')
- for item in self.selected_items:
- if self.tag_has('odd', item) or self.tag_has('even', item):
- self.tag_remove(('selected', '_selected'), item)
- else:
- self.tag_replace('_selected', 'selected')
- def get_selected(self, event):
- selected_items = []
- window_y = int(self.root.geometry().rsplit('+', 1)[-1])
- titlebar_height = self.root.winfo_rooty() - window_y
- sw = self.select_window
- start = sw.winfo_rooty() - titlebar_height - window_y
- end = start + sw.winfo_height()
- while start < end:
- start += 1
- node = self.identify('item', event.x, start)
- if not node or node in selected_items:
- continue
- selected_items.append(node)
- return sorted(selected_items)
- def set_selected(self, event):
- def set_row_colors():
- items = self.selected_items = self.get_selected(event)
- for item in items:
- if self.tag_has('selected', item):
- if item == self.root.anchor_item:
- continue
- if self.tag_has('selected_odd', item):
- self.tag_replace('selected_odd', 'odd', item)
- self.tag_add('_selected', item)
- elif self.tag_has('selected_even', item):
- self.tag_replace('selected_even', 'even', item)
- self.tag_add('_selected', item)
- elif self.tag_has('odd', item):
- self.tag_replace('odd', 'selected_odd', item)
- self.tag_add('_selected', item)
- elif self.tag_has('even', item):
- self.tag_replace('even', 'selected_even', item)
- self.tag_add('_selected', item)
- for item in self.tag_has('_selected'):
- if item not in items:
- self.tag_remove('_selected', item)
- if self.tag_has('odd', item):
- self.tag_replace('odd', 'selected_odd', item)
- elif self.tag_has('even', item):
- self.tag_replace('even', 'selected_even', item)
- elif self.tag_has('selected_odd', item):
- self.tag_replace('selected_odd', 'odd', item)
- elif self.tag_has('selected_even', item):
- self.tag_replace('selected_even', 'even', item)
- font = tkfont.nametofont('TkTextFont')
- linespace = font.metrics('linespace') + 5
- root_x = self.root.winfo_rootx()
- if event.x < self.root.anchor_x:
- width = self.root.anchor_x - event.x
- coord_x = root_x + event.x
- else:
- width = event.x - self.root.anchor_x
- coord_x = root_x + self.root.anchor_x
- if coord_x+width > root_x+self.winfo_width():
- width -= (coord_x+width)-(root_x+self.winfo_width())
- elif self.winfo_pointerx() < root_x:
- width -= (root_x - self.winfo_pointerx())
- coord_x = root_x
- root_y = self.winfo_rooty()
- if event.y < self.root.anchor_y:
- height = self.root.anchor_y - event.y
- coord_y = root_y + event.y
- else:
- height = event.y - self.root.anchor_y
- coord_y = root_y + self.root.anchor_y
- if coord_y+height > root_y+self.winfo_height():
- height -= (coord_y+height)-(root_y+self.winfo_height())
- elif self.winfo_pointery() < root_y + linespace:
- height -= (root_y - self.winfo_pointery() + linespace)
- coord_y = root_y + linespace
- if height < 0:
- height = self.winfo_rooty() + self.root.anchor_y
- self.select_window.geometry(f'{width}x{height}+{coord_x}+{coord_y}')
- set_row_colors()
- class App(tk.Tk):
- def __init__(self):
- super().__init__()
- def print_selected_items(_):
- print(sorted(tv.tag_has('selected_odd') + tv.tag_has('selected_even')))
- return 'break'
- tv = Treeview(self)
- style = self.style = ttk.Style()
- style.map("Treeview", foreground=tv.fixed_map("foreground"), background=tv.fixed_map("background"))
- tv.heading('#0', text='Name')
- tv.tag_configure('odd', background='#ffffff')
- tv.tag_configure('even', background='#aaaaaa')
- tv.tag_configure('selected_odd', background='#b0eab2')
- tv.tag_configure('selected_even', background='#25a625')
- color_tag = 'odd'
- for idx in range(0, 4):
- # Populating the tree with test data.
- color_tag = 'even' if color_tag == 'odd' else 'odd'
- tv.insert('', idx, f'!{idx}', text=f'Item {idx+1}', open=1, tags=(color_tag,))
- color_tag = 'even' if color_tag == 'odd' else 'odd'
- iid = f'!{idx}_!{0}'
- tv.insert(f'!{idx}', '0', iid, text=f'Menu {idx+1}', open=1, tags=(color_tag,))
- for i in range(0, 5):
- color_tag = 'even' if color_tag == 'odd' else 'odd'
- tv.insert(iid, i, f'{iid}_!{i}', text=f'Sub item {i+1}', tags=(color_tag,))
- color_tag = 'even' if color_tag == 'odd' else 'odd'
- tv.insert(iid, 5, f'{iid}_!{5}', text=f'Another Menu {idx+1}', open=1, tags=(color_tag,))
- iid = f'{iid}_!{5}'
- for i in range(0, 3):
- color_tag = 'even' if color_tag == 'odd' else 'odd'
- tv.insert(iid, i, f'{iid}_!{i}', text=f'Sub item {i+1}', tags=(color_tag,))
- self.title('Treeview Demo')
- self.geometry('275x650+3000+250')
- self.rowconfigure(0, weight=1)
- self.columnconfigure(0, weight=1)
- self.update_idletasks()
- tv.config(selectmode="none")
- tv.grid(sticky='NSEW')
- button = ttk.Button(self, text='Get Selected Items')
- button.grid()
- button.bind('<Button-1>', print_selected_items)
- self.bind('<Escape>', tv.tags_reset)
- self.bind('<Button-1>', tv.button_press)
- self.bind('<ButtonRelease-1>', tv.button_release)
- def main():
- app = App()
- app.mainloop()
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement