Browse Source

feat(TableModel): add displayData method to allow cell text alignment (#474)

pull/481/head
Pier CeccoPierangioliEugenio 5 months ago committed by GitHub
parent
commit
dddfc58d30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 72
      libs/pyTermTk/TermTk/TTkAbstract/abstracttablemodel.py
  2. 2
      libs/pyTermTk/TermTk/TTkCore/canvas.py
  3. 10
      libs/pyTermTk/TermTk/TTkCore/string.py
  4. 16
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablewidget.py
  5. 72
      tests/t.ui/test.ui.032.table.11.pyside.align.01.py
  6. 433
      tests/t.ui/test.ui.032.table.13.alignment.01.py
  7. 108
      tests/t.ui/test.ui.032.table.13.alignment.02.overloading.py
  8. 95
      tests/t.ui/test.ui.032.table.13.alignment.03.mixin.py

72
libs/pyTermTk/TermTk/TTkAbstract/abstracttablemodel.py

@ -22,6 +22,8 @@
__all__ = ['TTkAbstractTableModel','TTkModelIndex']
from typing import Tuple,Any
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
@ -215,9 +217,19 @@ class TTkAbstractTableModel():
'''
return False
@staticmethod
def _dataToTTkString(data:Any) -> TTkString:
"""Convert arbitrary data to TTkString (passes through TTkString, wraps str, else str() coercion)."""
if isinstance(data,TTkString):
return data
elif isinstance(data,str):
return TTkString(data)
else:
return TTkString(str(data))
def ttkStringData(self, row:int, col:int) -> TTkString:
'''
Returns the :py:class:`TTkString` reprsents the ddata stored in the row/column.
Returns the :py:class:`TTkString` reprsents the data stored in the row/column.
:param row: the row position of the data
:type row: int
@ -227,12 +239,58 @@ class TTkAbstractTableModel():
:return: :py:class:`TTkString`
'''
data = self.data(row,col)
if isinstance(data,TTkString):
return data
elif type(data) == str:
return TTkString(data)
else:
return TTkString(str(data))
return TTkAbstractTableModel._dataToTTkString(data)
def displayData(self, row:int, col:int) -> Tuple[TTkString, TTkK.Alignment]:
'''
Returns the display representation and alignment for the data stored at the specified row/column position.
This method provides the formatted text representation of the cell data along with its preferred alignment
for display purposes in table widgets. It combines the functionality of :meth:`ttkStringData` for text
formatting with alignment information for proper cell rendering.
The default implementation returns the :py:class:`TTkString` representation of the data with left alignment.
Subclasses can override this method to provide custom formatting and alignment based on data type,
column purpose, or other display requirements.
**Usage Examples:**
- Numeric columns might return right alignment for better visual presentation
- Boolean columns might return center alignment with custom symbols
- Date columns might return formatted date strings with specific alignment
:param row: The row position of the data (0-based index)
:type row: int
:param col: The column position of the data (0-based index)
:type col: int
:return: A tuple containing the formatted text and its preferred alignment
:rtype: Tuple[:py:class:`TTkString`, :py:class:`TTkK.Alignment`]
**Example Implementation:**
.. code-block:: python
def displayData(self, row: int, col: int) -> Tuple[TTkString, TTkK.Alignment]:
data = self.data(row, col)
# Custom formatting based on column type
if isinstance(data, (int, float)):
# Right-align numeric data
return TTkString(f"{data:,.2f}"), TTkK.Alignment.RIGHT_ALIGN
elif isinstance(data, bool):
# Center-align boolean with custom symbols
symbol = "" if data else ""
return TTkString(symbol), TTkK.Alignment.CENTER_ALIGN
else:
# Default left-align for text
return self.ttkStringData(row, col), TTkK.Alignment.LEFT_ALIGN
'''
data = self.data(row,col)
retData = TTkAbstractTableModel._dataToTTkString(data)
if isinstance(data, (int,float)):
return retData, TTkK.Alignment.RIGHT_ALIGN
return retData, TTkK.Alignment.LEFT_ALIGN
def headerData(self, pos:int, orientation:TTkK.Direction) -> TTkString:
'''

2
libs/pyTermTk/TermTk/TTkCore/canvas.py

@ -212,7 +212,7 @@ class TTkCanvas():
self._set(y, x, char, color)
def drawTTkString(self, pos, text, width=None, color=TTkColor.RST, alignment=TTkK.NONE, forceColor=False):
def drawTTkString(self, pos, text:TTkString, width=None, color=TTkColor.RST, alignment=TTkK.NONE, forceColor=False):
'''
NOTE:
drawText is one of the most abused functions,

10
libs/pyTermTk/TermTk/TTkCore/string.py

@ -355,9 +355,13 @@ class TTkString():
ret._text = " " *p1 + self._text + " " *p2
ret._colors = [color]*p1 + self._colors + [color]*p2
elif alignment == TTkK.JUSTIFY:
# TODO: Text Justification
ret._text = self._text + " " *pad
ret._colors = self._colors + [color]*pad
if not (_slices := [_t for _t in self.split(" ") if _t._text]) or len(_slices) < 2:
return self
_avg_spaces = width - sum(_slice.termWidth() for _slice in _slices)
_avg_space = _avg_spaces // (len(_slices)-1)
_left_part = TTkString(' '*_avg_space).join(_slices[:-1])
_last_space = width - _left_part.termWidth() - _slices[-1].termWidth()
return _left_part + TTkString(' '*_last_space) + _slices[-1]
elif self._hasSpecialWidth is not None:
# Trim the string to a fixed size taking care of the variable width unicode chars
rt = ""

16
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablewidget.py

@ -1587,14 +1587,16 @@ class TTkTableWidget(TTkAbstractScrollView):
_cellsCache.append([row,col,xa,xb,ya,yb,cellColor])
def _drawCellContent(_col,_row,_xa,_xb,_ya,_yb,_color):
txt = self._tableModel.ttkStringData(_row, _col)
_txt, _align = self._tableModel.displayData(_row, _col)
if _color != TTkColor.RST:
txt = txt.completeColor(_color)
for i,line in enumerate(txt.split('\n')):
y = i+_ya
canvas.drawTTkString(pos=(_xa,y), text=line, width=_xb-_xa, color=_color)
if y >= _yb-1: break
canvas.fill(pos=(_xa,y+1),size=(_xb-_xa,_yb-y-1),color=_color)
_txt = _txt.completeColor(_color)
for _i,_line in enumerate(_txt.split('\n')):
_y = _i+_ya
_width=_xb-_xa
_line = _line.align(width=_width, color=_color, alignment=_align)
canvas.drawTTkString(pos=(_xa,_y), text=_line, width=_width, color=_color)
if _y >= _yb-1: break
canvas.fill(pos=(_xa,_y+1),size=(_xb-_xa,_yb-_y-1),color=_color)
def _drawCellBottom(_col,_row,_xa,_xb,_ya,_yb,cellColor):
if _yb>=h: return

72
tests/t.ui/test.ui.032.table.11.pyside.align.01.py

@ -0,0 +1,72 @@
#!/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.
from PySide6.QtWidgets import QApplication, QTableView
from PySide6.QtCore import Qt, QAbstractTableModel
class MyTableModel(QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def rowCount(self, parent=None):
return len(self._data)
def columnCount(self, parent=None):
return len(self._data[0])
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return None
value = self._data[index.row()][index.column()]
if role == Qt.DisplayRole:
return value
if role == Qt.TextAlignmentRole:
if index.column() == 0:
return Qt.AlignLeft | Qt.AlignTop
elif index.column() == 1:
return Qt.AlignCenter
elif index.column() == 2:
return Qt.AlignRight | Qt.AlignVCenter
return None
app = QApplication([])
data = [
["Left", "Center\npippo", "Right"],
["Apple", "Banana\npippo", "Cherry"],
["Dog", "Elephant\npippo", "Fox"]
]
model = MyTableModel(data)
view = QTableView()
view.setModel(model)
view.show()
app.exec()

433
tests/t.ui/test.ui.032.table.13.alignment.01.py

@ -0,0 +1,433 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2025 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
from typing import Tuple
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen (default)', action='store_true')
parser.add_argument('-w', help='Windowed', 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 = not args.w
mouseTrack = True
# 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__(data=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)
def flags(self, row: int, col: int) -> ttk.TTkConstant.ItemFlag:
if col==0:
return (
ttk.TTkK.ItemFlag.ItemIsEnabled |
ttk.TTkK.ItemFlag.ItemIsSelectable )
if col==1:
return (
ttk.TTkK.ItemFlag.ItemIsEnabled |
ttk.TTkK.ItemFlag.ItemIsEditable )
return super().flags(row, col)
def displayData(self, row:int, col:int) -> Tuple[ttk.TTkString, ttk.TTkK.Alignment]:
if 0 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.LEFT_ALIGN
if 1 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.CENTER_ALIGN
if 2 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.RIGHT_ALIGN
if 3 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.JUSTIFY
return super().displayData(row,col)
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"{y:03}\npippo\npeppo-ooo"]+[str(x) for x in range(10) ] for y in range(20)]
data_list1[3][3] = "abc\ndef\nghi\njkl"
data_list2 = [
[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_list3 = [
[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_list4 = [
[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,
sigmask=(
ttk.TTkTerm.Sigmask.CTRL_Q |
ttk.TTkTerm.Sigmask.CTRL_S |
ttk.TTkTerm.Sigmask.CTRL_C |
ttk.TTkTerm.Sigmask.CTRL_Z ))
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_model4 = MyTableModel(data_list4)
# table_model = MyTableModel(data_list, size=(15,10))
table = ttk.TTkTable(parent=splitter, tableModel=table_model1)
# table = ttk.TTkTable(parent=splitter)
# # 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=7,title="LOGS")
controls = ttk.TTkContainer()
debugView = 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")} }
#########################
# Define the Controls #
#########################
quitBtn = ttk.TTkButton(parent=controls, pos=(0,0), size=(5,6), text="Q\nU\nI\nT")
quitBtn.clicked.connect(root.quit)
offsetQuit = 6
# Header Checkboxes
ttk.TTkLabel(parent=controls, pos=(offsetQuit,0), text="Header:")
ht = ttk.TTkCheckbox(parent=controls, pos=( offsetQuit ,1), size=(8,1), text=' Top ',checked=True)
hl = ttk.TTkCheckbox(parent=controls, pos=( offsetQuit+9,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=(offsetQuit,2), text="Lines:")
vli = ttk.TTkCheckbox(parent=controls, pos=(offsetQuit ,3), size=(5,1), text=' V',checked=True)
hli = ttk.TTkCheckbox(parent=controls, pos=(offsetQuit+9,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=(offsetQuit+19,1), size=(11,1), text=' Theme 1', radiogroup='Themes', checked=True)
t2 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+19,2), size=(11,1), text=' Theme 2', radiogroup='Themes')
t3 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+19,3), size=(11,1), text=' Theme 3', radiogroup='Themes')
t4 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+19,4), size=(11,1), text=' Theme 4', radiogroup='Themes')
t5 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+19,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=(offsetQuit+32,0), size=(11,1), text=' Model 1', radiogroup='Models', checked=True)
m2 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+32,1), size=(11,1), text=' Model 2', radiogroup='Models')
m3 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+32,2), size=(11,1), text=' Model 3', radiogroup='Models')
m4 = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+32,3), size=(11,1), text=' Model 4', 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))
m4.clicked.connect(lambda : table.setModel(table_model4))
if args.csv:
table_model_csv = ttk.TTkTableModelCSV(fd=args.csv)
m_csv = ttk.TTkRadioButton(parent=controls, pos=(offsetQuit+32,4), size=(11,1), text=' CSV', radiogroup='Models')
m_csv.clicked.connect(lambda : table.setModel(table_model_csv))
# Resize Button
rcb = ttk.TTkButton(parent=controls, pos=(offsetQuit ,5), size=( 3,1), text="C", border=False)
rrb = ttk.TTkButton(parent=controls, pos=(offsetQuit+3,5), size=( 3,1), text="R", border=False)
rb = ttk.TTkButton(parent=controls, pos=(offsetQuit+6,5), size=(11,1), text="Resize", border=False)
rrb.clicked.connect(table.resizeRowsToContents)
rcb.clicked.connect(table.resizeColumnsToContents)
rb.clicked.connect( table.resizeRowsToContents)
rb.clicked.connect( table.resizeColumnsToContents)
cbs = ttk.TTkCheckbox(parent=controls, pos=(offsetQuit+32,5), size=(8,1), text='-Sort', checked=False)
cbs.toggled.connect(table.setSortingEnabled)
wtb = ttk.TTkButton(parent=controls, pos=(offsetQuit+41,4), size=( 4,1), text="👌", border=False)
wkb = ttk.TTkButton(parent=controls, pos=(offsetQuit+41,5), size=( 4,1), text="🤌", border=False)
btn_ins_row = ttk.TTkButton(parent=controls, pos=(offsetQuit+45,0), size=( 4,1), text="", border=False)
btn_del_row = ttk.TTkButton(parent=controls, pos=(offsetQuit+45,1), size=( 4,1), text="", border=False)
btn_ins_col = ttk.TTkButton(parent=controls, pos=(offsetQuit+45,3), size=( 4,1), text="", border=False)
btn_del_col = ttk.TTkButton(parent=controls, pos=(offsetQuit+45,4), size=( 4,1), text="", border=False)
#########################
# Define the DebugView #
#########################
debugViewLayout = ttk.TTkGridLayout()
debugView.setLayout(debugViewLayout)
debugViewLayout.addWidget(l_ch:=ttk.TTkLabel(text="Cell Changed: xxxx"),0,0)
debugViewLayout.addWidget(l_cl:=ttk.TTkLabel(text="Cell Clicked: xxxx"),1,0)
debugViewLayout.addWidget(l_dc:=ttk.TTkLabel(text="DoubleClked : xxxx"),2,0)
debugViewLayout.addWidget(l_ce:=ttk.TTkLabel(text="Cell Entered: xxxx"),3,0)
debugViewLayout.addWidget(l_cc:=ttk.TTkLabel(text="Changed: xxxx"),4,0)
debugViewLayout.addWidget(t_ed:=ttk.TTkTextEdit(lineNumber=True),0,1,6,1)
table.cellChanged.connect( lambda r,c: l_ch.setText(f"Cell Changed: {r,c}"))
table.cellClicked.connect( lambda r,c: l_cl.setText(f"Cell Clicked: {r,c}"))
table.cellEntered.connect( lambda r,c: l_ce.setText(f"Cell Entered: {r,c}"))
table.cellDoubleClicked.connect( lambda r,c: l_dc.setText(f"DoubleClked : {r,c}"))
table.currentCellChanged.connect(lambda cr,cc,pr,pc: l_cc.setText(f"Changed:{cr,cc}<-{pr,pc}"))
table.cellEntered.connect( lambda r,c: t_ed.setText(table.model().ttkStringData(r,c)))
controlAndLogsSplitter.addWidget(controls, size=55)
controlAndLogsSplitter.addWidget(debugView, size=60)
controlAndLogsSplitter.addWidget(ttk.TTkLogViewer())
@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)
@ttk.pyTTkSlot()
def _showWinText():
winText = ttk.TTkWindow(title="Notepad",layout=ttk.TTkGridLayout(), size=(80,7))
winText.layout().addWidget(ttk.TTkTextEdit(lineNumber=True, readOnly=False))
ttk.TTkHelper.overlay(None, winText, 50, 20, toolWindow=True)
@ttk.pyTTkSlot()
def _insertRows():
_model = table.model()
_model.insertRows(3,2)
@ttk.pyTTkSlot()
def _deleteRows():
_model = table.model()
_model.removeRows(5,5)
@ttk.pyTTkSlot()
def _insertCols():
_model = table.model()
_model.insertColumns(3,2)
@ttk.pyTTkSlot()
def _deleteCols():
_model = table.model()
_model.removeColumns(5,5)
btn_ins_row.clicked.connect(_insertRows)
btn_del_row.clicked.connect(_deleteRows)
btn_ins_col.clicked.connect(_insertCols)
btn_del_col.clicked.connect(_deleteCols)
wtb.clicked.connect(_showWinText)
table.setFocus()
root.mainloop()

108
tests/t.ui/test.ui.032.table.13.alignment.02.overloading.py

@ -0,0 +1,108 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2025 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
from typing import Tuple
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen (default)', action='store_true')
parser.add_argument('-w', help='Windowed', action='store_true')
args = parser.parse_args()
fullScreen = not args.w
mouseTrack = True
class MyTableMixin():
def headerData(self, num, orientation):
if orientation == ttk.TTkK.HORIZONTAL:
if 0 == num%4:
return f"{num} Left"
if 1 == num%4:
return f"{num} Center"
if 2 == num%4:
return f"{num} Right"
if 3 == num%4:
return f"{num} Justified"
return super().headerData(num, orientation)
def displayData(self:ttk.TTkTableModelList, row:int, col:int) -> Tuple[ttk.TTkString, ttk.TTkK.Alignment]:
data, legacy_align = super().displayData(row,col)
if 0 == col%4:
return data, ttk.TTkK.Alignment.LEFT_ALIGN
if 1 == col%4:
return data, ttk.TTkK.Alignment.CENTER_ALIGN
if 2 == col%4:
return data, ttk.TTkK.Alignment.RIGHT_ALIGN
if 3 == col%4:
return data, ttk.TTkK.Alignment.JUSTIFY
return data, legacy_align
class MyTableModel(MyTableMixin, ttk.TTkTableModelList):
def flags(self, row: int, col: int) -> ttk.TTkConstant.ItemFlag:
if col==0:
return (
ttk.TTkK.ItemFlag.ItemIsEnabled |
ttk.TTkK.ItemFlag.ItemIsSelectable )
if col==1:
return (
ttk.TTkK.ItemFlag.ItemIsEnabled |
ttk.TTkK.ItemFlag.ItemIsEditable )
return super().flags(row, col)
data_list1 = [[f"{y:03}\npippo\npeppo-ooo"]+[str(x) for x in range(10) ] for y in range(20)]
data_list1[1][1] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[2][2] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[3][3] = "abc def ghi\ndef ghi a b c de fgh s\nghi\njkl - pippo"
data_list1[4][4] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[5][5] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
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)
table_model1 = MyTableModel(data=data_list1)
table = ttk.TTkTable(parent=rootTable, tableModel=table_model1)
table.resizeRowsToContents()
table.resizeColumnsToContents()
root.mainloop()

95
tests/t.ui/test.ui.032.table.13.alignment.03.mixin.py

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2025 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
from typing import Tuple
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen (default)', action='store_true')
parser.add_argument('-w', help='Windowed', action='store_true')
args = parser.parse_args()
fullScreen = not args.w
mouseTrack = True
class MyTableModel(ttk.TTkTableModelList):
def headerData(self, num, orientation):
if orientation == ttk.TTkK.HORIZONTAL:
if 0 == num%4:
return f"{num} Left"
if 1 == num%4:
return f"{num} Center"
if 2 == num%4:
return f"{num} Right"
if 3 == num%4:
return f"{num} Justified"
return super().headerData(num, orientation)
def displayData(self:ttk.TTkTableModelList, row:int, col:int) -> Tuple[ttk.TTkString, ttk.TTkK.Alignment]:
if 0 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.LEFT_ALIGN
if 1 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.CENTER_ALIGN
if 2 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.RIGHT_ALIGN
if 3 == col%4:
return self.ttkStringData(row, col), ttk.TTkK.Alignment.JUSTIFY
return super().displayData(row,col)
data_list1 = [[f"{y:03}\npippo\npeppo-ooo"]+[str(x) for x in range(10) ] for y in range(20)]
data_list1[1][1] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[2][2] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[3][3] = "abc def ghi\ndef ghi a b c de fgh s\nghi\njkl - pippo"
data_list1[4][4] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
data_list1[5][5] = "abc def ghi\ndef ghi\nghi\njkl - pippo"
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)
table_model1 = MyTableModel(data=data_list1)
table = ttk.TTkTable(parent=rootTable, tableModel=table_model1)
table.resizeRowsToContents()
table.resizeColumnsToContents()
root.mainloop()
Loading…
Cancel
Save