Advertisement
Uno-Dan

Treeview

Apr 17th, 2020
506
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 28.87 KB | None | 0 0
  1. class Treeview(Base, ttk.Treeview):
  2.     def __init__(self, parent, key, **kwargs):
  3.         Base.__init__(self, parent, key, **kwargs)
  4.         self.columns = self.setting_get('columns', {})
  5.         self.headings = self.setting_get('headings', {})
  6.         ttk.Treeview.__init__(self, parent, columns=tuple(range(1, len(self.columns))), **self.options_get())
  7.  
  8.         self.undo_data = {}
  9.         self.bindings = {}
  10.         self.column_sort = {}
  11.  
  12.         self.levels = \
  13.             self.x_root = \
  14.             self.y_root = \
  15.             self.largest = \
  16.             self.xscroll = \
  17.             self.yscroll = \
  18.             self.pointer_x = \
  19.             self.pointer_y = \
  20.             self.direction = \
  21.             self.detached = \
  22.             self._selection_colors = \
  23.             self.root_selection = None
  24.  
  25.         self.init()
  26.  
  27.     def init(self):
  28.         def set_columns():
  29.             for index, column in enumerate(self.columns):
  30.                 self.column(f'#{index}', **dict(column))
  31.  
  32.         def set_headings():
  33.             for index, heading in enumerate(self.headings):
  34.                 self.column_sort[f'#{index}'] = True
  35.                 self.heading(f'#{index}', **dict(heading))
  36.  
  37.         def set_scrolling():
  38.             scroll = self.setting_get('scroll')
  39.             if scroll:
  40.                 self.scrollbars_add(*scroll)
  41.  
  42.         set_columns()
  43.         set_headings()
  44.         set_scrolling()
  45.  
  46.         self.bindings_set()
  47.         self.populate()
  48.         self.render()
  49.  
  50.     def level(self, item):
  51.         level = 1
  52.         while self.parent_get(item):
  53.             level += 1
  54.             item = self.parent_get(item)
  55.         return level
  56.  
  57.     def has_name(self, parent, name):
  58.         for iid in self.get_children(parent):
  59.             if name == self.item(iid)['text']:
  60.                 return True
  61.         return False
  62.  
  63.     def parent_get(self, item):
  64.         return super(Treeview, self).parent(item)
  65.  
  66.     def tags_add(self, item, tags):
  67.         if isinstance(tags, str):
  68.             tags = (tags,)
  69.  
  70.         _tags = list(self.item(item)['tags'])
  71.         _tags = [] if not _tags else _tags
  72.  
  73.         for tag in tags:
  74.             if tag not in _tags:
  75.                 _tags.append(tag)
  76.         self.item(item, tags=_tags)
  77.  
  78.     def tags_remove(self, item, tags):
  79.         if isinstance(tags, str):
  80.             tags = (tags,)
  81.  
  82.         _tags = list(self.item(item)['tags'])
  83.         for tag in tags:
  84.             if tag in _tags:
  85.                 _tags.pop(_tags.index(tag))
  86.         self.item(item, tags=_tags)
  87.  
  88.     def tags_refresh(self):
  89.         def do_refresh(children, _tag):
  90.             for child in children:
  91.                 item = self.item(child)
  92.  
  93.                 _tag = 'even' if _tag == 'odd' else 'odd'
  94.                 _tags = list(self.item(child)['tags'])
  95.                 if f'selected_{_tag}' not in item['tags']:
  96.                     _tags.append(_tag)
  97.                 self.item(child, tags=_tags)
  98.  
  99.                 if int(item['open']):
  100.                     _tag = do_refresh(self.get_children(child), _tag)
  101.  
  102.             self.tags_set_colors()
  103.             return _tag
  104.  
  105.         self.tags_remove_all('odd', 'even', 'cut_odd', 'cut_even', 'copy_odd', 'copy_even')
  106.         do_refresh(self.get_children(), 'odd')
  107.  
  108.     def tags_remove_all(self, *tags):
  109.  
  110.         def do_remove(node):
  111.             self.tags_remove(node, tags)
  112.             for node in self.get_children(node):
  113.                 do_remove(node)
  114.  
  115.         for child in self.get_children():
  116.             do_remove(child)
  117.  
  118.     def tags_replace_all(self, old, new):
  119.         for tag in self.tag_has(old):
  120.             tags = sorted(self.item(tag, 'tags'))
  121.             tags.pop(tags.index(old))
  122.             tags.append(new)
  123.             self.item(tag, tags=tags)
  124.  
  125.     def tags_set_colors(self, mode=None):
  126.         def set_colors(children):
  127.             for child in children:
  128.                 _tags = self.item(child, 'tags')
  129.  
  130.                 if 'odd' in _tags:
  131.                     self.tags_remove(child, 'odd')
  132.                     self.tags_add(child, f'{mode}_odd')
  133.                 elif 'even' in _tags:
  134.                     self.tags_remove(child, 'even')
  135.                     self.tags_add(child, f'{mode}_even')
  136.  
  137.                 _item = self.item(child)
  138.                 nodes = self.get_children(child)
  139.                 if int(_item['open']):
  140.                     set_colors(nodes)
  141.  
  142.         if not mode:
  143.             mode = 'cut' if self.tag_has('cut') else 'copy'
  144.  
  145.         set_colors(sorted(self.tag_has(mode)))
  146.  
  147.     def cut(self, _=None):
  148.         self.copy('cut')
  149.  
  150.     def undo(self):
  151.         for k, value in self.undo_data.items():
  152.             self.content_set(k, **value)
  153.  
  154.         for item in sorted(self.detached):
  155.             parent = '' if '_' not in item else item.rsplit('_', 1)[0]
  156.             index = int(item.rsplit('_', 1).pop().lstrip('!'))
  157.             self.move(item, parent, index)
  158.  
  159.         self.popup.disable_items('undo')
  160.  
  161.     def copy(self, mode='copy'):
  162.         def update_tags(_item):
  163.             tags = list(self.item(_item, 'tags'))
  164.  
  165.             if 'odd' in tags:
  166.                 tag = f'{mode}_odd'
  167.                 self.tags_remove(_item, 'odd')
  168.             else:
  169.                 tag = f'{mode}_even'
  170.                 self.tags_remove(_item, 'even')
  171.  
  172.             self.tags_add(_item, tag)
  173.             if int(self.item(_item)['open']):
  174.                 for node in self.get_children(_item):
  175.                     update_tags(node)
  176.  
  177.         def get_item_list():
  178.             _items = []
  179.             root = selections.pop(0)
  180.             _items.append(root)
  181.             while selections:
  182.                 selection = selections.pop(0)
  183.                 if not selection.startswith(root):
  184.                     root = selection
  185.                     _items.append(root)
  186.             return _items
  187.  
  188.         selections = sorted(self.selection())
  189.         if not selections:
  190.             return
  191.  
  192.         self.tags_remove_all('cut', 'copy', 'selected', 'cut_odd', 'cut_even', 'copy_odd', 'copy_even')
  193.         self.tags_refresh()
  194.  
  195.         items = get_item_list()
  196.         for item in items:
  197.             self.tags_add(item, mode)
  198.             update_tags(item)
  199.  
  200.         self.pointer_x, self.pointer_y = self.winfo_toplevel().winfo_pointerxy()
  201.  
  202.         self.focus(items[-1])
  203.         self.focus_set()
  204.  
  205.     def paste(self):
  206.         def rename(value=''):
  207.             def get_name(_text):
  208.                 _dlg = self.app.dialog_manager.dialog(dlg_key, **{
  209.                     'options': {
  210.                         'value': _text,
  211.                         'message': f'The name ({_text}) already exists.\nPlease enter a different name and try again.',
  212.                         'position': (self.x_root, self.y_root),
  213.                     },
  214.                 })
  215.                 self.x_root, self.y_root = _dlg.results['geometry'].split('+', 1).pop().split('+')
  216.  
  217.                 if _dlg.results.get('cancel', False):
  218.                     return _dlg.results
  219.  
  220.                 while not _dlg.results.get('text'):
  221.                     _dlg = self.app.dialog_manager.dialog(dlg_key, **{
  222.                         'options': {
  223.                             'value': '',
  224.                             'message': 'You have not entered a name.\n'
  225.                                        'Please enter a name and try again or select cancel.',
  226.                             'position': (self.x_root, self.y_root),
  227.                         },
  228.                     })
  229.                     if 'text' not in _dlg.results:
  230.                         return _dlg.results
  231.  
  232.                 return _dlg.results
  233.  
  234.             _selections = self.selection()
  235.             _selection = '' if not _selections else _selections[0]
  236.  
  237.             if not self.x_root or not self.y_root:
  238.                 offset_x = offset_y = 0
  239.                 if _selection:
  240.                     offset_x = self.bbox(_selection)[0]
  241.                     offset_y = self.bbox(_selection)[1]
  242.                 self.x_root, self.y_root = self.winfo_rootx() + offset_x, self.winfo_rooty() + offset_y
  243.  
  244.                 font = tkfont.nametofont('TkTextFont')
  245.  
  246.                 self.x_root = self.x_root + self.column('#0')['width']
  247.                 self.y_root = self.y_root + font.metrics('linespace')
  248.  
  249.             dlg_key = 'rename'
  250.             while True:
  251.                 data = get_name(value)
  252.                 value = data.get('text')
  253.                 if self.has_name(_selection, value):
  254.                     continue
  255.                 elif value or 'text' not in data:
  256.                     break
  257.             return data
  258.  
  259.         def get_items(parent):
  260.             item_names = []
  261.             for c in self.get_children(parent):
  262.                 item_names.append(self.item(c, 'text'))
  263.             return item_names
  264.  
  265.         def add_nodes(parent, _node):
  266.             _item = self.item(_node)
  267.             text = _item['text']
  268.  
  269.             if self.has_name(parent, text):
  270.                 _results = rename(text)
  271.                 _text = _results.get('text', '')
  272.  
  273.                 while _results.get('text', '') in get_items(parent):
  274.                     _results = rename(text)
  275.  
  276.                 if 'text' not in _results:
  277.                     return 'cancel'
  278.  
  279.                 text = _results['text']
  280.  
  281.             _iid = self.append(parent, text, _item['values'], open=_item['open'])
  282.  
  283.             if 'cut' in _item['tags']:
  284.                 self.item(_iid, tags=(self.item(_iid, 'tags'), 'copy'))
  285.  
  286.             for child in self.get_children(_node):
  287.                 add_nodes(_iid, child)
  288.                 self.tags_add(_iid, 'paste')
  289.  
  290.         mode = 'cut' if self.tag_has('cut') else 'copy'
  291.         items = sorted(self.tag_has(mode))
  292.         if mode == 'copy' and not items:
  293.             if self.tag_has('selected'):
  294.                 self.tags_replace_all('selected', 'copy')
  295.             items = sorted(self.tag_has('copy'))
  296.  
  297.         selections = self.selection()
  298.         if not selections:
  299.             selections = ('',)
  300.         for selection in selections:
  301.             for item in items:
  302.                 if add_nodes(selection if '_' in item else '', item) == 'cancel':
  303.                     return
  304.  
  305.         if self.tag_has('paste'):
  306.             self.tags_set_colors('paste')
  307.  
  308.         if mode == 'cut':
  309.             self.delete(*items)
  310.  
  311.         def reset_tags():
  312.             for _tag in ('odd', 'even'):
  313.                 for _item in self.tag_has(f'copy_{_tag}'):
  314.                     self.tags_add(_item, _tag)
  315.                     self.tags_remove(_item, f'copy_{_tag}')
  316.             self.tags_refresh()
  317.             self.tags_remove_all('paste', 'paste_odd', 'paste_even')
  318.         self.after(1300, reset_tags)
  319.  
  320.         if self.tag_has('copy'):
  321.             for node in self.tag_has('copy'):
  322.                 self.tags_remove(node, 'copy')
  323.                 self.tags_add(node, 'selected')
  324.  
  325.     def rename(self):
  326.         def after_do_ok(dialog):
  327.             def text_get(_text):
  328.                 while not _text:
  329.                     _data = entry_value_get()
  330.                     if 'text' not in _data:
  331.                         return
  332.                     _text = _data['text']
  333.                 return _text
  334.  
  335.             def item_name_get():
  336.                 dlg = self.app.dialog_manager.dialog(dlg_key, **{
  337.                     'options': {
  338.                         'value': text,
  339.                         'message': f'The name ({text}) already exists.\nPlease enter a different name and try again.',
  340.                         'position': (self.x_root, self.y_root),
  341.                     },
  342.                 })
  343.                 self.x_root, self.y_root = dlg.results['geometry'].split('+', 1).pop().split('+')
  344.                 return dlg.results
  345.  
  346.             def entry_value_get():
  347.                 dlg = self.app.dialog_manager.dialog(dlg_key, **{
  348.                     'options': {
  349.                         'value': '',
  350.                         'message': 'You have not entered a name.\nPlease enter a name and try again or select cancel.',
  351.                         'position': (self.x_root, self.y_root),
  352.                     },
  353.                 })
  354.                 self.x_root, self.y_root = dlg.results['geometry'].split('+', 1).pop().split('+')
  355.                 return dlg.results
  356.  
  357.             text = dialog.results.get('text', '').strip(' ')
  358.             target = tv.selection()[0]
  359.  
  360.             text = text_get(text)
  361.  
  362.             while tv.has_name(tv.parent_get(target), text):
  363.                 data = item_name_get()
  364.                 if 'text' not in data:
  365.                     return
  366.                 text = text_get(data['text'])
  367.  
  368.             if text:
  369.                 tv.item(target, text=text)
  370.  
  371.         def before_exit(dialog):
  372.             results = dialog.results
  373.             self.x_root, self.y_root = results['geometry'].split('+', 1).pop().split('+')
  374.  
  375.         tv = self
  376.         target_item = tv.item(tv.selection()[0])
  377.  
  378.         dlg_key = 'rename'
  379.         self.app.dialog_manager.dialog(dlg_key, **{
  380.             'options': {
  381.                 'value': target_item['text'],
  382.                 'message': f'Enter a new name for the {target_item["values"][0].lower()}.',
  383.                 'position': (self.x_root, self.y_root),
  384.             },
  385.             'callbacks': {
  386.                 'after_do_ok': after_do_ok,
  387.                 'before_exit': before_exit,
  388.             },
  389.         })
  390.  
  391.     def insert(self, parent='', index=tk.END, **kwargs):
  392.         def get_iid(_parent=''):
  393.             nodes = self.get_children(_parent)
  394.  
  395.             idx = len(nodes)
  396.             _iid = f'{_parent}_!{idx}'.strip('_')
  397.  
  398.             while self.exists(_iid):
  399.                 idx += 1
  400.                 _iid = f'{_parent}_!{idx}'.strip('_')
  401.             return _iid
  402.  
  403.         kwargs.pop('iid', None)
  404.         kwargs.pop('image', None)
  405.         unique = kwargs.pop('unique', True)
  406.  
  407.         try:
  408.             if unique:
  409.                 for child in self.get_children(parent):
  410.                     if not kwargs['text'] or kwargs['text'] == self.item(child)['text']:
  411.                         raise NameError()
  412.  
  413.             iid = get_iid(parent)
  414.             item = super(Treeview, self).insert(parent, index, iid=iid, **kwargs)
  415.  
  416.             self.content_set(iid, **kwargs)
  417.             self.tags_refresh()
  418.             return item
  419.  
  420.         except NameError:
  421.             return False
  422.  
  423.     def append(self, parent, text, values, **kwargs):
  424.         kwargs['text'] = text
  425.         kwargs['values'] = values
  426.  
  427.         return self.insert(parent, **kwargs)
  428.  
  429.     def remove(self, iid=None):
  430.         def do_remove(uri):
  431.             def walk(_content):
  432.                 data = _content.copy()
  433.                 for key in data.keys():
  434.                     if key == target:
  435.                         self.undo_data.update({key: _content[key]})
  436.                         del _content[key]
  437.                         break
  438.                     if isinstance(data[key], dict):
  439.                         walk(_content[key])
  440.  
  441.             target = uri.rsplit('/').pop()
  442.  
  443.             content = self.setting_get('content', {})
  444.             walk(content)
  445.             self.setting_set('content', content)
  446.  
  447.         selections = self.selection()
  448.         if not iid and not selections:
  449.             return
  450.         elif iid:
  451.             selections = [iid]
  452.  
  453.         for selection in sorted(selections):
  454.             do_remove(tvi2uri(selection))
  455.  
  456.         selected = selections[0]
  457.         _prev = self.prev(selected)
  458.         _next = self.next(selected)
  459.         _parent = super(Treeview, self).parent(selected)
  460.  
  461.         self.detached = []
  462.         for selection in sorted(selections, reverse=True):
  463.             self.detached.append(selection)
  464.             self.detach(selection)
  465.  
  466.         if _next and self.exists(_next):
  467.             self.selection_set(_next)
  468.             self.focus(_next)
  469.         elif _prev and self.exists(_prev) and not self.item(_prev)['open']:
  470.             self.selection_set(_prev)
  471.             self.focus(_prev)
  472.         else:
  473.             if _parent and not _prev:
  474.                 node = _parent
  475.             elif self.get_children(_prev):
  476.                 node = list(self.get_children(_prev)).pop()
  477.             else:
  478.                 return
  479.  
  480.             if node:
  481.                 self.selection_set(node)
  482.  
  483.         self.refresh()
  484.         if self.undo_data:
  485.             self.popup.enable_items('undo')
  486.  
  487.         self.tags_refresh()
  488.  
  489.         for tag in ('odd', 'even'):
  490.             for _item in self.tag_has(f'copy_{tag}'):
  491.                 self.tags_add(_item, tag)
  492.                 self.tags_remove(_item, f'copy_{tag}')
  493.  
  494.     def escape(self, _=None):
  495.         self.tags_remove_all('cut', 'cut_odd', 'cut_even', 'copy_odd', 'copy_even')
  496.         self.selection_set('')
  497.         self.tags_refresh()
  498.         self.tags_replace_all('copy', 'selected')
  499.  
  500.     def select(self, _):
  501.         items = sorted(self.selection(), key=len)
  502.         if items:
  503.             root = items.pop(0)
  504.             if '_' not in root:
  505.                 for item in items.copy():
  506.                     self.selection_remove(root)
  507.                     if item.startswith(root):
  508.                         items.pop(items.index(item))
  509.  
  510.     def expand(self, _):
  511.         def set_row_colors():
  512.             mode = 'cut' if self.tag_has('cut') else 'copy'
  513.             tags = sorted(self.tag_has(mode))
  514.             selections = sorted(self.selection())
  515.             self.tags_refresh()
  516.  
  517.             if not tags or not selections:
  518.                 return
  519.  
  520.             tag = tags[0]
  521.             selection = selections[0]
  522.  
  523.             if not selection.startswith(tag):
  524.                 for tag in ('odd', 'even'):
  525.                     for _item in self.tag_has(f'copy_{tag}'):
  526.                         self.tags_add(_item, tag)
  527.                         self.tags_remove(_item, f'copy_{tag}')
  528.         self.after(0, set_row_colors)
  529.  
  530.     def collapse(self, _):
  531.         def set_row_colors():
  532.             mode = 'cut' if self.tag_has('cut') else 'copy'
  533.             tags = sorted(self.tag_has(mode))
  534.             selections = sorted(self.selection())
  535.             self.tags_refresh()
  536.  
  537.             if not tags or not selections:
  538.                 return
  539.  
  540.             tag = tags[0]
  541.             selection = selections[0]
  542.  
  543.             if not selection.startswith(tag):
  544.                 for tag in ('odd', 'even'):
  545.                     for _item in self.tag_has(f'copy_{tag}'):
  546.                         self.tags_add(_item, tag)
  547.                         self.tags_remove(_item, f'copy_{tag}')
  548.  
  549.         self.after(0, set_row_colors)
  550.  
  551.     def refresh(self):
  552.         font = tkfont.nametofont('TkTextFont')
  553.         self.style.configure('Treeview', rowheight=font.metrics('linespace') + 5)
  554.         self.style.configure("Treeview.Heading", font=tkfont.nametofont('TkHeadingFont'))
  555.         self.column_widths_set()
  556.  
  557.     def populate(self):
  558.         def walk(_parent, _data, _content):
  559.             _idx = 0
  560.  
  561.             for _, _value in sorted(_data.items()):
  562.                 if isinstance(_value, dict):
  563.                     _iid = f'{_parent}_!{_idx}'
  564.                     _idx += 1
  565.                     _kwargs = {
  566.                         'text': _value.get('text', ''),
  567.                         'image': _value.get('image', None),
  568.                         'values': _value.get('values', []),
  569.                         'open': _value.get('open', 0),
  570.                         'tags': _value.get('tags', []),
  571.                     }
  572.                     _content[_iid] = _kwargs
  573.                     self.insert(_parent, tk.END, iid=_iid, **_kwargs)
  574.                     walk(_iid, _value, _content[_iid])
  575.  
  576.         idx = 0
  577.         parent = ''
  578.         content = {}
  579.  
  580.         for _, value in sorted(self.setting_get('content', {}).items()):
  581.             if isinstance(value, dict):
  582.                 iid = f'{parent}_!{idx}'.strip("_")
  583.                 idx += 1
  584.                 kwargs = {
  585.                     'text': value.get('text', ''),
  586.                     'image': value.get('image', None),
  587.                     'values': value.get('values', []),
  588.                     'open': value.get('open', 0),
  589.                     'tags': value.get('tags', []),
  590.                 }
  591.                 self.insert(parent, tk.END, iid=iid, **kwargs)
  592.                 content[iid] = kwargs
  593.                 walk(iid, value, content[iid])
  594.  
  595.         self.setting_set('content', content)
  596.         self.refresh()
  597.         self.tags_refresh()
  598.  
  599.     def content_set(self, iid, **kwargs):
  600.         def do_set(data):
  601.             for key, value in data.items():
  602.                 if not isinstance(value, dict):
  603.                     continue
  604.  
  605.                 elif key == iid:
  606.                     value.update(kwargs)
  607.                     return 'break'
  608.  
  609.                 elif key == parent and iid not in value:
  610.                     value[iid] = kwargs
  611.                     return 'break'
  612.  
  613.                 if do_set(value) == 'break':
  614.                     break
  615.  
  616.         content = self.setting_get('content', {})
  617.         parent = '' if '_' not in iid else iid.rsplit('_', 1)[0]
  618.  
  619.         if '_' not in iid and iid not in content:
  620.             content[iid] = kwargs
  621.         else:
  622.             do_set(content)
  623.  
  624.     def content_get(self, iid=None):
  625.         def do_get(data):
  626.             for _key, _value in data.items():
  627.                 if not isinstance(_value, dict):
  628.                     continue
  629.                 if _key == iid:
  630.                     return _value
  631.                 do_get(_value)
  632.             return None
  633.  
  634.         content = self.setting_get('content', {})
  635.         if not iid or not content:
  636.             return content
  637.         elif iid in content:
  638.             _data = {}
  639.             for key, value in content[iid].items():
  640.                 if not isinstance(value, dict):
  641.                     _data[key] = value
  642.             return _data
  643.         return do_get(content)
  644.  
  645.     def bindings_set(self):
  646.  
  647.         def shift_up(_):
  648.             cur_item = self.focus()
  649.             get_parent = super(Treeview, self).parent
  650.  
  651.             if not get_parent(cur_item) and not self.index(cur_item):
  652.                 return 'break'
  653.  
  654.             if not self.direction:
  655.                 self.direction = 'up'
  656.  
  657.             def prev_item(node):
  658.                 _prev = self.prev(node)
  659.                 if not _prev:
  660.                     _prev = get_parent(node)
  661.                 elif self.get_children(_prev) and self.item(_prev)['open']:
  662.                     def walk(_node):
  663.                         if self.get_children(_node) and self.item(_node)['open']:
  664.                             return walk(list(self.get_children(_node)).pop())
  665.                         return _node
  666.                     _prev = walk(list(self.get_children(_prev)).pop())
  667.  
  668.                 return _prev
  669.  
  670.             item = prev_item(cur_item)
  671.             if self.direction == 'up':
  672.                 if item:
  673.                     self.selection_add(item)
  674.                     self.focus(item)
  675.                 else:
  676.                     _parent = get_parent(item)
  677.                     if not self.item(_parent)['open']:
  678.                         item = _parent
  679.             else:
  680.                 self.selection_remove(cur_item)
  681.                 self.focus(item)
  682.  
  683.             if item == self.root_selection:
  684.                 self.direction = None
  685.  
  686.             return 'break'
  687.  
  688.         def shift_down(_):
  689.             cur_item = self.focus()
  690.  
  691.             if not self.direction:
  692.                 self.direction = 'down'
  693.  
  694.             get_parent = super(Treeview, self).parent
  695.  
  696.             def next_item(node):
  697.                 _next = self.next(node)
  698.  
  699.                 if self.get_children(node) and self.item(node)['open']:
  700.                     _next = self.get_children(node)[0]
  701.                 elif not _next:
  702.                     def walk(_node):
  703.                         _parent = get_parent(_node)
  704.                         _item = self.next(_parent)
  705.                         if not _item and get_parent(_parent):
  706.                             return walk(get_parent(_parent))
  707.                         return _item
  708.  
  709.                     if get_parent(cur_item):
  710.                         _next = walk(cur_item)
  711.  
  712.                 return _next
  713.  
  714.             item = next_item(cur_item)
  715.  
  716.             if not item:
  717.                 return
  718.  
  719.             if self.direction == 'down':
  720.                 self.selection_add(item)
  721.                 self.focus(item)
  722.             else:
  723.                 self.selection_remove(cur_item)
  724.                 self.focus(cur_item)
  725.  
  726.             if item == self.root_selection and item != self.focus():
  727.                 self.direction = None
  728.  
  729.             self.focus(item)
  730.  
  731.             return 'break'
  732.  
  733.         def control_a(_):
  734.             def walk(children):
  735.                 for child in children:
  736.                     self.selection_add(child)
  737.                     _children = self.get_children(child)
  738.                     if _children:
  739.                         walk(_children)
  740.  
  741.             walk(self.get_children(self.focus()))
  742.             self.selection_add(self.focus())
  743.  
  744.         def control_x(_):
  745.             self.copy()
  746.             self.remove()
  747.  
  748.         def control_c(_):
  749.             self.copy()
  750.  
  751.         def key_press(event):
  752.             tree = event.widget
  753.             if 'Shift' in event.keysym:
  754.                 if tree.selection() and len(tree.selection()) <= 1:
  755.                     self.root_selection = tree.focus()
  756.  
  757.         bindings = {
  758.             '<Key>': key_press,
  759.             '<Control-a>': control_a,
  760.             '<Control-x>': control_x,
  761.             '<Control-c>': control_c,
  762.             '<Shift-Up>': shift_up,
  763.             '<Shift-Down>': shift_down,
  764.             '<<TreeviewSelect>>': self.select,
  765.             '<<TreeviewOpen>>': self.expand,
  766.             '<<TreeviewClose>>': self.collapse,
  767.             '<Double-Button-1>': self.column_view_all,
  768.         }
  769.         for command, callback in bindings.items():
  770.             self.bindings[command] = self.bind(command, callback)
  771.  
  772.     def scrollbars_add(self, xscroll, yscroll):
  773.         if not xscroll and not yscroll:
  774.             return
  775.  
  776.         self.xscroll, self.yscroll = xscroll, yscroll
  777.  
  778.         if xscroll:
  779.             sb = self.parent.add_element('scrollbar1', **scrollbar1)
  780.             sb.parent = self
  781.             sb.configure(command=self.xview)
  782.             self.configure(xscrollcommand=sb.set_scroll)
  783.  
  784.         if yscroll:
  785.             sb = self.parent.add_element('scrollbar0', **scrollbar0)
  786.             sb.parent = self
  787.             sb.configure(command=self.yview)
  788.             self.configure(yscrollcommand=sb.set_scroll)
  789.  
  790.     def scrollbars_scroll(self, event):
  791.         wdg = event.widget
  792.  
  793.         options = wdg.options_get()
  794.         if wdg.type == 'Scrollbar' and options.get('orient', tk.HORIZONTAL) == tk.HORIZONTAL:
  795.             event.state = (event.state | 1)
  796.  
  797.         c_width, c_height = self.winfo_width(), self.winfo_height()
  798.  
  799.         autosave_state = self.setting_get('autosave_state', True)
  800.  
  801.         if event.state & 0x1 and self.xscroll:
  802.             if event.num == 4:
  803.                 if c_width <= self.winfo_reqwidth():
  804.                     self.xview_scroll(-1, tk.UNITS)
  805.             elif event.num == 5:
  806.                 self.xview_scroll(1, tk.UNITS)
  807.  
  808.             if autosave_state:
  809.                 self.setting_set('xview', self.xview())
  810.  
  811.         elif self.yscroll:
  812.             if event.num == 4:
  813.                 if c_height <= self.winfo_reqheight():
  814.                     self.yview_scroll(-1, tk.UNITS)
  815.             elif event.num == 5:
  816.                 self.yview_scroll(1, tk.UNITS)
  817.  
  818.             if autosave_state:
  819.                 self.setting_set('yview', self.yview())
  820.  
  821.     def column_widths_set(self, _=None):
  822.         column_widths = self.setting_get('column_widths')
  823.         if column_widths:
  824.             for idx in range(len(self.cget('columns')) + 1):
  825.                 self.column(f'#{idx}', width=column_widths[idx])
  826.  
  827.     def column_view_all(self, _=None):
  828.         def walk(_children):
  829.             _largest = 0
  830.             for child in _children:
  831.  
  832.                 _length = font.measure(self.item(child, 'text')) + 15 * self.level(child)
  833.  
  834.                 if _length > _largest:
  835.                     _largest = _length
  836.                 _children = self.get_children(child)
  837.                 if _children:
  838.                     _length = walk(_children)
  839.                     if _length > _largest:
  840.                         _largest = _length
  841.  
  842.             return _largest
  843.  
  844.         largest = 0
  845.         font = tkfont.nametofont('TkTextFont')
  846.  
  847.         for c in self.get_children():
  848.             length = font.measure(self.item(c, 'text'))+15
  849.             largest = length if length > largest else largest
  850.             children = self.get_children(c)
  851.             if children:
  852.                 length = walk(children)
  853.                 if length > largest:
  854.                     largest = length
  855.  
  856.         column_widths = self.setting_get('column_widths')
  857.         self.column('#0', width=largest+7)
  858.         column_widths[0] = largest
  859.         self.setting_set('column_widths', column_widths)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement