Horizontal scrolling won't activate for ttk Treeview widget
OK, after some playing with minwidth
and stretch
, I think I have a better handle on it. The horizontal scrolling is triggered by the column-edge going out of the window's bounds, not the content of the column. So you can use these parameters to force the column to be wider and thus force the scrolling.
The problem though is that you then lose the automatic adjustment of the column width to suit the width of the tree itself. You either have to force it very wide to accommodate any (assumed) likely folder depth, or you live with folder names getting truncated at the right boundary of the column.
So bottom line: it's just a limitation of the widget itself. (At least with respect to its behavior on my platform, MS Windows.)
Here's what I finally came up with to display a TreeView
of files which are lazy-loaded (thanks to this answer) which is inside a PanedWindow
(SplitterWindow in wxPython terms) along with a Notebook
. The scrollbars are auto-displayed/hidden as needed, thanks to this example.
import osimport Tkinter as tkimport ttk as ttkfrom ScrolledText import ScrolledTextclass App(object): def __init__(self, master, path): splitter = tk.PanedWindow(master, orient=tk.HORIZONTAL) # left-side frame_left = tk.Frame(splitter) self.tree = ttk.Treeview(frame_left, show='tree') ysb = ttk.Scrollbar(frame_left, orient='vertical', command=self.tree.yview) xsb = ttk.Scrollbar(frame_left, orient='horizontal', command=self.tree.xview) # right-side frame_right = tk.Frame(splitter) nb = ttk.Notebook(frame_right) page1 = ttk.Frame(nb) page2 = ttk.Frame(nb) text = ScrolledText(page2) # overall layout splitter.add(frame_left) splitter.add(frame_right) splitter.pack(fill=tk.BOTH, expand=1) # left-side widget layout self.tree.grid(row=0, column=0, sticky='NSEW') ysb.grid(row=0, column=1, sticky='ns') xsb.grid(row=1, column=0, sticky='ew') # left-side frame's grid config frame_left.columnconfigure(0, weight=1) frame_left.rowconfigure(0, weight=1) # right-side widget layout text.pack(expand=1, fill="both") nb.add(page1, text='One') nb.add(page2, text='Two') nb.pack(expand=1, fill="both") # setup self.tree.configure(yscrollcommand=lambda f, l:self.autoscroll(ysb,f,l), xscrollcommand=lambda f, l:self.autoscroll(xsb,f,l)) # use this line instead of the previous, if you want the scroll bars to always be present, but grey-out when uneeded instead of disappearing #self.tree.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set) self.tree.heading('#0', text='Project tree', anchor='w') self.tree.column("#0",minwidth=1080, stretch=True) # add default tree node abspath = os.path.abspath(path) self.nodes = dict() self.insert_node('', abspath, abspath) self.tree.bind('<<TreeviewOpen>>', self.open_node) def autoscroll(self, sbar, first, last): """Hide and show scrollbar as needed.""" first, last = float(first), float(last) if first <= 0 and last >= 1: sbar.grid_remove() else: sbar.grid() sbar.set(first, last) def insert_node(self, parent, text, abspath): node = self.tree.insert(parent, 'end', text=text, open=False) if os.path.isdir(abspath): self.nodes[node] = abspath self.tree.insert(node, 'end') def open_node(self, event): node = self.tree.focus() abspath = self.nodes.pop(node, None) if abspath: self.tree.delete(self.tree.get_children(node)) for p in os.listdir(abspath): self.insert_node(node, p, os.path.join(abspath, p))if __name__ == '__main__': root = tk.Tk() root.geometry("800x600") app = App(root, path='.') root.mainloop()
import tkinter as tkimport tkinter.ttk as ttkimport tkinter.font as tk_fontclass TreeListBox: def __init__(self, master, root, dict_group): self.master = master self.root = root self.dict_group = dict_group self.level = 0 self.setup_widget_tree() self.build_tree(self.root, '') def setup_widget_tree(self): container_tree = tk.Frame(self.master, width=250, height=300) container_tree.propagate(False) container_tree.pack(side="left", fill='y') self.tree = ttk.Treeview(container_tree, show="tree", selectmode='browse') fr_y = tk.Frame(container_tree) fr_y.pack(side='right', fill='y') tk.Label(fr_y, borderwidth=1, relief='raised', font="Arial 8").pack(side='bottom', fill='x') sb_y = tk.Scrollbar(fr_y, orient="vertical", command=self.tree.yview) sb_y.pack(expand='yes', fill='y') fr_x = tk.Frame(container_tree) fr_x.pack(side='bottom', fill='x') sb_x = tk.Scrollbar(fr_x, orient="horizontal", command=self.tree.xview) sb_x.pack(expand='yes', fill='x') self.tree.configure(yscrollcommand=sb_y.set, xscrollcommand=sb_x.set) self.tree.pack(fill='both', expand='yes') def build_tree(self, parent, id_stroki): self.level += 1 id = self.tree.insert(id_stroki, 'end', text=parent) # ----------------- col_w = tk_font.Font().measure(parent) if col_w > 1000: col_w -= 400 elif col_w > 500: col_w -= 200 elif col_w > 300: col_w -= 100 col_w = col_w + 25 * self.level if col_w > self.tree.column('#0', 'width'): self.tree.column('#0', width=col_w) # ----------------- for element in sorted(self.dict_group[parent]): self.build_tree(element, id) self.level -= 1if __name__ == '__main__': dict_group = {'Nomenclature': ['ABC1', 'ABC2'], 'ABC1': ['ABC3', 'ABC4'], 'ABC2': ['ABC5'], 'ABC3': ['ABC______________________________________6'], 'ABC4': ['ABC--------------------------------------8'], 'ABC5': ['ABC######################################9'], 'ABC______________________________________6': [], 'ABC--------------------------------------8': [], 'ABC######################################9': [] } root = tk.Tk() myTest = TreeListBox(root, 'Nomenclature', dict_group) root.mainloop()