Browse Source

TTkTble, added rows,cols,all selection

pull/272/head
Eugenio Parodi 2 years ago
parent
commit
45cc1c2e99
  1. 153
      TermTk/TTkWidgets/TTkModelView/tablewidget.py
  2. 4
      tests/t.ui/test.ui.032.table.03.py
  3. 58
      tests/timeit/02.array.03.check copy.py
  4. 58
      tests/timeit/02.array.04.modify.py
  5. 152
      tests/timeit/30.rendering.01.TTkTable.py

153
TermTk/TTkWidgets/TTkModelView/tablewidget.py

@ -233,6 +233,8 @@ class TTkTableWidget(TTkAbstractScrollView):
return super().leaveEvent(evt)
def _findCell(self, x, y):
showHS = self._showHSeparators
showVS = self._showVSeparators
showVH = self._verticalHeader.isVisible()
showHH = self._horizontallHeader.isVisible()
hhs = self._hHeaderSize if showHH else 0
@ -242,24 +244,28 @@ class TTkTableWidget(TTkAbstractScrollView):
rp = self._rowsPos
cp = self._colsPos
for row,py in enumerate(rp):
if py>y:
break
for col,px in enumerate(cp):
if px>x:
break
row = -1
col = -1
if y<0:
row = -1
else:
y += 0 if showHS else 0
for row,py in enumerate(rp):
if py>=y:
break
if x<0:
col = -1
else:
x += 0 if showVS else 0
for col,px in enumerate(cp):
if px>=x:
break
return row,col
def mouseMoveEvent(self, evt) -> bool:
showVH = self._verticalHeader.isVisible()
showHH = self._horizontallHeader.isVisible()
hhs = self._hHeaderSize if showHH else 0
vhs = self._vHeaderSize if showVH else 0
x,y = evt.x,evt.y
self._hoverPos = None
if x<vhs or y<hhs:
self.update()
return True
self._hoverPos = self._findCell(x,y)
self.update()
return True
@ -267,6 +273,8 @@ class TTkTableWidget(TTkAbstractScrollView):
def mousePressEvent(self, evt) -> bool:
x,y = evt.x, evt.y
ox, oy = self.getViewOffsets()
showHS = self._showHSeparators
showVS = self._showVSeparators
showVH = self._verticalHeader.isVisible()
showHH = self._horizontallHeader.isVisible()
hhs = self._hHeaderSize if showHH else 0
@ -276,44 +284,66 @@ class TTkTableWidget(TTkAbstractScrollView):
self._vSeparatorSelected = None
# Handle Header Events
if y < hhs:
x += ox-vhs
# And return if handled
# This is important to handle the header selection in the next part
if showVS and y < hhs:
_x = x+ox-vhs
for i, c in enumerate(self._colsPos):
if x == c:
if _x == c:
# I-th separator selected
self._hSeparatorSelected = i
self.update()
break
elif x < c:
return True
elif _x < c:
# # I-th header selected
# order = not self._sortOrder if self._sortColumn == i else TTkK.AscendingOrder
# self.sortItems(i, order)
break
return True
elif x < vhs:
y += oy-hhs
# return True
elif showHS and x < vhs:
_y = y+oy-hhs
for i, r in enumerate(self._rowsPos):
if y == r:
if _y == r:
# I-th separator selected
self._vSeparatorSelected = i
self.update()
break
elif y < r:
return True
elif _y < r:
# # I-th header selected
# order = not self._sortOrder if self._sortColumn == i else TTkK.AscendingOrder
# self.sortItems(i, order)
break
return True
else:
row,col = self._findCell(x,y)
# return True
row,col = self._findCell(x,y)
if not row==col==-1:
self._dragPos = [(row,col),(row,col)]
if evt.mod==TTkK.ControlModifier:
self._selected[row][col] = not self._selected[row][col]
else:
self._selected[row][col] = True
self._hoverPos = None
self.update()
return True
rows = self._tableModel.rowCount()
cols = self._tableModel.columnCount()
_ctrl = evt.mod==TTkK.ControlModifier
if row==col==-1:
# Corner Press
# Select Everything
self._selected = [[True]*cols for _ in range(rows)]
elif col==-1:
# Row select
state = all(self._selected[row])
if not _ctrl:
self._selected = [[False]*cols for _ in range(rows)]
self._selected[row] = [not state]*cols
elif row==-1:
# Col select
state = all(line[col] for line in self._selected)
if not _ctrl:
self._selected = [[False]*cols for _ in range(rows)]
for line in self._selected:
line[col] = not state
else:
# Cell Select
if _ctrl: self._selected[row][col] = not self._selected[row][col]
else: self._selected[row][col] = True
self._hoverPos = None
self.update()
return True
def mouseDragEvent(self, evt) -> bool:
@ -377,14 +407,28 @@ class TTkTableWidget(TTkAbstractScrollView):
cols = self._tableModel.columnCount()
state = True
(rowa,cola),(rowb,colb) = self._dragPos
if evt.mod==TTkK.ControlModifier:
state = self._selected[rowa][cola]
# Pick the status to be applied to the selection if CTRL is Pressed
# In case of line/row selection I choose the element 0 of that line
state = self._selected[max(0,rowa)][max(0,cola)]
else:
# Clear the selection if no ctrl has been pressed
self._selected = [[False]*cols for _ in range(rows)]
cola,colb=min(cola,colb),max(cola,colb)
rowa,rowb=min(rowa,rowb),max(rowa,rowb)
if rowa == -1:
cola,colb=min(cola,colb),max(cola,colb)
rowa,rowb=0,rows
elif cola == -1:
rowa,rowb=min(rowa,rowb),max(rowa,rowb)
cola,colb=0,cols
else:
cola,colb=min(cola,colb),max(cola,colb)
rowa,rowb=min(rowa,rowb),max(rowa,rowb)
for line in self._selected[rowa:rowb+1]:
line[cola:colb+1] = [state]*(colb-cola+1)
self._hoverPos = None
self._dragPos = None
self.update()
@ -507,7 +551,7 @@ class TTkTableWidget(TTkAbstractScrollView):
if xa>w : break
if xb<vhs: continue
cellColor = (
hoverColor if (row,col) == self._hoverPos else
hoverColor if self._hoverPos in [(row,col),(-1,col),(row,-1),(-1,-1)] else
selectedColor if self._selected[row][col] else
rowColor )
_colorCache2d[row-rowa][col-cola] = cellColor
@ -719,8 +763,14 @@ class TTkTableWidget(TTkAbstractScrollView):
if self._hoverPos:
row,col = self._hoverPos
ya,yb = sliceRow[row]
xa,xb = sliceCol[col]
if row == -1:
ya,yb = -1,rp[-1]
else:
ya,yb = sliceRow[row]
if col == -1:
xa,xb = -1,cp[-1]
else:
xa,xb = sliceCol[col]
if showVS:
xa,xb = xa+vhs-ox, xb+vhs-ox
@ -745,12 +795,23 @@ class TTkTableWidget(TTkAbstractScrollView):
if self._dragPos:
(rowa,cola),(rowb,colb) = self._dragPos
cola,colb=min(cola,colb),max(cola,colb)
rowa,rowb=min(rowa,rowb),max(rowa,rowb)
xa = sliceCol[cola][0]-ox+vhs
xb = sliceCol[colb][1]-ox+vhs
ya = sliceRow[rowa][0]-oy+hhs
yb = sliceRow[rowb][1]-oy+hhs
if rowa == -1:
cola,colb = min(cola,colb),max(cola,colb)
xa = sliceCol[cola][0]-ox+vhs
xb = sliceCol[colb][1]-ox+vhs + (0 if showHS else 1)
ya,yb = -1-oy+hhs,rp[-1]-oy+hhs
elif cola == -1:
rowa,rowb = min(rowa,rowb),max(rowa,rowb)
ya = sliceRow[rowa][0]-oy+hhs
yb = sliceRow[rowb][1]-oy+hhs + (0 if showVS else 1)
xa,xb = -1-ox+vhs,cp[-1]-ox+vhs
else:
cola,colb = min(cola,colb),max(cola,colb)
rowa,rowb = min(rowa,rowb),max(rowa,rowb)
xa = sliceCol[cola][0]-ox+vhs
xb = sliceCol[colb][1]-ox+vhs + (0 if showHS else 1)
ya = sliceRow[rowa][0]-oy+hhs
yb = sliceRow[rowb][1]-oy+hhs + (0 if showVS else 1)
hoverColorInv = hoverColor.background().invertFgBg()
canvas.drawTTkString(pos=(xa,ya), text=TTkString(''+(''*(xb-xa-1))+'',hoverColorInv))

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

@ -230,4 +230,8 @@ table.setSelection((3,59),(1,2),1)
splitter.addWidget(ttk.TTkLogViewer(),size=10,title="LOGS")
winKey = ttk.TTkWindow(title="KeyPress",layout=ttk.TTkGridLayout(), size=(30,7))
winKey.layout().addWidget(ttk.TTkKeyPressView(maxHeight=3))
ttk.TTkHelper.overlay(None, winKey, 10, 4, toolWindow=True)
root.mainloop()

58
tests/timeit/02.array.03.check copy.py

@ -0,0 +1,58 @@
#!/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 timeit
array1 = [[True for _ in range(1000)] for _ in range(1000)]
array2 = [[False for _ in range(1000)] for _ in range(1000)]
array3 = [[x==y==999 for x in range(1000)] for y in range(1000)]
array4 = [[not (x==y==999) for x in range(1000)] for y in range(1000)]
array5 = [[x==y==500 for x in range(1000)] for y in range(1000)]
array6 = [[not (x==y==500) for x in range(1000)] for y in range(1000)]
def test1(): return all(all(r) for r in array1)
def test2(): return all(all(r) for r in array2)
def test3(): return all(all(r) for r in array3)
def test4(): return all(all(r) for r in array4)
def test5(): return all(all(r) for r in array5)
def test6(): return all(all(r) for r in array6)
def test7(): return any(any(r) for r in array1)
def test8(): return any(any(r) for r in array2)
def test9(): return any(any(r) for r in array3)
def test10(): return any(any(r) for r in array4)
def test11(): return any(any(r) for r in array5)
def test12(): return any(any(r) for r in array6)
loop = 100
a = {}
iii = 1
while (testName := f'test{iii}') and (testName in globals()):
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"test{iii:02}) | {result / loop:.10f} sec. | {loop / result : 15.3f} Fps ╞╡-> {globals()[testName](*a)}")
iii+=1

58
tests/timeit/02.array.04.modify.py

@ -0,0 +1,58 @@
#!/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 timeit
array1 = [[True for _ in range(1000)] for _ in range(1000)]
array2 = [[False for _ in range(1000)] for _ in range(1000)]
array3 = [[x==y==999 for x in range(1000)] for y in range(1000)]
array4 = [[not (x==y==999) for x in range(1000)] for y in range(1000)]
array5 = [[x==y==500 for x in range(1000)] for y in range(1000)]
array6 = [[not (x==y==500) for x in range(1000)] for y in range(1000)]
def test_ti_1_1():
array1[500] = [True]*1000
return True
def test_ti_1_2():
array1[500][0:1000] = [True]*1000
return True
def test_ti_2_1():
for line in array1[400:600]:
line[400:600] = [True]*(600-400)
return True
def test_ti_3_1():
array1 = [[True]*1000 for _ in range(1000)]
return True
loop = 1000
a = {}
# while (testName := f'test{iii}') and (testName in globals()):
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)}")

152
tests/timeit/30.rendering.01.TTkTable.py

@ -0,0 +1,152 @@
#!/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
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
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
pepper = ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['pepper'])
python = ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['python'])
fire = ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['fire'])
fireMini = ttk.TTkUtil.base64_deflate_2_obj(d['compressed']['fireMini'])
img_pepper = ttk.TTkString(pepper)
img_python = ttk.TTkString(python)
img_fire = ttk.TTkString(fire)
img_fireMini = ttk.TTkString(fireMini)
class MyTableModel(ttk.TTkAbstractTableModel):
def __init__(self, mylist, *args):
super().__init__(*args)
self.mylist = mylist
def rowCount(self): return len(self.mylist)
def columnCount(self): return len(self.mylist[0])
def data(self, row, col): return self.mylist[row][col]
def headerData(self, num, orientation):
prefix = ['aa','bb','cc','dd','ee','ff','gg','Euge']
return f"{prefix[num%len(prefix)]}:{num:03}"
# use numbers for numeric data to sort properly
txt1 = "Text"
txt2 = txt1*10
txt3 = '\n'.join([txt1*2]*5)
txt4 = '\n'.join([txt1*5]*10)
txt5 = ttk.TTkString(txt4, ttk.TTkColor.RED + ttk.TTkColor.BG_BLUE)
data_lists = [
[[txt1 for y in range(100)] for x in range(1000)],
[[txt2 for y in range(100)] for x in range(1000)],
[[txt3 for y in range(100)] for x in range(1000)],
[[txt4 for y in range(100)] for x in range(1000)],
[[txt5 for y in range(100)] for x in range(1000)],
[[1234567 for y in range(100)] for x in range(1000)],
[[1234567.123456 for y in range(100)] for x in range(1000)],
[[img_fireMini for y in range(100)] for x in range(1000)],
[[img_python for y in range(100)] for x in range(1000)],
[[fireMini for y in range(100)] for x in range(1000)],
[[python for y in range(100)] for x in range(1000)]]
table_models = [MyTableModel(dl) for dl in data_lists]
tables = [ttk.TTkTableWidget(tableModel=tm) for tm in table_models]
def paint(table):
canvas = table.getCanvas()
return table.paintEvent(canvas)
def resize1():
[t.resize(200,50) for t in tables]
def resize2():
[t.resize(500,200) for t in tables]
tests = [
lambda : [t.resize(200,50) for t in tables],
lambda : paint(tables[0]),
lambda : paint(tables[1]),
lambda : paint(tables[2]),
lambda : paint(tables[3]),
lambda : paint(tables[4]),
lambda : paint(tables[5]),
lambda : paint(tables[6]),
lambda : paint(tables[7]),
lambda : paint(tables[8]),
lambda : paint(tables[9]),
lambda : paint(tables[10]),
lambda : [t.resize(500,200) for t in tables],
lambda : paint(tables[0]),
lambda : paint(tables[1]),
lambda : paint(tables[2]),
lambda : paint(tables[3]),
lambda : paint(tables[4]),
lambda : paint(tables[5]),
lambda : paint(tables[6]),
lambda : paint(tables[7]),
lambda : paint(tables[8]),
lambda : paint(tables[9]),
lambda : paint(tables[10]) ]
def test_ti_01(): return tests[0]()
def test_ti_02(): return tests[1]()
def test_ti_03(): return tests[2]()
def test_ti_04(): return tests[3]()
def test_ti_05(): return tests[4]()
def test_ti_06(): return tests[5]()
def test_ti_07(): return tests[6]()
def test_ti_08(): return tests[7]()
def test_ti_09(): return tests[8]()
def test_ti_10(): return tests[9]()
def test_ti_11(): return tests[10]()
def test_ti_12(): return tests[11]()
def test_ti_13(): return tests[12]()
def test_ti_14(): return tests[13]()
def test_ti_15(): return tests[14]()
def test_ti_16(): return tests[15]()
def test_ti_17(): return tests[16]()
def test_ti_18(): return tests[17]()
def test_ti_19(): return tests[18]()
def test_ti_20(): return tests[19]()
def test_ti_21(): return tests[20]()
def test_ti_22(): return tests[21]()
for t in tables: t.resize(500,200)
loop = 10
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