Browse Source

Added Table sort

pull/272/head
Eugenio Parodi 1 year ago
parent
commit
4a380cf992
  1. 5
      TermTk/TTkCore/constant.py
  2. 1
      TermTk/TTkWidgets/TTkModelView/__init__.py
  3. 62
      TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py
  4. 0
      TermTk/TTkWidgets/TTkModelView/tablemodeljson.py
  5. 71
      TermTk/TTkWidgets/TTkModelView/tablemodellist.py
  6. 118
      TermTk/TTkWidgets/TTkModelView/tablewidget.py
  7. 4
      tests/t.ui/test.ui.032.table.06.py
  8. 323
      tests/t.ui/test.ui.032.table.07.py
  9. 82
      tests/timeit/02.array.05.sort.py

5
TermTk/TTkCore/constant.py

@ -154,9 +154,12 @@ class TTkConstant:
DontShowIndicator = ChildIndicatorPolicy.DontShowIndicator
DontShowIndicatorWhenChildless = ChildIndicatorPolicy.DontShowIndicatorWhenChildless
class SortOrder(int):
class SortOrder():
'''This enum describes how the items in a widget are sorted.'''
AscendingOrder = 0x00
'''The items are sorted ascending e.g. starts with 'AAA' ends with 'ZZZ' in Latin-1 locales'''
DescendingOrder = 0x01
'''The items are sorted descending e.g. starts with 'ZZZ' ends with 'AAA' in Latin-1 locales'''
AscendingOrder = SortOrder.AscendingOrder
DescendingOrder = SortOrder.DescendingOrder

1
TermTk/TTkWidgets/TTkModelView/__init__.py

@ -7,5 +7,6 @@ from .filetreewidgetitem import *
from .table import *
from .tablewidget import *
from .tablewidgetitem import *
from .tablemodellist import *
from .tablemodelcsv import *
from .tablemodeljson import *

62
TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py

@ -25,59 +25,37 @@ __all__=['TTkTableModelCSV']
import csv
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel
from TermTk.TTkWidgets.TTkModelView.tablemodellist import TTkTableModelList
class TTkTableModelCSV(TTkAbstractTableModel):
__slots__ = ('_list', '_hheader', '_vheader')
class TTkTableModelCSV(TTkTableModelList):
def __init__(self, *, filename='', fd=None):
self._list = []
self._hheader = []
self._vheader = []
ml, head, idx = [[]], [], []
if filename:
with open(filename, "r") as fd:
self._csvImport(fd)
ml, head, idx = self._csvImport(fd)
elif fd:
self._csvImport(fd)
super().__init__()
ml, head, idx = self._csvImport(fd)
super().__init__(list=ml,header=head,indexes=idx)
def _csvImport(self, fd):
def _csvImport(self, fd) -> tuple[list,list,list[list]]:
ml, head, idx = [], [], []
sniffer = csv.Sniffer()
has_header = sniffer.has_header(fd.read(2048))
fd.seek(0)
csvreader = csv.reader(fd)
for row in csvreader:
self._list.append(row)
ml.append(row)
if has_header:
self._hheader = self._list.pop(0)
head = ml.pop(0)
# check if the first column include an index:
if self._checkIndexColumn():
self._hheader.pop(0)
for l in self._list:
self._vheader.append(l.pop(0))
def _checkIndexColumn(self):
if all(l[0].isdigit() for l in self._list):
num = int(self._list[0][0])
return all(num+i==int(l[0]) for i,l in enumerate(self._list))
if self._checkIndexColumn(ml):
head.pop(0)
for l in ml:
idx.append(l.pop(0))
return ml, head, idx
def _checkIndexColumn(self, ml:list[list]) -> bool:
if all(l[0].isdigit() for l in ml):
num = int(ml[0][0])
return all(num+i==int(l[0]) for i,l in enumerate(ml))
return False
def rowCount(self):
return len(self._list)
def columnCount(self):
return len(self._list[0]) if self._list else 0
def data(self, row, col):
return self._list[row][col]
def setData(self, row, col, data):
self._list[row][col] = data
def headerData(self, num, orientation):
if orientation == TTkK.HORIZONTAL:
if self._hheader:
return self._hheader[num]
if orientation == TTkK.VERTICAL:
if self._vheader:
return self._vheader[num]
return super().headerData(num, orientation)

0
TermTk/TTkWidgets/TTkModelView/tablemodeljson.py

71
TermTk/TTkWidgets/TTkModelView/tablemodellist.py

@ -0,0 +1,71 @@
# MIT License
#
# Copyright (c) 2024 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__=['TTkTableModelList']
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel
class TTkTableModelList(TTkAbstractTableModel):
__slots__ = ('_list','_listOriginal', '_hheader', '_vheader')
def __init__(self, *, list=[], header=[], indexes=[]):
self._list = self._listOriginal = list
self._hheader = header
self._vheader = indexes
super().__init__()
def modelList(self) -> list[list]:
return self._list
def setModelList(self, modelList:list[list]) -> None:
if modelList == self._list: return
self._list = modelList
def rowCount(self) -> int:
return len(self._list)
def columnCount(self) -> int:
return len(self._list[0]) if self._list else 0
def data(self, row:int, col:int) -> None:
return self._list[row][col]
def setData(self, row:int, col:int, data:object) -> None:
self._list[row][col] = data
def headerData(self, num:int, orientation:int):
if orientation == TTkK.HORIZONTAL:
if self._hheader:
return self._hheader[num]
if orientation == TTkK.VERTICAL:
if self._vheader:
return self._vheader[num]
return super().headerData(num, orientation)
def sort(self, column:int, order:TTkK.SortOrder) -> None:
if column == -1:
self._list = self._listOriginal
else:
try:
self._list = sorted(self._listOriginal, key=lambda x:x[column], reverse=order==TTkK.SortOrder.DescendingOrder)
except TypeError as _:
self._list = sorted(self._listOriginal, key=lambda x:str(x[column]), reverse=order==TTkK.SortOrder.DescendingOrder)

118
TermTk/TTkWidgets/TTkModelView/tablewidget.py

@ -125,8 +125,11 @@ class TTkTableWidget(TTkAbstractScrollView):
'_showVSeparators', '_showHSeparators',
'_verticalHeader', '_horizontallHeader',
'_colsPos', '_rowsPos',
'_sortingEnabled',
'_dataPadding',
'_internal',
'_selected', '_hSeparatorSelected', '_vSeparatorSelected',
'_selected', '_selectedBase',
'_hSeparatorSelected', '_vSeparatorSelected',
'_hoverPos', '_dragPos', '_currentPos',
'_sortColumn', '_sortOrder',
'_fastCheck', '_guessDataEdit',
@ -137,6 +140,7 @@ class TTkTableWidget(TTkAbstractScrollView):
def __init__(self, *,
tableModel:TTkAbstractTableModel=None,
vSeparator:bool=True, hSeparator:bool=True,
sortingEnabled=False, dataPadding=1,
**kwargs) -> None:
# Signals
# self.itemActivated = pyTTkSignal(TTkTableWidgetItem, int)
@ -148,11 +152,14 @@ class TTkTableWidget(TTkAbstractScrollView):
self._fastCheck = True
self._guessDataEdit = True
self._dataPadding = dataPadding
self._sortingEnabled = sortingEnabled
self._showHSeparators = vSeparator
self._showVSeparators = hSeparator
self._verticalHeader = _HeaderView()
self._horizontallHeader = _HeaderView()
self._selected = None
self._selectedBase = None
self._hoverPos = None
self._dragPos = None
self._currentPos = None
@ -172,6 +179,50 @@ class TTkTableWidget(TTkAbstractScrollView):
self._verticalHeader.visibilityUpdated.connect( self._headerVisibilityChanged)
self._horizontallHeader.visibilityUpdated.connect(self._headerVisibilityChanged)
@pyTTkSlot(bool)
def setSortingEnabled(self, enable:bool) -> None:
'''
If enable is true, enables sorting for the table and immediately trigger a call to sortByColumn() with the current sort section and order
Note: Setter function for property sortingEnabled.
:param enable: the availability of undo
:type enable: bool
'''
if enable == self._sortingEnabled: return
self._sortingEnabled = enable
self.sortByColumn(self._sortColumn, self._sortOrder)
def isSortingEnabled(self) -> bool:
'''
This property holds whether sorting is enabled
If this property is true, sorting is enabled for the table. If this property is false, sorting is not enabled. The default value is false.
Note: . Setting the property to true with setSortingEnabled() immediately triggers a call to sortByColumn() with the current sort section and order.
This property was introduced in Qt 4.2.
'''
return self._sortingEnabled
@pyTTkSlot(int, TTkK.SortOrder)
def sortByColumn(self, column:int, order:TTkK.SortOrder) -> None:
'''
Sorts the model by the values in the given column and order.
column may be -1, in which case no sort indicator will be shown and the model will return to its natural, unsorted order. Note that not all models support this and may even crash in this case.
:param column: the column used for the sorting
:type column: bool
:param order: the sort order
:type order: :class:`~TermTk.TTkCore.constant.TTkK.SortOrder`
'''
self._sortColumn = column
self._sortOrder = order
self._tableModel.sort(column,order)
self.update()
@pyTTkSlot()
def _headerVisibilityChanged(self):
showVH = self._verticalHeader.isVisible()
@ -195,6 +246,8 @@ class TTkTableWidget(TTkAbstractScrollView):
self._rowsPos = [1+x*2 for x in range(rows)]
else:
self._rowsPos = [1+x for x in range(rows)]
# self._selectedBase = sb = [False]*cols
# self._selected = [sb]*rows
self._selected = [[False]*cols for _ in range(rows)]
# Overridden function
@ -214,8 +267,12 @@ class TTkTableWidget(TTkAbstractScrollView):
def setSelection(self, pos:tuple[int,int], size:tuple[int,int], flags:TTkK.TTkItemSelectionModel):
x,y = pos
w,h = size
for line in self._selected[y:y+h]:
line[x:x+w]=[True]*w
if flags & (TTkK.TTkItemSelectionModel.Clear|TTkK.TTkItemSelectionModel.Deselect):
for line in self._selected[y:y+h]:
line[x:x+w]=[False]*w
elif flags & TTkK.TTkItemSelectionModel.Select:
for line in self._selected[y:y+h]:
line[x:x+w]=[True]*w
self.update()
@pyTTkSlot()
@ -224,6 +281,22 @@ class TTkTableWidget(TTkAbstractScrollView):
self.layout().setOffset(-x,-y)
self.update()
def rowCount(self) -> int:
return self._tableModel.rowCount()
def currentRow(self) -> int:
if _cp := self._currentPos:
return _cp[0]
return 0
def columnCount(self) -> int:
return self._tableModel.columnCount()
def currentColumn(self) -> int:
if _cp := self._currentPos:
return _cp[1]
return 0
def verticalHeader(self):
return self._verticalHeader
@ -261,9 +334,6 @@ class TTkTableWidget(TTkAbstractScrollView):
self._refreshLayout()
self.viewChanged.emit()
def setSortingEnabled(self, enable) -> None:
pass
def focusOutEvent(self) -> None:
self._hSeparatorSelected = None
self._vSeparatorSelected = None
@ -299,7 +369,7 @@ class TTkTableWidget(TTkAbstractScrollView):
rowa,rowb = max(0,row-100), min(row+100,rows)
else:
rowa,rowb = 0,rows
return max(_wid(i) for i in range(rowa,rowb))
return max(_wid(i) for i in range(rowa,rowb))+self._dataPadding
@pyTTkSlot(int)
def resizeColumnToContents(self, column:int) -> None:
@ -670,6 +740,13 @@ class TTkTableWidget(TTkAbstractScrollView):
self._hSeparatorSelected = i
self.update()
return True
elif self._sortingEnabled and _x == c-1: # Pressed the sort otder icon
if self._sortColumn == i:
order = TTkK.SortOrder.DescendingOrder if self._sortOrder==TTkK.SortOrder.AscendingOrder else TTkK.SortOrder.AscendingOrder
else:
order = TTkK.SortOrder.AscendingOrder
self.sortByColumn(i,order)
return True
elif _x < c:
# # I-th header selected
# order = not self._sortOrder if self._sortColumn == i else TTkK.AscendingOrder
@ -929,7 +1006,7 @@ class TTkTableWidget(TTkAbstractScrollView):
if xa>w : break
if xb<vhs: continue
cellColor = (
# currentColor if self._currentPos == (row,col) else
currentColor if self._currentPos == (row,col) else
hoverColor if self._hoverPos in [(row,col),(-1,col),(row,-1),(-1,-1)] else
selectedColor if self._selected[row][col] else
rowColor )
@ -1198,14 +1275,21 @@ class TTkTableWidget(TTkAbstractScrollView):
if self._currentPos:
row,col = self._currentPos
xa = sliceCol[col][0]-ox+vhs
xb = sliceCol[col][1]-ox+vhs + (0 if showHS else 1)
xb = sliceCol[col][1]-ox+vhs + (0 if showVS else 1)
ya = sliceRow[row][0]-oy+hhs
yb = sliceRow[row][1]-oy+hhs + (0 if showVS else 1)
yb = sliceRow[row][1]-oy+hhs + (0 if showHS else 1)
currentColorInv = currentColor.background().invertFgBg()
canvas.drawTTkString(pos=(xa,ya), text=TTkString(''+(''*(xb-xa-1))+'',currentColorInv))
canvas.drawTTkString(pos=(xa,yb), text=TTkString(''+(''*(xb-xa-1))+'',currentColorInv))
canvas.fill(char='',pos=(xa,ya+1), size=(1,yb-ya-1), color=currentColorInv)
canvas.fill(char='',pos=(xb,ya+1), size=(1,yb-ya-1), color=currentColorInv)
if showVS and showHS:
canvas.drawTTkString(pos=(xa,ya), text=TTkString(''+(''*(xb-xa-1))+'',currentColorInv))
canvas.drawTTkString(pos=(xa,yb), text=TTkString(''+(''*(xb-xa-1))+'',currentColorInv))
canvas.fill(char='',pos=(xa,ya+1), size=(1,yb-ya-1), color=currentColorInv)
canvas.fill(char='',pos=(xb,ya+1), size=(1,yb-ya-1), color=currentColorInv)
# elif showHS:
# canvas.drawTTkString(pos=(xa+1,ya), text=TTkString( '▄'*(xb-xa-1) ,currentColorInv))
# canvas.drawTTkString(pos=(xa+1,yb), text=TTkString( '▀'*(xb-xa-1) ,currentColorInv))
# if showVS:
# canvas.fill(char='▐',pos=(xa,ya+1), size=(1,yb-ya-1), color=currentColorInv)
# canvas.fill(char='▌',pos=(xb,ya+1), size=(1,yb-ya-1), color=currentColorInv)
# Draw H-Header first:
if showHH:
@ -1220,9 +1304,9 @@ class TTkTableWidget(TTkAbstractScrollView):
else:
xa,xb = xa+vhs-ox+1, xb+vhs-ox+1
canvas.drawText(pos=(xa,0), text=txt, width=xb-xa, color=headerColor)
if col == self._sortColumn:
s = '' if self._sortOrder == TTkK.AscendingOrder else ''
canvas.drawText(pos=(xb,0), text=s, color=headerColor)
if self._sortingEnabled:
s = '' if col != self._sortColumn else '' if self._sortOrder == TTkK.AscendingOrder else ''
canvas.drawText(pos=(xb-1,0), text=s, color=headerColor)
if showVS:
canvas.drawChar(pos=(xb,0), char='', color=headerColor)

4
tests/t.ui/test.ui.032.table.06.py

@ -292,8 +292,8 @@ tableStyle5 = {'default': defaultStyle|{
# Header Checkboxes
ttk.TTkLabel(parent=controls, pos=(1,0), text="Header:")
ht = ttk.TTkCheckbox(parent=controls, pos=( 1,1), size=(15,1), text=' Top ',checked=True)
hl = ttk.TTkCheckbox(parent=controls, pos=(10,1), size=(15,1), text=' Left',checked=True)
ht = ttk.TTkCheckbox(parent=controls, pos=( 1,1), size=(8,1), text=' Top ',checked=True)
hl = ttk.TTkCheckbox(parent=controls, pos=(10,1), size=(8,1), text=' Left',checked=True)
ht.toggled.connect(table.horizontalHeader().setVisible)
hl.toggled.connect(table.verticalHeader().setVisible)

323
tests/t.ui/test.ui.032.table.07.py

@ -0,0 +1,323 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2024 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Demo inspired from:
# https://www.daniweb.com/programming/software-development/code/447834/applying-pyside-s-qabstracttablemodel
import os
import sys
import csv
import re
import argparse
import operator
import json
import random
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen', action='store_true')
parser.add_argument('-t', help='Track Mouse', action='store_true')
parser.add_argument('--csv', help='Open CSV File', type=argparse.FileType('r'))
args = parser.parse_args()
fullScreen = args.f
mouseTrack = args.t
# csvData = []
# if args.csv:
# sniffer = csv.Sniffer()
# has_header = sniffer.has_header(args.csv.read(2048))
# args.csv.seek(0)
# csvreader = csv.reader(args.csv)
# for row in csvreader:
# csvData.append(row)
imagesFile = os.path.join(os.path.dirname(os.path.abspath(__file__)),'../ansi.images.json')
with open(imagesFile) as f:
d = json.load(f)
# Image exported by the Dumb Paint Tool - Removing the extra '\n' at the end
diamond = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['diamond' ])[0:-1])
fire = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['fire' ])[0:-1])
fireMini = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['fireMini'])[0:-1])
key = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['key' ])[0:-1])
peach = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['peach' ])[0:-1])
pepper = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['pepper' ])[0:-1])
python = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['python' ])[0:-1])
ring = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['ring' ])[0:-1])
sword = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['sword' ])[0:-1])
whip = ttk.TTkString(ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['whip' ])[0:-1])
tiles = [diamond,fire,key,peach,ring,sword,whip]
images = [fireMini,pepper,python]
class CustomColorModifier(ttk.TTkAlternateColor):
colors = (
[ ttk.TTkColor.bg("#000066"), ttk.TTkColor.bg("#0000FF") ] * 3 +
[ ttk.TTkColor.bg("#003300"), ttk.TTkColor.bg("#006600") ] +
[ ttk.TTkColor.bg("#000066"), ttk.TTkColor.bg("#0000FF") ] * 3 +
[ttk.TTkColor.RST] * 5 +
[ #Rainbow-ish
ttk.TTkColor.fgbg("#00FFFF","#880000") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#00FFFF","#FF0000") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#0000FF","#FFFF00") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#FF00FF","#00FF00") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#FF0000","#00FFFF") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#FFFF00","#0000FF") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#00FF00","#FF00FF") + ttk.TTkColor.BOLD,
ttk.TTkColor.fgbg("#00FF00","#880088") + ttk.TTkColor.BOLD] +
[ttk.TTkColor.RST] * 3 +
[ttk.TTkColor.bg("#0000FF")] +
[ttk.TTkColor.RST] * 2 +
[ttk.TTkColor.bg("#0000FF")] +
[ttk.TTkColor.RST] +
[ttk.TTkColor.bg("#0000FF")] +
[ttk.TTkColor.RST] +
[ttk.TTkColor.bg("#0000FF")] * 2 +
[ttk.TTkColor.RST] * 3 +
[ttk.TTkColor.bg("#0000FF")] * 3 +
[ttk.TTkColor.RST] * 5 +
#Rainbow-ish 2
[ttk.TTkColor.fgbg("#00FF00","#880088")] * 2 +
[ttk.TTkColor.fgbg("#00FF00","#FF00FF")] * 2 +
[ttk.TTkColor.fgbg("#FFFF00","#0000FF")] * 2 +
[ttk.TTkColor.fgbg("#FF0000","#00FFFF")] * 2 +
[ttk.TTkColor.fgbg("#FF00FF","#00FF00")] * 2 +
[ttk.TTkColor.fgbg("#0000FF","#FFFF00")] * 2 +
[ttk.TTkColor.fgbg("#00FFFF","#FF0000")] * 2 +
[ttk.TTkColor.fgbg("#00FFFF","#880000")] * 2 +
[ttk.TTkColor.RST] * 2
)
def __init__(self):
super().__init__()
def exec(self, x:int, y:int, base_color:ttk.TTkColor) -> ttk.TTkColor:
c = CustomColorModifier.colors
return c[y%len(c)]
class MyTableModel(ttk.TTkTableModelList):
def __init__(self, mylist, size=None):
self.size=size
super().__init__(list=mylist)
def rowCount(self): return super().rowCount() if not self.size else self.size[0]
def columnCount(self): return super().columnCount() if not self.size else self.size[1]
def headerData(self, num, orientation):
if orientation == ttk.TTkK.HORIZONTAL:
prefix = ['H-aa','H-bb','H-cc','H-dd','H-ee','H-ff','H-gg','Parodi','H-hh',]
return f"{prefix[num%len(prefix)]}:{num:03}"
if orientation == ttk.TTkK.VERTICAL:
prefix = ['aa','bb','cc','dd','ee','ff','gg','Euge']
return f"{prefix[num%len(prefix)]}:{num:03}"
return super().headerData(num, orientation)
txt1 = "Text"
txt2 = txt1*5
txt3 = 'M1: '+' -M1\n'.join([txt1*2]*3)
txt4 = 'M2: '+' -M2\n'.join([txt1*5]*5)
txt5 = ttk.TTkString(txt4, ttk.TTkColor.RED + ttk.TTkColor.BG_YELLOW)
# use numbers for numeric data to sort properly
p1 = 4
p2 = 2
p3 = 2
p4 = 2
data_list1 = [
[f"{x:04}"]+
[txt1,txt2,txt3,txt4,txt5]+
[random.choice(tiles*p1+images*p2+[txt1,txt2]*p3+[123,234,345,567,890,123.001,234.02,345.3,567.04,890.01020304,1]*p4)]+
[random.choice(tiles*p1+images*p2+[txt1,txt2]*p3+[123,234,345,567,890,123.001,234.02,345.3,567.04,890.01020304,1]*p4)]+
[random.choice(tiles*p1+images*p2+[txt1,txt2]*p3+[123,234,345,567,890,123.001,234.02,345.3,567.04,890.01020304,1]*p4)]+
[random.choice(tiles*p1+images*p2+[txt1,txt2]*p3+[123,234,345,567,890,123.001,234.02,345.3,567.04,890.01020304,1]*p4)]+
[random.choice(tiles*p1+images*p2+[txt1,txt2]*p3+[123,234,345,567,890,123.001,234.02,345.3,567.04,890.01020304,1]*p4)]+
[y for y in range(10)]+
[y+0.123 for y in range(10)]
for x in range(5000)]
data_list2 = [
[txt1,txt2,txt3]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[y for y in range(10)]+
[y+0.123 for y in range(10)]
for x in range(1000)]
data_list3 = [
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]+
[random.choice(tiles*p1)]
for x in range(30)]
root = ttk.TTk(title="pyTermTk Table Demo", mouseTrack=mouseTrack)
if fullScreen:
rootTable = root
root.setLayout(ttk.TTkGridLayout())
else:
rootTable = ttk.TTkWindow(parent=root,pos = (0,0), size=(150,40), title="Test Table 1", layout=ttk.TTkGridLayout(), border=True)
splitter = ttk.TTkSplitter(parent=rootTable,orientation=ttk.TTkK.VERTICAL)
table_model1 = MyTableModel(data_list1)
table_model2 = MyTableModel(data_list2)
table_model3 = MyTableModel(data_list3)
# table_model = MyTableModel(data_list, size=(15,10))
table = ttk.TTkTable(parent=splitter, tableModel=table_model1)
# # set column width to fit contents (set font first!)
# table.resizeColumnsToContents()
# # enable sorting
# table.setSortingEnabled(True)
table.setSelection((0,0),(2,2),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((3,0),(1,2),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((1,3),(2,4),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((2,5),(2,4),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((0,9),(2,3),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((1,59),(1,2),ttk.TTkK.TTkItemSelectionModel.Select)
table.setSelection((3,59),(1,2),ttk.TTkK.TTkItemSelectionModel.Select)
controlAndLogsSplitter = ttk.TTkSplitter()
splitter.addWidget(controlAndLogsSplitter,size=8,title="LOGS")
controls = ttk.TTkContainer()
defaultStyle = table.style()['default']
tableStyle1 = {'default': defaultStyle|{'color': ttk.TTkColor.RST} }
tableStyle2 = {'default': defaultStyle|{'color': ttk.TTkColor.bg("#000066", modifier=ttk.TTkAlternateColor(alternateColor=ttk.TTkColor.BG_BLUE))} }
tableStyle3 = {'default': defaultStyle|{
'color': ttk.TTkColor.bg("#006600", modifier=ttk.TTkAlternateColor(alternateColor=ttk.TTkColor.bg("#003300"))),
'selectedColor': ttk.TTkColor.bg("#00AA00"),
'hoverColor': ttk.TTkColor.bg("#00AAAA")} }
tableStyle4 = {'default': defaultStyle|{'color': ttk.TTkColor.bg("#660066", modifier=ttk.TTkAlternateColor(alternateColor=ttk.TTkColor.RST))} }
tableStyle5 = {'default': defaultStyle|{
'color': ttk.TTkColor.bg("#000000", modifier=CustomColorModifier()),
'lineColor': ttk.TTkColor.fg("#FFFF00"),
'headerColor': ttk.TTkColor.fg("#FFFF00")+ttk.TTkColor.bg("#660066"),
'hoverColor': ttk.TTkColor.bg("#AAAAAA"),
'selectedColor': ttk.TTkColor.bg("#FFAA66"),
'separatorColor': ttk.TTkColor.fg("#330055")+ttk.TTkColor.bg("#660066")} }
# Header Checkboxes
ttk.TTkLabel(parent=controls, pos=(1,0), text="Header:")
ht = ttk.TTkCheckbox(parent=controls, pos=( 1,1), size=(8,1), text=' Top ',checked=True)
hl = ttk.TTkCheckbox(parent=controls, pos=(10,1), size=(8,1), text=' Left',checked=True)
ht.toggled.connect(table.horizontalHeader().setVisible)
hl.toggled.connect(table.verticalHeader().setVisible)
# Lines/Separator Checkboxes
ttk.TTkLabel(parent=controls, pos=(1,2), text="Lines:")
vli = ttk.TTkCheckbox(parent=controls, pos=( 1,3), size=(5,1), text=' V',checked=True)
hli = ttk.TTkCheckbox(parent=controls, pos=(10,3), size=(5,1), text=' H',checked=True)
vli.toggled.connect(table.setVSeparatorVisibility)
hli.toggled.connect(table.setHSeparatorVisibility)
# Themes Control
t1 = ttk.TTkRadioButton(parent=controls, pos=(20,1), size=(11,1), text=' Theme 1', radiogroup='Themes', checked=True)
t2 = ttk.TTkRadioButton(parent=controls, pos=(20,2), size=(11,1), text=' Theme 2', radiogroup='Themes')
t3 = ttk.TTkRadioButton(parent=controls, pos=(20,3), size=(11,1), text=' Theme 3', radiogroup='Themes')
t4 = ttk.TTkRadioButton(parent=controls, pos=(20,4), size=(11,1), text=' Theme 4', radiogroup='Themes')
t5 = ttk.TTkRadioButton(parent=controls, pos=(20,5), size=(11,1), text=' Theme 5', radiogroup='Themes')
t1.clicked.connect(lambda : table.mergeStyle(tableStyle1))
t2.clicked.connect(lambda : table.mergeStyle(tableStyle2))
t3.clicked.connect(lambda : table.mergeStyle(tableStyle3))
t4.clicked.connect(lambda : table.mergeStyle(tableStyle4))
t5.clicked.connect(lambda : table.mergeStyle(tableStyle5))
# Model Picker
m1 = ttk.TTkRadioButton(parent=controls, pos=(33,1), size=(11,1), text=' Model 1', radiogroup='Models', checked=True)
m2 = ttk.TTkRadioButton(parent=controls, pos=(33,2), size=(11,1), text=' Model 2', radiogroup='Models')
m3 = ttk.TTkRadioButton(parent=controls, pos=(33,3), size=(11,1), text=' Model 3', radiogroup='Models')
m1.clicked.connect(lambda : table.setModel(table_model1))
m2.clicked.connect(lambda : table.setModel(table_model2))
m3.clicked.connect(lambda : table.setModel(table_model3))
if args.csv:
table_model_csv = ttk.TTkTableModelCSV(fd=args.csv)
m_csv = ttk.TTkRadioButton(parent=controls, pos=(33,4), size=(11,1), text=' CSV', radiogroup='Models')
m_csv.clicked.connect(lambda : table.setModel(table_model_csv))
# Resize Button
rvb = ttk.TTkButton(parent=controls, pos=(1,5), size=( 3,1), text="V", border=False)
rhb = ttk.TTkButton(parent=controls, pos=(4,5), size=( 3,1), text="H", border=False)
rb = ttk.TTkButton(parent=controls, pos=(7,5), size=(11,1), text="Resize", border=False)
rhb.clicked.connect(table.resizeRowsToContents)
rvb.clicked.connect(table.resizeColumnsToContents)
rb.clicked.connect( table.resizeRowsToContents)
rb.clicked.connect( table.resizeColumnsToContents)
controlAndLogsSplitter.addWidget(controls, size=50)
controlAndLogsSplitter.addWidget(ttk.TTkLogViewer())
cbs = ttk.TTkCheckbox(parent=controls, pos=( 33,5), size=(8,1), text='-Sort', checked=False)
cbs.toggled.connect(table.setSortingEnabled)
wkb = ttk.TTkButton(parent=controls, pos=(43,5), size=( 4,1), text="🤌", border=False)
@ttk.pyTTkSlot()
def _showWinKey():
winKey = ttk.TTkWindow(title="KeyPress",layout=ttk.TTkGridLayout(), size=(80,7))
winKey.layout().addWidget(ttk.TTkKeyPressView(maxHeight=3))
ttk.TTkHelper.overlay(None, winKey, 10, 4, toolWindow=True)
wkb.clicked.connect(_showWinKey)
table.setFocus()
root.mainloop()

82
tests/timeit/02.array.05.sort.py

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2024 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
import argparse
import operator
import json
import timeit
import random
sys.path.append(os.path.join(sys.path[0],'../..'))
l1 =[ random.randint(0x100,0x20000) for _ in range(10000) ]
l2 =[ random.randint(0x100,0x20000) for _ in range(10000) ] + ['b']
def _k1(x): return x
def _k2(x): return str(x)
def test_ti_1_01(): sorted(l1)
def test_ti_1_02(): sorted(l1, key=lambda x: x)
def test_ti_1_03(): sorted(l1, key=lambda x:str(x))
def test_ti_1_04(): sorted(l1, key=_k1)
def test_ti_1_05(): sorted(l1, key=_k2)
def test_ti_1_06():
try:
sorted(l1)
except TypeError as _:
sorted(l1, key=lambda x:str(x))
def test_ti_1_07():
try:
sorted(l1)
except TypeError as _:
sorted(l1, key=_k2)
def test_ti_2_01(): sorted(l2, key=lambda x:str(x))
def test_ti_2_02(): sorted(l2, key=_k2)
def test_ti_2_03():
try:
sorted(l2)
except TypeError as _:
sorted(l2, key=lambda x:str(x))
def test_ti_2_04():
try:
sorted(l2)
except TypeError as _:
sorted(l2, key=_k2)
loop = 300
a = {}
for testName in sorted([tn for tn in globals() if tn.startswith('test_ti_')]):
result = timeit.timeit(f'{testName}(*a)', globals=globals(), number=loop)
# print(f"test{iii}) fps {loop / result :.3f} - s {result / loop:.10f} - {result / loop} {globals()[testName](*a)}")
print(f"{testName} | {result / loop:.10f} sec. | {loop / result : 15.3f} Fps ╞╡-> {globals()[testName](*a)}")
Loading…
Cancel
Save