Skip to content

feat(tree): implement sortData and sortKey#560

Draft
slook wants to merge 2 commits into
ceccopierangiolieugenio:mainfrom
slook:tree-sortkey
Draft

feat(tree): implement sortData and sortKey#560
slook wants to merge 2 commits into
ceccopierangiolieugenio:mainfrom
slook:tree-sortkey

Conversation

@slook

@slook slook commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

TTkTreeWidget() and TTkTreeWidgetItem():

  • Added: sortKey() and sortData() to provide custom values to the key argument of the internal sort() function.
  • Added: Optional key keyword argument for sortChildren(). None fall-back to the simple character-based sorting.
  • Fixed: Use plaintext strings instead of ANSI codes in TTkString() objects to sort items where no custom key is specified.

TTkFileTreeWidget() and TTkFileTreeWidgetItem():

  • Added: Natural sorting of folders and files using _raw data values which were stored but hadn't been implemented yet.
  • Changed: sortData() in FileTreeWidgetItem now returns List instead of str type.
  • Fixed: Sort all FileTreeWidgetItem items after populating the widget instead of sorting every row after each insertion which was causing a very large performance impact while listing large folders after enabling a sort column.

Folders are always grouped at the top (regardless of the direction of the sort order), including inside expanded children:
image image
Sorting by "Size" uses the _raw values in the FileTreeWidgetItem to give natural sorting order ^^

The file and folder items are now always grouped at the top and the proper numeric values cause the items to be displayed in a much more logical manor that makes navigating a filesystem work more like one would reasonably expect. It is a good example of the reason why such an implementation is desirable. The documentation strings explain how to implement it for any purpose.

The default behaviour of TTkTree is unchanged, you have to override the sortKey() or sortData() in a sub-class to get some other values to use as the key to do custom sorting strategies. The advantage of allowing Any type of value (or tuple of values) to be returned by sortKey() is that any complexity of multi-factor ordering can be sorted() in a single pass.


Note: AI (Cognition) wrote the regex used for alphanumeric splitting and a human (me) implemented the sorting with reference to the Python docs sorted() and https://docs.python.org/3/howto/sorting.html#sortinghowto

@slook

slook commented Dec 22, 2025

Copy link
Copy Markdown
Contributor Author

Pushed a change only to clarify the docstrings but did not make any changes to the code. If not deemed suitable for merging then it is open for suggestions or other ideas to be taken from it.

@slook slook marked this pull request as draft May 4, 2026 14:10
@slook

slook commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Draft as need to adapt CI test for the new sorting algorithm...

============================= test session starts ==============================
platform linux -- Python 3.9.25, pytest-8.4.2, pluggy-1.6.0
rootdir: /home/runner/work/pyTermTk/pyTermTk
configfile: pyproject.toml
collected 772 items
tests/pytest/extra/test_enum.py .                                        [  0%]
tests/pytest/modelView/test_tablemodelcsv.py .........................   [  3%]
tests/pytest/modelView/test_tablemodellist.py .......................    [  6%]
tests/pytest/modelView/test_tablemodelsqlite3.py ....................... [  9%]
......................................                                   [ 14%]
tests/pytest/modelView/test_tablewidget.py ............................. [ 18%]
.                                                                        [ 18%]
tests/pytest/modelView/test_tablewidget_selectionproxy.py .............. [ 19%]
...........................                                              [ 23%]
tests/pytest/modelView/test_treewidget.py .............................. [ 27%]
.............                                                            [ 29%]
tests/pytest/test_003_string.py .                                        [ 29%]
tests/pytest/test_004_signals_slots.py ..                                [ 29%]
tests/pytest/test_006_tree.py .........F..                               [ 30%]
tests/pytest/test_007_terminal.py ...................................... [ 35%]
...........................................                              [ 41%]
tests/pytest/widgets/test_add_remove_widget.py ...............           [ 43%]
tests/pytest/widgets/test_combobox.py .................................. [ 47%]
..                                                                       [ 48%]
tests/pytest/widgets/test_date.py ...................................... [ 52%]
...........................                                              [ 56%]
tests/pytest/widgets/test_focus_01.py ........                           [ 57%]
tests/pytest/widgets/test_focus_02_first_focus.py ..                     [ 57%]
tests/pytest/widgets/test_focus_02_tab.py ..............                 [ 59%]
tests/pytest/widgets/test_kode_tab.py ...........................        [ 63%]
tests/pytest/widgets/test_list.py ..............................         [ 66%]
tests/pytest/widgets/test_tab.py ...................................     [ 71%]
tests/pytest/widgets/test_time.py ...................................... [ 76%]
...                                                                      [ 76%]
tests/pytest/widgets/textedit/test_textcursor.py ..........              [ 78%]
tests/pytest/widgets/textedit/test_textdocument.py ............          [ 79%]
tests/pytest/widgets/textedit/test_textdocument_highlight.py ........... [ 81%]
.................                                                        [ 83%]
tests/pytest/widgets/textedit/test_textedit.py ......................... [ 86%]
........                                                                 [ 87%]
tests/pytest/widgets/textedit/test_textedit_ruler.py ................... [ 90%]
............                                                             [ 91%]
tests/pytest/widgets/textedit/test_textwrap.py .....................     [ 94%]
tests/pytest/widgets/textedit/test_textwrap_threading.py ............... [ 96%]
....                                                                     [ 96%]
tests/pytest/widgets/textedit/test_textwrap_vim_wrap.py ................ [ 98%]
.........                                                                [100%]
=================================== FAILURES ===================================
________________________________ test_tree_sort ________________________________
    def test_tree_sort():
        tree,c1,c2 = _create_tree()
    
        def _test():
            full_page = _get_full_tree_page(tree)
            page = tree._get_page(0,0,10000)
            assert (
                [f"{c.isExpanded()} {c.data(0)}" for c in full_page] ==
                [f"{c.isExpanded()} {c.data(0)}" for _,_,c in page] )
    
        _test()
    
>       tree.sortChildren(0,ttk.TTkK.AscendingOrder)
tests/pytest/test_006_tree.py:410: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/treewidgetitem.py:538: in sortChildren
    self._children.sort()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = _TTkTreeChildren(_parent=<TermTk.TTkWidgets.TTkModelView.treewidgetitem.TTkTreeWidgetItem object at 0x7fc1e2768100>, _...object at 0x7fc1e2768340>, <TermTk.TTkWidgets.TTkModelView.treewidgetitem.TTkTreeWidgetItem object at 0x7fc1e2768400>])
    def sort(self):
        if self._parent._sortColumn == -1: return
>       self._children = sorted(
            self._children,
            key=self._parent._sortKey,
            reverse=self._parent._sortOrder == TTkK.DescendingOrder
        )
E       TypeError: '<' not supported between instances of 'TTkTreeWidgetItem' and 'TTkTreeWidgetItem'
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/treewidgetitem.py:212: TypeError
=============================== warnings summary ===============================
libs/pyTermTk/TermTk/TTkWidgets/TTkTerminal/terminalhelper.py:203
  /home/runner/work/pyTermTk/pyTermTk/tests/pytest/modelView/../../../libs/pyTermTk/TermTk/TTkWidgets/TTkTerminal/terminalhelper.py:203: DeprecationWarning: invalid escape sequence \ 
    '''
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/pytest/test_006_tree.py::test_tree_sort - TypeError: '<' not supported between instances of 'TTkTreeWidgetItem' and 'TTkTreeWidgetItem'
=================== 1 failed, 771 passed, 1 warning in 7.82s ===================
Error: Process completed with exit code 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant