Home > Software engineering >  Python Treeview alternate row colouring after sorting
Python Treeview alternate row colouring after sorting

Time:11-01

Is there a way that the alternative row colouring can be maintained after sorting? My treeview has 2,000 rows and would like to know if there is any solution other than retagging all the rows each time a column is sorted. When you click on any column, the row colouring gets mixed up.

Environment: Python 3.10.0 Windows 21H1

import tkinter as objTK
from tkinter import ttk as objTTK
from functools import partial

# Custom column sort for treeview
class MyTreeview(objTTK.Treeview):
    def heading(self, column, sort_by=None, **kwargs):
        if sort_by and not hasattr(kwargs, 'command'):
            func = getattr(self, f"_sort_by_{sort_by}", None)
            if func:
                kwargs['command'] = partial(func, column, False)
            # End of if
        # End of if
        return super().heading(column, **kwargs)
    # End of heading()

    def _sort(self, column, reverse, data_type, callback):
        l = [(self.set(k, column), k) for k in self.get_children('')]
        l.sort(key=lambda t: data_type(t[0]), reverse=reverse)
        for index, (_, k) in enumerate(l):
            self.move(k, '', index)
        # End of for loop
        self.heading(column, command=partial(callback, column, not reverse))
    # End of _sort()

    def _sort_by_name(self, column, reverse):
        self._sort(column, reverse, str, self._sort_by_name)
    # End of _sort_by_num()
# End of class MyTreeview

objWindow = objTK.Tk()

arrlbHeader = ["Type" , "Description"]
treeview = MyTreeview(columns=arrlbHeader, show="headings")
arrRows = [
    ["Expenses", "Gen"],
    ["Expenses", "Aug"],
    ["Expenses", "Aug"],
    ["Income", "Aug"],
    ["Expenses", "Aug"]
]
arrColWidth = [70, 80]
arrColAlignment = ["center", "e"]

# Column header and attributes
arrSortType = ["name", "name"]
for iCount in range(len(arrlbHeader)):
    strHdr = arrlbHeader[iCount]
    treeview.heading(strHdr, text=strHdr.title(), sort_by=arrSortType[iCount])
    treeview.column(arrlbHeader[iCount], width=arrColWidth[iCount], stretch=True, anchor=arrColAlignment[iCount])
# End of for loop

treeview.pack()

# Row colouring tags
treeview.tag_configure("tgOddRow", background="white")
treeview.tag_configure("tgEvenRow", background="blue")      

# Insert rows
for iCount in range(len(arrRows)):
    if iCount % 2 == 0:
        treeview.insert("", "end", values=arrRows[iCount], tags="tgEvenRow")
    else:
        treeview.insert("", "end", values=arrRows[iCount], tags="tgOddRow")
# End of for loop

objWindow.bind("<Escape>", lambda funcWinSer: objWindow.destroy())

objWindow.mainloop()

CodePudding user response:

The solution is to retag all of the items. The treeview widget can retag a couple thousand rows in a tiny fraction of a second.

Here's a simple example. It assumes you don't have items nested under other items. If you do, it's fairly straightforward to account for that.

def retag():
    tag = "tgOddRow"
    for iid in treeview.get_children(""):
        tag = "tgOddRow" if tag == "tgEvenRow" else "tgEvenRow"
        treeview.item(iid, tags=(tag,))

When I run that function on a treeview with 10,000 rows, it takes well under 100ms.

  • Related