Как реализовать сортировку БД таблицы tkinter по заголовку

Проблема - lambda не видит созданную функцию treeview_sort_column, пытался вынести функцию в класс main, но тогда появляется проблема с ключевыми командами в функции ('Treeview' object has no attribute 'item, move" и тд.) пример кода:

import sqlite3  # импорт модуля SQLite
import tkinter as tk  # импорт библиотеки tkinter
from tkinter import ttk  # импорт модуля TTk

class Main(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.init_main()
        self.db = db
        self.view_records()

    # noinspection PyTypeChecker
    def init_main(self):  # главное окно
        toolbar = tk.Frame(bg='#d7d8e0', bd=2)
        toolbar.pack(side=tk.TOP, fill=tk.X)


        self.add_img = tk.PhotoImage('')
        btn_open_dialog = tk.Button(toolbar, text='Добавить позицию', command=self.open_dialog, bg='#d7d8e0', bd=0,compound=tk.TOP, image=self.add_img)
        btn_open_dialog.pack(side=tk.LEFT)

        self.update_img = tk.PhotoImage('')
        btn_edit_dialog = tk.Button(toolbar, text='Редактировать', bg='#d7d8e0', bd=0, image=self.update_img, compound=tk.TOP, command=self.open_update_dialog)
        btn_edit_dialog.pack(side=tk.LEFT)

        self.tree = ttk.Treeview(self, columns=('id', 'prefix', 'number', 'postfix', 'zavod', 'quantity', 'cons_date', 'ubd_date'), height=15, show='headings')

        def treeview_sort_column(self, col, reverse):
            l = [(self.item(k)["text"], k) for k in self.get_children()]
            l.sort(key=lambda t: t[0], reverse=reverse)

            for index, (val, k) in enumerate(l):
                self.move(k, '', index)

            self.heading(col, command=lambda: treeview_sort_column(self, col, not reverse))

        self.tree.column('id', width=30, anchor=tk.CENTER)
        self.tree.column('prefix', width=60, anchor=tk.CENTER)
        self.tree.column('number', width=150, anchor=tk.CENTER)
        self.tree.column('postfix', width=50, anchor=tk.CENTER)
        self.tree.column('zavod', width=80, anchor=tk.CENTER)
        self.tree.column('quantity', width=50, anchor=tk.CENTER)
        self.tree.column('cons_date', width=100, anchor=tk.CENTER)
        self.tree.column('ubd_date', width=100, anchor=tk.CENTER)

        self.tree.heading('id', text='ID', command=lambda: self.treeview_sort_column(self.tree, 'id', False))
        self.tree.heading('prefix', text='Префикс', command=lambda: self.treeview_sort_column(self.tree, "prefix", False))
        self.tree.heading('number', text='Номенклатура', command=lambda: self.treeview_sort_column(self.tree, "number", False))
        self.tree.heading('postfix', text='Суфикс', command=lambda: self.treeview_sort_column(self.tree, "postfix", False))
        self.tree.heading('zavod', text='Завод', command=lambda: self.treeview_sort_column(self.tree, "zavod", False))
        self.tree.heading('quantity', text='Ящ норма', command=lambda: self.treeview_sort_column(self.tree, "quantity", False))
        self.tree.heading('cons_date', text='Дата консервации', command=lambda: self.treeview_sort_column(self.tree, "cons_date", False))
        self.tree.heading('ubd_date', text='Дата введения', command=lambda: self.treeview_sort_column(self.tree, "ubd_date", False))

        self.tree.pack()

Пробую реализовать вот эту сортировку Как реализовать сортировку в БД?, однако я, похоже, где-то ошибся и не вижу этого.


Ответы (1 шт):

Автор решения: insolor

У вас в коде вариант treeview_sort_column для сортировки по тексту из дерева (у вас дерево вообще отключено). Вам нужен вариант этой функции из этого вопроса. Также это отдельная функция, а не метод класса Main, поэтому не нужно пытаться вызывать ее как self.treeview_sort_column(...), иначе будет сыпать ошибками AttributeError: 'Main' object has no attribute 'treeview_sort_column'. Т.к. это не метод, то и не нужно переименовывать первый параметр в self, а оставить как есть.

Рабочий пример:

import tkinter as tk  # импорт библиотеки tkinter
from tkinter import ttk  # импорт модуля TTk


class Main(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.init_main()
        # self.db = db
        self.view_records()

    def view_records(self):
        for i in range(10):
            self.tree.insert("",0,values=(i,))

    # noinspection PyTypeChecker
    def init_main(self):  # главное окно
        toolbar = tk.Frame(bg='#d7d8e0', bd=2)
        toolbar.pack(side=tk.TOP, fill=tk.X)

        # self.add_img = tk.PhotoImage('')
        # btn_open_dialog = tk.Button(toolbar, text='Добавить позицию', command=self.open_dialog, bg='#d7d8e0', bd=0,compound=tk.TOP, image=self.add_img)
        # btn_open_dialog.pack(side=tk.LEFT)

        # self.update_img = tk.PhotoImage('')
        # btn_edit_dialog = tk.Button(toolbar, text='Редактировать', bg='#d7d8e0', bd=0, image=self.update_img, compound=tk.TOP, command=self.open_update_dialog)
        # btn_edit_dialog.pack(side=tk.LEFT)

        self.tree = ttk.Treeview(self, columns=('id', 'prefix', 'number', 'postfix', 'zavod', 'quantity', 'cons_date', 'ubd_date'), height=15, show='headings')

        def treeview_sort_column(tv, col, reverse):
            l = [(tv.set(k, col), k) for k in tv.get_children()]
            l.sort(reverse=reverse)

            for index, (val, k) in enumerate(l):
                tv.move(k, '', index)

            tv.heading(col, command=lambda: treeview_sort_column(tv, col, not reverse))

        self.tree.column('id', width=30, anchor=tk.CENTER)
        self.tree.column('prefix', width=60, anchor=tk.CENTER)
        self.tree.column('number', width=150, anchor=tk.CENTER)
        self.tree.column('postfix', width=50, anchor=tk.CENTER)
        self.tree.column('zavod', width=80, anchor=tk.CENTER)
        self.tree.column('quantity', width=50, anchor=tk.CENTER)
        self.tree.column('cons_date', width=100, anchor=tk.CENTER)
        self.tree.column('ubd_date', width=100, anchor=tk.CENTER)

        self.tree.heading('id', text='ID', command=lambda: treeview_sort_column(self.tree, 'id', False))
        self.tree.heading('prefix', text='Префикс', command=lambda: treeview_sort_column(self.tree, "prefix", False))
        self.tree.heading('number', text='Номенклатура', command=lambda: treeview_sort_column(self.tree, "number", False))
        self.tree.heading('postfix', text='Суфикс', command=lambda: treeview_sort_column(self.tree, "postfix", False))
        self.tree.heading('zavod', text='Завод', command=lambda: treeview_sort_column(self.tree, "zavod", False))
        self.tree.heading('quantity', text='Ящ норма', command=lambda: treeview_sort_column(self.tree, "quantity", False))
        self.tree.heading('cons_date', text='Дата консервации', command=lambda: treeview_sort_column(self.tree, "cons_date", False))
        self.tree.heading('ubd_date', text='Дата введения', command=lambda: treeview_sort_column(self.tree, "ubd_date", False))

        self.tree.pack()


root = tk.Tk()
Main(root).pack()
root.mainloop()

По поводу более естественной сортировки, изменяем функцию таким образом:

def treeview_sort_column(tv, col, reverse, key=str):
    l = [(tv.set(k, col), k) for k in tv.get_children()]
    l.sort(reverse=reverse, key=lambda t: key(t[0]))

    for index, (val, k) in enumerate(l):
        tv.move(k, '', index)

    tv.heading(col, command=lambda: treeview_sort_column(tv, col, not reverse, key=key))

Для числовых колонок в качестве параметра key передаем int (или float, если возможны дробные значения), для даты передаем функцию (в данном случае лябмда-функцию lambda s: datetime.strptime(s, "%d.%m.%Y") для даты формата DD.MM.YYYY, datetime импортируется как from datetime import datetime), конвертирующую дату в виде строки в объект даты, который потом нормально отсортируется при помощи sort. Для колонок со строковыми значениями можно ничего не передавать. Примеры:

self.tree.heading('id', text='ID', command=lambda: treeview_sort_column(self.tree, 'id', False, key=int))
self.tree.heading('prefix', text='Префикс', command=lambda: treeview_sort_column(self.tree, "prefix", False))
self.tree.heading('number', text='Номенклатура', command=lambda: treeview_sort_column(self.tree, "number", False))
self.tree.heading('postfix', text='Суфикс', command=lambda: treeview_sort_column(self.tree, "postfix", False))
self.tree.heading('zavod', text='Завод', command=lambda: treeview_sort_column(self.tree, "zavod", False))
self.tree.heading('quantity', text='Ящ норма', command=lambda: treeview_sort_column(self.tree, "quantity", False, key=int))
self.tree.heading('cons_date', text='Дата консервации', command=lambda: treeview_sort_column(self.tree, "cons_date", False, key=lambda s: datetime.strptime(s, "%d.%m.%Y")))
self.tree.heading('ubd_date', text='Дата введения', command=lambda: treeview_sort_column(self.tree, "ubd_date", False, key=lambda s: datetime.strptime(s, "%d.%m.%Y")))

Вообще, параметр key методов сортировки (list.sort или sorted) - это функция, которая применяется к каждому из сортируемых значений, по результатам выполнения этих функций в итоге выполняется сортировка. Документация.

Для сортировки смешанных данных (числа внутри строк, например что-то вроде "Изображение 10") можно использовать модуль natsort (установка через pip install natsort)

→ Ссылка