Класс ttk.Treeview – пример принципиально нового элемента, который входит в библиотеку tkinter.ttk. С его помощью информация может быть представлена в иерархической или табличной форме. Это позволит более удобно ее считывать.
Каждый элемент, который добавляется к классу ttk.Treeview, состоит из нескольких колонок (одной – также возможный вариант). С помощью первой отображаются текст и иконка, показывающие, возможно ли раскрытие элемента. А все остальные колонки показывают сами значения.
Первая строка этого класса содержит заголовки, которые можно спрятать. Также этот класс может работать с CSV-файлами. И мы наглядно это продемонстрируем.
Практика использования виджета Treeview
Давайте попробуем создать такое приложение. Оно получает список контактов, которые содержатся в CSV-файле и представляет его в виде таблицы. Выглядеть приложение будет так.
Давайте создадим объект класса ttk.Treeview, который будет содержать три колонки, в которых будут содержаться имя, фамилия и адрес электронной почты.
Эти данные будут загружаться из файла формата CSV, а потом будет осуществляться связывание виртуального элемента <<TreeviewSelect>>. Отсюда исходит вопрос – откуда он берется? Он появляется, если выбирается хотя бы один элемент.
Нагляднее будет с примером кода.
import csv
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self, path):
super().__init__()
self.title("Ttk Treeview")
columns = ("#1", "#2", "#3")
self.tree = ttk.Treeview(self, show="headings", columns=columns)
self.tree.heading("#1", text="Фамилия")
self.tree.heading("#2", text="Имя")
self.tree.heading("#3", text="Почта")
ysb = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscroll=ysb.set)
with open("../lesson_13/contacts.csv", newline="") as f:
for contact in csv.reader(f):
self.tree.insert("", tk.END, values=contact)
self.tree.bind("<<TreeviewSelect>>", self.print_selection)
self.tree.grid(row=0, column=0)
ysb.grid(row=0, column=1, sticky=tk.N + tk.S)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
def print_selection(self, event):
for selection in self.tree.selection():
item = self.tree.item(selection)
last_name, first_name, email = item["values"][0:3]
text = "Выбор: {}, {} <{}>"
print(text.format(last_name, first_name, email))
if __name__ == "__main__":
app = App(path=".")
app.mainloop()
При запуске этого приложения после выбора контакта информация о нем будет выводиться в стандартный вывод.
Принцип использования виджета Treeview
Чтобы создать экземпляр класса ttk.Treeview, в котором есть несколько колонок, необходимо задать идентификатор каждого столбца с использованием параметра columns. После этого уже осуществляется настройка текста заголовка, для чего используется функция heading(), которая вызывается из объекта tree(проще говоря, это – метод).
В нашем примере мы используем идентификаторы #1, #2, #3. Все дело в том, что первая колонка всегда идентифицируется, как #0.
Кроме этого, необходимо передать значение headings параметру show. Это делается с целью обозначить, что нужно спрятать нулевую колонку. Ведь в нашем случае вложенных элементов не предусматривается.
Для параметра show следующие значения могут использоваться:
- tree. Показывает нулевую колонку.
- headings. Показывает строку заголовка.
- tree headings. Показывает и нулевую колонку, и строку заголовка. Это стандартное значение. То есть, если не задать другое, то будет использоваться это.
- “». Не показывает ни строку заголовка, ни нулевую колонку.
Затем к виджету мы добавляем вертикальную прокрутку.
columns = ("#1", "#2", "#3")
self.tree = ttk.Treeview(self, show="headings", columns=columns)
self.tree.heading("#1", text="Фамилия")
self.tree.heading("#2", text="Имя")
self.tree.heading("#3", text="Почта")
ysb = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self.tree.yview)
self.tree.configure(yscroll=ysb.set)
Чтобы осуществить загрузку контактов в таблицу, необходимо воспользоваться функцией render(), которая находится в модуле CSV, чтобы обработать ее. В процессе этого, строки на каждой из итераций, добавляются к дереву.
Как это делается? Используется метод insert(), получающий родительский узел и положение для размещения элемента.
Так, как все контакты отображаются, как объекты верхнего уровня, передаем в качестве первого параметра пустую строку, а также константу END. Это делается для того, чтобы добавлять новые контакты на последнюю позицию.
Кроме этого, возможно использование и других ключевых слов для этого метода. Тут применяется параметр values, принимающий последовательность значений, которые и показываются в колонках нашего дерева.
with open("../lesson_13/contacts.csv", newline="") as f:
for contact in csv.reader(f):
self.tree.insert("", tk.END, values=contact)
self.tree.bind("<<TreeviewSelect>>", self.print_selection)
<<TreeviewSelect>> – это виртуальное событие, генерация которого осуществляется при выборе элементов таблицы. Далее используем метод selection для того, чтобы получить текущее выделение. Эта команда исполняется в обработчике print_selection().
Далее для каждого результата выполняется следующая последовательность действий:
- Используется метод item(), который помогает нам получить параметры и значения выбранного объекта в виде словаря.
- Получаем имя, фамилию и адрес электронной почты. Для этого необходимо получить первые три значения этого словаря.
- После этого значения выводятся в стандартный вывод. Предварительно осуществляется их форматирование.
def print_selection(self, event):
for selection in self.tree.selection():
item = self.tree.item(selection)
last_name, first_name, email = item["values"][0:3]
text = "Выбор: {}, {} <{}>"
print(text.format(last_name, first_name, email))
Мы рассмотрели основные особенности класса ttk.Treeview, так как мы работали со стандартной таблицей. Тем не менее, она поддерживает значительно больше функций.
Как использовать теги в элементах Treeview?
С помощью тегов разработчик может связать последовательности события с конкретными строками таблицы. Допустим, появилась потребность открывать новое окно, чтобы добавить информацию об электронной почте по двойному клику. Правда, это должно работать исключительно для тех записей, в которых поле email уже содержит какую-то информацию.
Чтобы это реализовать, необходимо добавить тег с условием во время вставки. Затем необходимо осуществить вызов tag_bind() на элементе виджета, содержащего последовательность <Double-Button-1>. Тут просто возможна ссылка на функцию-обработчика send_email_to_contact() по имени.
columns = ("Фамилия", "Имя", "Почта")
tree = ttk.Treeview(self, show="headings", columns=columns)
for contact in csv.reader(f):
email = contact[2]
tags = ("dbl-click",) if email else ()
self.tree.insert("", tk.END, values=contact, tags=tags)
tree.tag_bind("dbl-click", "<Double-Button-1>", send_email_to_contact)
Аналогично процессам, которые осуществляются при связывании событий с элементами Canvas, необходимо помнить о том, что нужно добавлять элементы с тегами к ttk.Treeview до того, как будет осуществлен вызов tag_bind(), поскольку добавление связываний осуществляется исключительно к имеющимся совпадениям в элементах.
Как осуществлять заполнение вложенных элементов?
ttk.Treeview нередко используется в качестве обычной таблицы. Тем не менее, намного интереснее использовать его по своему основному назначению – для отображения иерархической структуры.
Давайте попробуем создать такое приложение.
Для заполнения дерева будет использоваться метод populate_node().
При раскрытии узла осуществляется еще один вызов этого метода, после чего туда записываются вложенные элементы.
import os
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self, path):
super().__init__()
self.title("Ttk Treeview")
abspath = os.path.abspath(path)
self.nodes = {}
self.tree = ttk.Treeview(self)
self.tree.heading("#0", text=abspath, anchor=tk.W)
ysb = ttk.Scrollbar(self, orient=tk.VERTICAL,
command=self.tree.yview)
xsb = ttk.Scrollbar(self, orient=tk.HORIZONTAL,
command=self.tree.xview)
self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)
self.tree.grid(row=0, column=0, sticky=tk.N + tk.S + tk.E + tk.W)
ysb.grid(row=0, column=1, sticky=tk.N + tk.S)
xsb.grid(row=1, column=0, sticky=tk.E + tk.W)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.tree.bind("<<TreeviewOpen>>", self.open_node)
self.populate_node("", abspath)
def populate_node(self, parent, abspath):
for entry in os.listdir(abspath):
entry_path = os.path.join(abspath, entry)
node = self.tree.insert(parent, tk.END, text=entry, open=False)
if os.path.isdir(entry_path):
self.nodes[node] = entry_path
self.tree.insert(node, tk.END)
def open_node(self, event):
item = self.tree.focus()
abspath = self.nodes.pop(item, False)
if abspath:
children = self.tree.get_children(item)
self.tree.delete(children)
self.populate_node(item, abspath)
if __name__ == "__main__":
app = App(path="../")
app.mainloop()
Как видим, ничего нет сложного. Все просто и интуитивно понятно. Достаточно выполнять инструкции, которые приводятся здесь, и можно создавать самые сложные иерархические структуры и таблицы.















