Browse Source

TTkTable: Added Edit cells

pull/272/head
Eugenio Parodi 2 years ago
parent
commit
1d54180cbb
  1. 14
      TermTk/TTkAbstract/abstracttablemodel.py
  2. 158
      TermTk/TTkWidgets/TTkModelView/tablewidget.py
  3. 35
      TermTk/TTkWidgets/TTkPickers/textpicker.py
  4. 13
      TermTk/TTkWidgets/spinbox.py
  5. 2
      TermTk/TTkWidgets/texedit.py
  6. 6
      tests/t.ui/test.ui.032.table.05.py
  7. 357
      tests/t.ui/test.ui.032.table.06.py
  8. 14
      tests/timeit/02.array.04.modify.py
  9. 74
      tests/timeit/30.rendering.01.TTkTable.py
  10. 2
      tools/dumbPaintTool/app/paintarea.py

14
TermTk/TTkAbstract/abstracttablemodel.py

@ -41,9 +41,21 @@ class TTkAbstractTableModel():
def columnCount(self) -> int:
raise NotImplementedError()
def data(self, row:int, col:int) -> TTkString:
def data(self, row:int, col:int) -> object:
return TTkString()
def ttkStringData(self, row:int, col:int) -> TTkString:
data = self.data(row,col)
if isinstance(data,TTkString):
return data
elif type(data) == str:
return TTkString(data)
else:
return TTkString(str(data))
def setData(self, row:int, col:int, data:object):
raise NotImplementedError()
def headerData(self, pos:int, orientation:TTkK.Direction) -> TTkString:
if orientation==TTkK.HORIZONTAL:
return TTkString(str(pos))

158
TermTk/TTkWidgets/TTkModelView/tablewidget.py

@ -28,10 +28,17 @@ from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.color import TTkColor
# from TermTk.TTkWidgets.TTkModelView.tablewidgetitem import TTkTableWidgetItem
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkGui.textdocument import TTkTextDocument
from TermTk.TTkWidgets.texedit import TTkTextEdit
from TermTk.TTkWidgets.lineedit import TTkLineEdit
from TermTk.TTkWidgets.spinbox import TTkSpinBox
from TermTk.TTkWidgets.TTkPickers.textpicker import TTkTextPicker, TTkTextDialogPicker
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
class _DefaultTableModel(TTkAbstractTableModel):
def __init__(self, **args):
@ -80,8 +87,8 @@ class TTkTableWidget(TTkAbstractScrollView):
'color': TTkColor.RST,
'lineColor': TTkColor.fg("#444444"),
'headerColor': TTkColor.fg("#FFFFFF")+TTkColor.bg("#444444")+TTkColor.BOLD,
'hoverColor': TTkColor.fg("#4444FF")+TTkColor.bg("#AAAA44")+TTkColor.BOLD,
'selectedColor': TTkColor.bg("#888800"),
'hoverColor': TTkColor.fg("#FFFF00")+TTkColor.bg("#0088AA")+TTkColor.BOLD,
'selectedColor': TTkColor.bg("#0066AA"),
'separatorColor': TTkColor.fg("#555555")+TTkColor.bg("#444444")},
'disabled': {
'color': TTkColor.fg("#888888"),
@ -101,6 +108,7 @@ class TTkTableWidget(TTkAbstractScrollView):
'_selected', '_hSeparatorSelected', '_vSeparatorSelected',
'_hoverPos', '_dragPos',
'_sortColumn', '_sortOrder',
'_fastCheck', '_guessDataEdit',
# Signals
# 'itemChanged', 'itemClicked', 'itemDoubleClicked', 'itemExpanded', 'itemCollapsed', 'itemActivated'
)
@ -116,6 +124,8 @@ class TTkTableWidget(TTkAbstractScrollView):
# self.itemDoubleClicked = pyTTkSignal(TTkTableWidgetItem, int)
# self.itemExpanded = pyTTkSignal(TTkTableWidgetItem)
# self.itemCollapsed = pyTTkSignal(TTkTableWidgetItem)
self._fastCheck = True
self._guessDataEdit = True
self._showHSeparators = vSeparator
self._showVSeparators = hSeparator
@ -130,22 +140,31 @@ class TTkTableWidget(TTkAbstractScrollView):
self._sortColumn = -1
self._sortOrder = TTkK.AscendingOrder
self._tableModel = tableModel if tableModel else _DefaultTableModel()
self._refreshLayout()
super().__init__(**kwargs)
self._refreshLayout()
self.setMinimumHeight(1)
self.setFocusPolicy(TTkK.ClickFocus)
# self._rootItem = TTkTableWidgetItem(expanded=True)
# self.clear()
self.setPadding(1,0,0,0)
self.viewChanged.connect(self._viewChangedHandler)
self._verticalHeader.visibilityUpdated.connect(lambda:self.viewChanged.emit())
self._horizontallHeader.visibilityUpdated.connect(lambda:self.viewChanged.emit())
self._verticalHeader.visibilityUpdated.connect( self._headerVisibilityChanged)
self._horizontallHeader.visibilityUpdated.connect(self._headerVisibilityChanged)
@pyTTkSlot()
def _headerVisibilityChanged(self):
showVH = self._verticalHeader.isVisible()
showHH = self._horizontallHeader.isVisible()
vhs = self._vHeaderSize if showVH else 0
hhs = self._hHeaderSize if showHH else 0
self.setPadding(hhs,0,vhs,0)
self.viewChanged.emit()
def _refreshLayout(self):
rows = self._tableModel.rowCount()
cols = self._tableModel.columnCount()
self._vHeaderSize = vhs= 1+max(len(self._tableModel.headerData(_p, TTkK.VERTICAL)) for _p in range(rows) )
self._hHeaderSize = hhs= 1
self._vHeaderSize = vhs = 1+max(len(self._tableModel.headerData(_p, TTkK.VERTICAL)) for _p in range(rows) )
self._hHeaderSize = hhs = 1
self.setPadding(hhs,0,vhs,0)
if self._showVSeparators:
self._colsPos = [(1+x)*11 for x in range(cols)]
else:
@ -197,7 +216,6 @@ class TTkTableWidget(TTkAbstractScrollView):
def setHSeparatorVisibility(self, visibility:bool):
if self._showHSeparators == visibility: return
self._showHSeparators = visibility
if visibility:
self._rowsPos = [v+i for i,v in enumerate(self._rowsPos,1)]
else:
@ -250,12 +268,16 @@ class TTkTableWidget(TTkAbstractScrollView):
def _columnContentsSize(self, column:int) -> int:
def _wid(_c):
txt = self._tableModel.data(_c, column)
if isinstance(txt,TTkString): pass
elif type(txt) == str: txt = TTkString(txt)
else: txt = TTkString(f"{txt}")
txt = self._tableModel.ttkStringData(_c, column)
return max(t.termWidth() for t in txt.split('\n'))
return max(_wid(i) for i in range(self._tableModel.rowCount()))
rows = self._tableModel.rowCount()
if self._fastCheck:
w,h = self.size()
row,_ = self._findCell(w//2, h//2, False)
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))
@pyTTkSlot(int)
def resizeColumnToContents(self, column:int) -> None:
@ -289,12 +311,16 @@ class TTkTableWidget(TTkAbstractScrollView):
def _rowContentsSize(self, row:int) -> int:
def _hei(_c):
txt = self._tableModel.data(row, _c)
if isinstance(txt,TTkString): pass
elif type(txt) == str: txt = TTkString(txt)
else: txt = TTkString(f"{txt}")
txt = self._tableModel.ttkStringData(row, _c)
return len(txt.split('\n'))
return max(_hei(i) for i in range(self._tableModel.columnCount()))
cols = self._tableModel.columnCount()
if self._fastCheck:
w,h = self.size()
_,col = self._findCell(w//2, h//2, False)
cola,colb = max(0,col-30), min(col+30,cols)
else:
cola,colb = 0,cols
return max(_hei(i) for i in range(cola,colb))
@pyTTkSlot(int)
def resizeRowToContents(self, row:int) -> None:
@ -341,6 +367,75 @@ class TTkTableWidget(TTkAbstractScrollView):
return row,col
def _editStr(self, x,y,w,h, row, col, data):
_te = TTkTextEdit(
parent=self, pos=(x, y), size=(w,h),
readOnly=False, wrapMode=TTkK.NoWrap)
_te.setText(data)
_te.setFocus()
@pyTTkSlot(bool)
def _processClose(change):
if change:
self.focusChanged.disconnect(_processClose)
txt = _te.toPlainText()
self._tableModel.setData(row,col,txt)
self.update()
_te.close()
self.focusChanged.connect(_processClose)
_dataEditType = {
str : '',
TTkString : ''
}
def _editNum(self, x,y,w,h, row, col, data):
_sb = TTkSpinBox(
parent=self, pos=(x, y), size=(w,1),
minimum=-1000000, maximum=1000000,
value=data)
_sb.setFocus()
@pyTTkSlot(bool)
def _processClose(change):
if change:
self.focusChanged.disconnect(_processClose)
val = _sb.value()
self._tableModel.setData(row,col,val)
self.update()
_sb.close()
self.focusChanged.connect(_processClose)
_dataEditType = {
str : '',
TTkString : ''
}
def _editTTkString(self, x,y,w,h, row, col, data):
_tp = TTkTextPicker(
parent=self, pos=(x, y), size=(w,h),
text=data, autoSize=False, wrapMode=TTkK.NoWrap)
_tp.setFocus()
@pyTTkSlot(bool)
def _processClose(change):
if change:
self.focusChanged.disconnect(_processClose)
txt = _tp.getTTkString()
self._tableModel.setData(row,col,txt)
self.update()
_tp.close()
self.focusChanged.connect(_processClose)
_dataEditType = {
str : '',
TTkString : ''
}
def mouseDoubleClickEvent(self, evt):
x,y = evt.x, evt.y
ox, oy = self.getViewOffsets()
@ -354,6 +449,9 @@ class TTkTableWidget(TTkAbstractScrollView):
self._hSeparatorSelected = None
self._vSeparatorSelected = None
rp = self._rowsPos
cp = self._colsPos
# Handle Header Events
# And return if handled
# This is important to handle the header selection in the next part
@ -374,6 +472,19 @@ class TTkTableWidget(TTkAbstractScrollView):
self.resizeRowToContents(i)
return True
row,col = self._findCell(x,y, headers=False)
xa,xb = 1+cp[col-1] if col>0 else 0, cp[col] + (0 if showVS else 1)
ya,yb = 1+rp[row-1] if row>0 else 0, rp[row] + (0 if showHS else 1)
data = self._tableModel.data(row, col)
if type(data) is str:
self._editStr(xa,ya,xb-xa,yb-ya,row,col,data)
elif type(data) in [int,float]:
self._editNum(xa,ya,xb-xa,yb-ya,row,col,data)
else:
data = self._tableModel.ttkStringData(row, col)
self._editTTkString(xa,ya,xb-xa,yb-ya,row,col,data)
return True
def mouseMoveEvent(self, evt) -> bool:
@ -694,10 +805,7 @@ class TTkTableWidget(TTkAbstractScrollView):
_cellsCache.append([row,col,xa,xb,ya,yb,cellColor])
def _drawCellContent(_col,_row,_xa,_xb,_ya,_yb,_color):
txt = self._tableModel.data(_row, _col)
if isinstance(txt,TTkString): pass
elif type(txt) == str: txt = TTkString(txt, _color)
else: txt = TTkString(f"{txt}", _color)
txt = self._tableModel.ttkStringData(_row, _col)
if _color != TTkColor.RST:
txt = txt.completeColor(_color)
for i,line in enumerate(txt.split('\n')):

35
TermTk/TTkWidgets/TTkPickers/textpicker.py

@ -137,11 +137,10 @@ class _emojiPicker(TTkResizableFrame):
self.emojiClicked = epa.viewport().emojiClicked
class TTkTextDialogPicker(TTkWindow):
__slots__ = ('_textEdit', '_autoSize', '_multiLine')
def __init__(self, *args, **kwargs):
self._autoSize = kwargs.get('autoSize',False)
self._multiLine = kwargs.get('multiLine',True)
super().__init__(*args, **kwargs)
__slots__ = ('_textEdit', '_autoSize')
def __init__(self, *, autoSize=False, multiLine=True, wrapMode=TTkK.WidgetWidth, **kwargs):
self._autoSize = autoSize
super().__init__(**kwargs)
fontLayout = TTkGridLayout(columnMinWidth=1)
# Char Fg/Bg buttons
fontLayout.addWidget(cb_fg := TTkCheckbox(text=" FG"),0,0)
@ -160,9 +159,9 @@ class TTkTextDialogPicker(TTkWindow):
fontLayout.addWidget(_superSimpleHorizontalLine(),0,10,2,1)
self._textEdit = TTkTextEdit(document=kwargs.get('document',TTkTextDocument()),multiLine=self._multiLine)
self._textEdit = TTkTextEdit(document=kwargs.get('document',TTkTextDocument()),multiLine=multiLine)
self._textEdit.setReadOnly(False)
self._textEdit.setLineWrapMode(TTkK.WidgetWidth)
self._textEdit.setLineWrapMode(wrapMode)
self._textEdit.setLineNumber('\n' in self._textEdit.toPlainText())
@pyTTkSlot()
@ -264,16 +263,15 @@ class TTkTextPicker(TTkContainer):
Do not use it unless you know what you are doing
And I've no idea what I am doing
'''
__slots__ = ('_teButton','_textEdit', 'documentViewChanged', 'textChanged', '_autoSize', '_multiLine')
def __init__(self, *args, **kwargs):
__slots__ = ('_teButton','_textEdit', 'documentViewChanged', 'textChanged', '_autoSize')
def __init__(self, *, text='', autoSize=False, multiLine=True, wrapMode=TTkK.WidgetWidth, **kwargs):
self.documentViewChanged = pyTTkSignal(int,int)
self._autoSize = kwargs.get('autoSize',False)
self._multiLine = kwargs.get('multiLine',True)
super().__init__(*args, **kwargs|{'layout':TTkHBoxLayout()})
self._textEdit = TTkTextEdit(pos=(0,0), size=(self.width()-2,self.height()),multiLine=self._multiLine)
self._textEdit.setText(kwargs.get('text',''))
self._autoSize = autoSize
super().__init__(**kwargs|{'layout':TTkHBoxLayout()})
self._textEdit = TTkTextEdit(pos=(0,0), size=(self.width()-2,self.height()),multiLine=multiLine)
self._textEdit.setText(text)
self._textEdit.setReadOnly(False)
self._textEdit.setLineWrapMode(TTkK.WidgetWidth)
self._textEdit.setLineWrapMode(wrapMode)
self.textChanged = self._textEdit.textChanged
self._teButton = TTkButton(border=True, text='', borderColor=TTkColor.fg("#AAAAFF")+TTkColor.bg("#002244") ,
pos=(self.width()-2,0),
@ -284,7 +282,9 @@ class TTkTextPicker(TTkContainer):
@pyTTkSlot()
def _showTextDialogPicker():
w,h = self.size()
tdp = TTkTextDialogPicker(size=(50,8+h), document=self._textEdit.document(), autoSize=self._autoSize, multiLine=self._multiLine)
tdp = TTkTextDialogPicker(size=(50,8+h),
document=self._textEdit.document(),
autoSize=autoSize, multiLine=multiLine, wrapMode=wrapMode)
TTkHelper.overlay(self, tdp, -1, -7, modal=True)
tdp.focusTextEdit()
@ -292,6 +292,9 @@ class TTkTextPicker(TTkContainer):
self._textEdit.viewport().viewChanged.connect(self._textPickerViewChanged)
def setFocus(self):
return self._textEdit.setFocus()
def getTTkString(self):
return self._textEdit.toRawText()

13
TermTk/TTkWidgets/spinbox.py

@ -100,9 +100,20 @@ class TTkSpinBox(TTkContainer):
self._maximum = maximum
self.setValue(self._value)
@staticmethod
def _isFloat(num):
try:
float(str(num))
return True
except:
return False
@pyTTkSlot(str)
def _textEdited(self, text):
self.setValue(int(str(text)))
if self._isFloat(text):
self.setValue(float(str(text)))
else:
self.setValue(int(str(text)))
self._lineEdit.setText(str(self._value))
def wheelEvent(self, evt):

2
TermTk/TTkWidgets/texedit.py

@ -148,7 +148,7 @@ class TTkTextEditView(TTkAbstractScrollView):
self.undoAvailable = pyTTkSignal(bool)
self.redoAvailable = pyTTkSignal(bool)
self.textChanged = pyTTkSignal()
self._readOnly = kwargs.get('readOnly', True)
self._readOnly = kwargs.get('readOnly', False)
self._multiLine = kwargs.get('multiLine', True)
self._multiCursor = True
self._hsize = 0

6
tests/t.ui/test.ui.032.table.05.py

@ -107,6 +107,7 @@ class MyTableModel(ttk.TTkAbstractTableModel):
def rowCount(self): return len(self.mylist) if not self.size else self.size[0]
def columnCount(self): return len(self.mylist[0]) if not self.size else self.size[1]
def data(self, row, col): return self.mylist[row][col]
def setData(self, row, col, data): self.mylist[row][col] = data
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',]
@ -138,7 +139,7 @@ data_list1 = [
[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(1000)]
for x in range(5000)]
data_list2 = [
[txt1,txt2,txt3]+
@ -158,6 +159,9 @@ 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)]
for x in range(30)]

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

@ -0,0 +1,357 @@
#!/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.TTkAbstractTableModel):
def __init__(self, mylist, size=None):
self.mylist = mylist
self.size=size
super().__init__()
def rowCount(self): return len(self.mylist) if not self.size else self.size[0]
def columnCount(self): return len(self.mylist[0]) if not self.size else self.size[1]
def data(self, row, col): return self.mylist[row][col]
def setData(self, row, col, data): self.mylist[row][col] = data
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)
class MyTableModelCSV(ttk.TTkAbstractTableModel):
def __init__(self, fd):
self.mylist = []
self.hheader = []
self.vheader = []
self._csvImport(fd)
super().__init__()
def _csvImport(self, fd):
sniffer = csv.Sniffer()
has_header = sniffer.has_header(fd.read(2048))
fd.seek(0)
csvreader = csv.reader(fd)
for row in csvreader:
self.mylist.append(row)
if has_header:
self.hheader = self.mylist.pop(0)
# check if the first column include an index:
if self._checkIndexColumn():
self.hheader.pop(0)
for l in self.mylist:
self.vheader.append(l.pop(0))
def _checkIndexColumn(self):
if all(l[0].isdigit() for l in self.mylist):
num = int(self.mylist[0][0])
return all(num+i==int(l[0]) for i,l in enumerate(self.mylist))
return False
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 setData(self, row, col, data): self.mylist[row][col] = data
def headerData(self, num, orientation):
if orientation == ttk.TTkK.HORIZONTAL:
if self.hheader:
return self.hheader[num]
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:
if self.vheader:
return self.vheader[num]
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),1)
table.setSelection((3,0),(1,2),1)
table.setSelection((1,3),(2,4),1)
table.setSelection((2,5),(2,4),1)
table.setSelection((0,9),(2,3),1)
table.setSelection((1,59),(1,2),1)
table.setSelection((3,59),(1,2),1)
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=(15,1), text=' Top ',checked=True)
hl = ttk.TTkCheckbox(parent=controls, pos=(10,1), size=(15,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 = MyTableModelCSV(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())
# 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()

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

@ -48,7 +48,19 @@ def test_ti_3_1():
array1 = [[True]*1000 for _ in range(1000)]
return True
loop = 1000
def test_ti_4_1():
array1 = [[True]*10000 for _ in range(10000)]
return True
def test_ti_5_1():
array1 = [[True]*100 for _ in range(100000)]
return True
def test_ti_6_1():
array1 = [[True]*11 for _ in range(2000000)]
return True
loop = 10
a = {}
# while (testName := f'test{iii}') and (testName in globals()):

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

@ -90,55 +90,31 @@ def resize1():
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]()
def test_ti_A_00_200x50(): return [t.resize(200,50) for t in tables]
def test_ti_A_01(): return paint(tables[0])
def test_ti_A_02(): return paint(tables[1])
def test_ti_A_03(): return paint(tables[2])
def test_ti_A_04(): return paint(tables[3])
def test_ti_A_05(): return paint(tables[4])
def test_ti_A_06(): return paint(tables[5])
def test_ti_A_07(): return paint(tables[6])
def test_ti_A_08(): return paint(tables[7])
def test_ti_A_09(): return paint(tables[8])
def test_ti_A_10(): return paint(tables[9])
def test_ti_A_11(): return paint(tables[10])
def test_ti_B_00_500x200(): return [t.resize(500,200) for t in tables]
def test_ti_B_01(): return paint(tables[0])
def test_ti_B_02(): return paint(tables[1])
def test_ti_B_03(): return paint(tables[2])
def test_ti_B_04(): return paint(tables[3])
def test_ti_B_05(): return paint(tables[4])
def test_ti_B_06(): return paint(tables[5])
def test_ti_B_07(): return paint(tables[6])
def test_ti_B_08(): return paint(tables[7])
def test_ti_B_09(): return paint(tables[8])
def test_ti_B_10(): return paint(tables[9])
def test_ti_B_11(): return paint(tables[10])
for t in tables: t.resize(500,200)

2
tools/dumbPaintTool/app/paintarea.py

@ -117,7 +117,7 @@ class PaintArea(ttk.TTkAbstractScrollView):
def viewFullAreaSize(self) -> tuple[int,int]:
_,_,w,h = self._getGeometry()
return w,h
return w+1,h+1
def viewDisplayedSize(self) -> tuple:
return self.size()

Loading…
Cancel
Save