Problem
How to create a button bind class in ttk.Treeview?
Example Code
When programming the main class, it defines the TreeView and then creates another class that defines bindings and events. It is not possible to create these bindings that are in another class, just the same. As the following example:
import Tkinter
import ttk
class clique_bind(ttk.Treeview):
def __init__(self, *args, **kwargs):
ttk.Treeview.__init__(self, *args, **kwargs)
print self.widgetName
# create the entry on init but does no show it
self.bind("<Key>", self._qual_tecla)
def _qual_tecla(self, event):
print("Tecla: " + event.keysym)
class principal(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
tree = ttk.Treeview()
tree["columns"] = ("one", "two")
tree.column("one", width=100)
tree.column("two", width=100)
tree.heading("one", text="coulmn A")
tree.heading("two", text="column B")
tree.insert("", 0, text="Line 1", values=("1A", "1b"))
id2 = tree.insert("", 1, "dir2", text="Dir 2")
tree.insert(id2, "end", "dir 2", text="sub dir 2", values=("2A", "2B"))
tree.insert("", 3, "dir3", text="Dir 3")
tree.insert("dir3", 3, text=" sub dir 3", values=("3A", " 3B"))
tree.pack()
tree.bind('<Button-3>', self._teste_direito)
clique_bind(tree)
def _teste_direito(self, event):
print("Direito")
if __name__ == "__main__":
App = principal(None)
App.mainloop()
When you bind the right-click on the main class, you can check print("Direito")
. But when creating class clique_bind
, where you capture the keyboard buttons in another Class is not possible.
Complete Code
The complete code of what is being programmed to better understand the problem.
Where the code to list directories used is based in the tkinter and search in treeview in this answer . Some changes have been made.
"""A directory browser using Ttk Treeview.
Based on the demo found in Tk 8.5 library/demos/browse
https://svn.python.org/projects/stackless/trunk/Demo/tkinter/ttk/dirbrowser.py
Search based on: https://stackoverflow.com/a/17271593/7690982
"""
import Tkinter
import ttk
import os
import glob
class SearchableTreeview(ttk.Treeview):
def __init__(self, *args, **kwargs):
ttk.Treeview.__init__(self, *args, **kwargs)
# create the entry on init but does no show it
self._toSearch = Tkinter.StringVar()
self.focus()
self.entry = Tkinter.Entry(self, textvariable=self._toSearch)
self.bind("<Key>", self._keyOnTree)
self._toSearch.trace_variable("w", self._search)
self.entry.bind("<Return>", self._hideEntry)
self.entry.bind("<Escape>", self._hideEntry)
print("init")
def _keyOnTree(self, event):
print("keyontree")
self.entry.place(relx=1, anchor=Tkinter.NE)
if event.char.isalpha():
self.entry.insert(Tkinter.END, event.char)
self.entry.focus_set()
def _hideEntry(self, event):
print("hideentry")
self.entry.delete(0, Tkinter.END)
self.entry.place_forget()
self.focus_set()
def _search(self, *args):
print("search")
pattern = self._toSearch.get()
#avoid search on empty string
if len(pattern) > 0:
self.search(pattern)
def search(self, pattern, item=''):
children = self.get_children(item)
for child in children:
text = self.item(child, 'text')
if text.lower().startswith(pattern.lower()):
self.selection_set(child)
self.see(child)
return True
else:
res = self.search(pattern, child)
if res:
return True
class ListagemDir(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.DirTree()
def DirTree(self):
vsb = ttk.Scrollbar(orient="vertical")
hsb = ttk.Scrollbar(orient="horizontal")
tree = ttk.Treeview(columns=("fullpath", "type", "size"),
displaycolumns="size", yscrollcommand=lambda f, l: self.autoscroll(vsb, f, l),
xscrollcommand=lambda f, l: self.autoscroll(hsb, f, l))
vsb['command'] = tree.yview
hsb['command'] = tree.xview
tree.heading("#0", text="Directory Structure", anchor='w')
tree.heading("size", text="File Size", anchor='w')
tree.column("size", stretch=0, width=100)
self.populate_roots(tree)
tree.bind('<<TreeviewOpen>>', self.update_tree)
tree.bind('<Double-Button-1>', self.change_dir)
# Arrange the tree and its scrollbars in the toplevel
tree.grid(column=0, row=0, sticky='nswe')
vsb.grid(column=1, row=0, sticky='ns')
hsb.grid(column=0, row=1, sticky='ew')
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
tree.bind("<Button-3>", self._press3)
searchable = SearchableTreeview(tree)
def populate_tree(self, tree, node):
if tree.set(node, "type") != 'directory':
return
path = tree.set(node, "fullpath")
tree.delete(*tree.get_children(node))
parent = tree.parent(node)
special_dirs = [] if parent else glob.glob('.') + glob.glob('..')
for p in special_dirs + os.listdir(path):
ptype = None
p = os.path.join(path, p).replace('\', '/')
if os.path.isdir(p): ptype = "directory"
elif os.path.isfile(p): ptype = "file"
fname = os.path.split(p)[1]
id = tree.insert(node, "end", text=fname, values=[p, ptype])
if ptype == 'directory':
if fname not in ('.', '..'):
tree.insert(id, 0, text="dummy")
tree.item(id, text=fname)
elif ptype == 'file':
size = os.stat(p).st_size
tree.set(id, "size", "%d bytes" % size)
def populate_roots(self, tree):
dir = os.path.abspath('.').replace('\', '/')
node = tree.insert('', 'end', text=dir, values=[dir, "directory"])
self.populate_tree(tree, node)
def update_tree(self, event):
tree = event.widget
self.populate_tree(tree, tree.focus())
def change_dir(self, event):
tree = event.widget
node = tree.focus()
if tree.parent(node):
path = os.path.abspath(tree.set(node, "fullpath"))
if os.path.isdir(path):
os.chdir(path)
tree.delete(tree.get_children(''))
self.populate_roots(tree)
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 _press3(self, event):
print("Tipo de Evento: " + event.keysym)
if __name__ == "__main__":
App = ListagemDir(None)
App.mainloop()
Attempt
It was possible to perform the complete code in the same class, but I'm not getting into different classes. It may be some class fundamentals error or even Tkinter syntax.
"""A directory browser using Ttk Treeview.
Based on the demo found in Tk 8.5 library/demos/browse
https://svn.python.org/projects/stackless/trunk/Demo/tkinter/ttk/dirbrowser.py
Search based on: https://stackoverflow.com/a/17271593/7690982
"""
import os
import glob
import Tkinter
import ttk
class ListagemDir(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.DirTree()
def DirTree(self):
vsb = ttk.Scrollbar(orient="vertical")
hsb = ttk.Scrollbar(orient="horizontal")
tree = ttk.Treeview(columns=("fullpath", "type", "size"),
displaycolumns="size", yscrollcommand=lambda f, l: self.autoscroll(vsb, f, l),
xscrollcommand=lambda f, l: self.autoscroll(hsb, f, l))
vsb['command'] = tree.yview
hsb['command'] = tree.xview
tree.heading("#0", text="Directory Structure", anchor='w')
tree.heading("size", text="File Size", anchor='w')
tree.column("size", stretch=0, width=100)
self.populate_roots(tree)
tree.bind('<<TreeviewOpen>>', self.update_tree)
tree.bind('<Double-Button-1>', self.change_dir)
# Arrange the tree and its scrollbars in the toplevel
tree.grid(column=0, row=0, sticky='nswe')
vsb.grid(column=1, row=0, sticky='ns')
hsb.grid(column=0, row=1, sticky='ew')
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
tree.bind("<Button-3>", self._press3)
self._toSearch = Tkinter.StringVar()
tree.focus()
self.entry = Tkinter.Entry(tree, textvariable = self._toSearch)
tree.bind("<Key>", self._keyOnTree)
self._toSearch.trace_variable("w", self._search)
self.entry.bind("<Return>", self._hideEntry)
self.entry.bind("<Escape>", self._hideEntry)
print("init")
def _keyOnTree(self, event):
print("keyontree")
self.entry.place(relx=1, anchor=Tkinter.NE)
if event.char.isalpha():
self.entry.insert(Tkinter.END, event.char)
self.entry.focus_set()
def _hideEntry(self, event):
print("hideentry")
self.entry.delete(0, Tkinter.END)
self.entry.place_forget()
self.focus_set()
def _search(self, *args):
print("search")
pattern = self._toSearch.get()
#avoid search on empty string
if len(pattern) > 0:
self.search(pattern)
def search(self, pattern, item=''):
widgets_children = self.winfo_children()
#print widgets_children
for widget_child in widgets_children:
if isinstance(widget_child, ttk.Treeview):
tree = widget_child
children = tree.get_children(item)
for child in children:
text = tree.item(child, 'text')
if text.lower().startswith(pattern.lower()):
tree.selection_set(child)
tree.see(child)
return True
else:
res = self.search(pattern, child)
if res:
return True
def populate_tree(self, tree, node):
if tree.set(node, "type") != 'directory':
return
path = tree.set(node, "fullpath")
tree.delete(*tree.get_children(node))
parent = tree.parent(node)
special_dirs = [] if parent else glob.glob('.') + glob.glob('..')
for p in special_dirs + os.listdir(path):
ptype = None
p = os.path.join(path, p).replace('\', '/')
if os.path.isdir(p): ptype = "directory"
elif os.path.isfile(p): ptype = "file"
fname = os.path.split(p)[1]
id = tree.insert(node, "end", text=fname, values=[p, ptype])
if ptype == 'directory':
if fname not in ('.', '..'):
tree.insert(id, 0, text="dummy")
tree.item(id, text=fname)
elif ptype == 'file':
size = os.stat(p).st_size
tree.set(id, "size", "%d bytes" % size)
def populate_roots(self, tree):
dir = os.path.abspath('.').replace('\', '/')
node = tree.insert('', 'end', text=dir, values=[dir, "directory"])
self.populate_tree(tree, node)
def update_tree(self, event):
tree = event.widget
self.populate_tree(tree, tree.focus())
def change_dir(self, event):
tree = event.widget
node = tree.focus()
if tree.parent(node):
path = os.path.abspath(tree.set(node, "fullpath"))
if os.path.isdir(path):
os.chdir(path)
tree.delete(tree.get_children(''))
self.populate_roots(tree)
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 _press3(self, event):
print("Tipo de Evento: " + event.keysym)
if __name__ == "__main__":
App = ListagemDir(None)
App.mainloop()