Horizontal scrolling is not activated for ttk Treeview widget

I am using the ttk Treeview widget to implement a folder / path select dialog. Everything works as expected, except that my horizontal scrollbar is not activated. No matter how wide the path to the folder goes horizontally, and no matter how narrow the window is, a horizontal slider never appears. Vertical scrolling works fine, though.

I see this as some kind of limitation when you use only one column in the tree structure or just a newbie mistake with setting up and connecting widgets. I would argue on the latter.

An example with a dialog extension showing the full depth of a folder:

full width

The dialog is narrowed down to the point where horizontal scrolling should be activated (but not):

narrowed width

Here is my GUI layout code:

winDirSel = tk.Toplevel() winDirSel.title('Select Test Directory...') tvwDirSel = ttk.Treeview(winDirSel, height=10,padding=3, show='tree') lblTestDir = tk.Label(winDirSel, relief=tk.SUNKEN, justify=tk.LEFT, anchor=tk.W, textvariable=ctrlTestDir,width=80) scbHDirSel = ttk.Scrollbar(winDirSel, orient=tk.HORIZONTAL, command=tvwDirSel.xview) scbVDirSel = ttk.Scrollbar(winDirSel, orient=tk.VERTICAL, command=tvwDirSel.yview) tvwDirSel.configure(xscrollcommand=scbHDirSel.set, yscrollcommand=scbVDirSel.set) lblTestDir.grid(row=0,column=0,sticky=tk.EW) tvwDirSel.grid(row=1,column=0,sticky=tk.NSEW) scbVDirSel.grid(row=1,column=1,sticky=tk.NS) scbHDirSel.grid(row=2,column=0,sticky=tk.EW) winDirSel.rowconfigure(1,weight=1) winDirSel.columnconfigure(0,weight=1) 
+8
tkinter ttk scrollbar treeview
source share
3 answers

Well, after some games with minwidth and stretch , I think I have a better handle. Horizontal scrolling is triggered using an edge-edge that extends from the borders of the window, not the contents of the column. Thus, you can use these options to force the column to be wider and thus force the scroll.

The problem is that you lose the automatic adjustment of the column width according to the width of the tree itself. You either have to distribute it heavily to accommodate any (supposed) probable folder depth, or you live with folder names that are truncated on the right border of the column.

So the bottom line: this is just a limitation of the widget itself. (At least regarding its behavior on my platform, MS Windows.)

+7
source share
 import tkinter as tk import tkinter.ttk as ttk import tkinter.font as tk_font class 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 -= 1 if __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() 
0
source share

Here is what I finally came up with to display TreeView files that are lazily loaded (thanks to this answer ), which is inside PanedWindow (SplitterWindow in wxPython terms) along with Notebook . Scroll bars are automatically displayed / hidden as needed, thanks to this example .

 import os import Tkinter as tk import ttk as ttk from ScrolledText import ScrolledText class 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 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() 
0
source share

All Articles