From b37af10b757bccbdf49ba8ad50b852b541849bab Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Mon, 14 Oct 2024 23:07:45 +0100 Subject: [PATCH] Added table signals --- TermTk/TTkWidgets/TTkModelView/table.py | 317 ++++++++++++-- TermTk/TTkWidgets/TTkModelView/tablewidget.py | 98 ++++- tests/t.ui/test.ui.032.table.09.py | 391 ++++++++++++++++++ 3 files changed, 761 insertions(+), 45 deletions(-) create mode 100755 tests/t.ui/test.ui.032.table.09.py diff --git a/TermTk/TTkWidgets/TTkModelView/table.py b/TermTk/TTkWidgets/TTkModelView/table.py index 385111c7..70958c03 100644 --- a/TermTk/TTkWidgets/TTkModelView/table.py +++ b/TermTk/TTkWidgets/TTkModelView/table.py @@ -28,22 +28,245 @@ from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel class TTkTable(TTkAbstractScrollArea): + ''' + A :class:`TTkTable` implements a table view (:class:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget`) that displays items from a model. + + :: + + Customer Id ╿First Name ╿Last Name ╿Company ╿City ╿ + 1 │DD37Cf93aecA6Dc │Sheryl │Baxter │Rasmussen Group │East Leonard │ + ╾╌╌┼────────────────┼───────────┼────────────┼────────────────────────────────┼────────────────────┤ + 2 │1Ef7b82A4CAAD10 │Preston │Lozano │Vega-Gentry │East Jimmychester │ + ╾╌╌┼────────────────┼───────────┼────────────┼────────────────────────────────┼────────────────────┤ + 3 │6F94879bDAfE5a6 │Roy │Berry │Murillo-Perry │Isabelborough │ + ╾╌╌┼────────────────┼───────────┼────────────┼────────────────────────────────┼────────────────────┤ + 4 │5Cef8BFA16c5e3c │Linda │Olsen │Dominguez, Mcmillan and Donovan │Bensonview │ + ╾╌╌┼────────────────┼───────────┼────────────┼────────────────────────────────┼────────────────────┤ + 5 │053d585Ab6b3159 │Joanna │Bender │Martin, Lang and Andrade │West Priscilla │ + ╾╌╌┼────────────────┼───────────┼────────────┼────────────────────────────────┼────────────────────┤ + 6 │2d08FB17EE273F4 │Aimee │Downs │Steele Group │Chavezborough │ + ╾╌╌┴────────────────┴───────────┴────────────┴────────────────────────────────┴────────────────────┘ + + please refer to :class:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget` for a detailed descriptoin of all the available methods and init params + + +-----------------------------------------------------------------------------------------------+ + | `Signals `_ | + +-----------------------------------------------------------------------------------------------+ + + .. py:method:: cellChanged(row, col) + :signal: + + This signal is emitted whenever the data of the item in the cell specified by row and column has changed. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellClicked(row, col) + :signal: + + This signal is emitted whenever a cell in the table is clicked. + The row and column specified is the cell that was clicked. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellDoubleClicked(row, col) + :signal: + + This signal is emitted whenever a cell in the table is double clicked. + The row and column specified is the cell that was double clicked. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellEntered(row, col) + :signal: + + This signal is emitted when the mouse cursor enters a cell. + The cell is specified by row and column. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: currentCellChanged(currRow, currCol, prevRow, prevCol) + :signal: + + This signal is emitted whenever the current cell changes. + The cell specified by **prevRow** and **prevCol** is the cell that previously had the focus, + the cell specified by **currRow** and **currCol** is the new current cell. + + :param currRow: the current row + :type currRow: int + :param currColumn: the current column + :type currColumn: int + :param prevRow: the previous row + :type prevRow: int + :param prevCol: the previous column + :type prevCol: int + + .. py:method:: undo() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.undo` + + .. py:method:: redo() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.redo` + + .. py:method:: copy() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.copy` + + .. py:method:: cut() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.cut` + + .. py:method:: paste() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.paste` + + .. py:method:: setSortingEnabled() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setSortingEnabled` + + .. py:method:: isSortingEnabled() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.isSortingEnabled` + + .. py:method:: sortByColumn() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.sortByColumn` + + .. py:method:: clearSelection() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.clearSelection` + + .. py:method:: selectAll() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.selectAll` + + .. py:method:: setSelection() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setSelection` + + .. py:method:: selectRow() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.selectRow` + + .. py:method:: selectColumn() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.selectColumn` + + .. py:method:: unselectRow() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.unselectRow` + + .. py:method:: unselectColumn() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.unselectColumn` + + .. py:method:: rowCount() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.rowCount` + + .. py:method:: currentRow() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.currentRow` + + .. py:method:: columnCount() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.columnCount` + + .. py:method:: currentColumn() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.currentColumn` + + .. py:method:: verticalHeader() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.verticalHeader` + + .. py:method:: horizontalHeader() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.horizontalHeader` + + .. py:method:: hSeparatorVisibility() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.hSeparatorVisibility` + + .. py:method:: vSeparatorVisibility() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.vSeparatorVisibility` + + .. py:method:: setHSeparatorVisibility() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setHSeparatorVisibility` + + .. py:method:: setVSeparatorVisibility() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setVSeparatorVisibility` + + .. py:method:: model() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.model` + + .. py:method:: setModel() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setModel` + + .. py:method:: setColumnWidth() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setColumnWidth` + + .. py:method:: resizeColumnToContents() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.resizeColumnToContents` + + .. py:method:: resizeColumnsToContents() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.resizeColumnsToContents` + + .. py:method:: setRowHeight() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.setRowHeight` + + .. py:method:: resizeRowToContents() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.resizeRowToContents` + + .. py:method:: resizeRowsToContents() + + This method is forwarded to :meth:`~TermTk.TTkWidgets.TTkModelView.tablewidget.TTkTableWidget.resizeRowsToContents` + + + ''' __slots__ = ( '_tableView', # Forwarded Signals - 'itemActivated', 'itemChanged', 'itemClicked', 'itemExpanded', 'itemCollapsed', 'itemDoubleClicked', - - # Forwarded Methods - 'model', 'setModel', - 'setSortingEnabled', - 'setSelection', + # 'cellActivated', + 'cellChanged', + 'cellClicked', 'cellDoubleClicked', + 'cellEntered', # 'cellPressed', + 'currentCellChanged', # Forwarded Methods From TTkTable - 'verticalHeader', 'horizntalHeader', - 'hSeparatorVisibility', 'vSeparatorVisibility', - 'setHSeparatorVisibility', 'setVSeparatorVisibility', - 'setRowHeight', 'resizeRowToContents', 'resizeRowsToContents', + 'undo', 'redo', + 'copy', 'cut', 'paste', + 'setSortingEnabled', 'isSortingEnabled', 'sortByColumn', + 'clearSelection', 'selectAll', 'setSelection', + 'selectRow', 'selectColumn', 'unselectRow', 'unselectColumn', + 'rowCount', 'currentRow', 'columnCount', 'currentColumn', + 'verticalHeader', 'horizontalHeader', + 'hSeparatorVisibility', 'vSeparatorVisibility', 'setHSeparatorVisibility', 'setVSeparatorVisibility', + 'model', 'setModel', 'setColumnWidth', 'resizeColumnToContents', 'resizeColumnsToContents', + 'setRowHeight', 'resizeRowToContents', 'resizeRowsToContents', ) def __init__(self, *, @@ -51,23 +274,46 @@ class TTkTable(TTkAbstractScrollArea): **kwargs): self._tableView = None super().__init__(parent=parent, visible=visible, **kwargs) - self._tableView = kwargs.get('TableWidget',TTkTableWidget(**kwargs)) + self._tableView:TTkTableWidget = kwargs.get('TableWidget',TTkTableWidget(**kwargs)) self.setViewport(self._tableView) # self.setFocusPolicy(TTkK.ClickFocus) - self.model = self._tableView.model - self.setModel = self._tableView.setModel - self.setSelection = self._tableView.setSelection + # Forward Signals + self.cellChanged = self._tableView.cellChanged + self.cellClicked = self._tableView.cellClicked + self.cellEntered = self._tableView.cellEntered + self.cellDoubleClicked = self._tableView.cellDoubleClicked + self.currentCellChanged = self._tableView.currentCellChanged + + # Forward Methods + self.setFocus = self._tableView.setFocus + self.focusChanged = self._tableView.focusChanged + + self.undo = self._tableView.undo + self.redo = self._tableView.redo + + self.copy = self._tableView.copy + self.cut = self._tableView.cut + self.paste = self._tableView.paste + self.setSortingEnabled = self._tableView.setSortingEnabled + self.isSortingEnabled = self._tableView.isSortingEnabled + self.sortByColumn = self._tableView.sortByColumn - self.setRowHeight = self._tableView.setRowHeight - self.setColumnWidth = self._tableView.setColumnWidth - self.resizeRowToContents = self._tableView.resizeRowToContents - self.resizeRowsToContents = self._tableView.resizeRowsToContents - self.resizeColumnToContents = self._tableView.resizeColumnToContents - self.resizeColumnsToContents = self._tableView.resizeColumnsToContents + self.clearSelection = self._tableView.clearSelection + self.selectAll = self._tableView.selectAll + self.setSelection = self._tableView.setSelection + self.selectRow = self._tableView.selectRow + self.selectColumn = self._tableView.selectColumn + self.unselectRow = self._tableView.unselectRow + self.unselectColumn = self._tableView.unselectColumn + + self.rowCount = self._tableView.rowCount + self.currentRow = self._tableView.currentRow + self.columnCount = self._tableView.columnCount + self.currentColumn = self._tableView.currentColumn - self.verticalHeader = self._tableView.verticalHeader + self.verticalHeader = self._tableView.verticalHeader self.horizontalHeader = self._tableView.horizontalHeader self.hSeparatorVisibility = self._tableView.hSeparatorVisibility @@ -75,27 +321,16 @@ class TTkTable(TTkAbstractScrollArea): self.setHSeparatorVisibility = self._tableView.setHSeparatorVisibility self.setVSeparatorVisibility = self._tableView.setVSeparatorVisibility - self.setFocus = self._tableView.setFocus - self.focusChanged = self._tableView.focusChanged + self.model = self._tableView.model + self.setModel = self._tableView.setModel + + self.setColumnWidth = self._tableView.setColumnWidth + self.resizeColumnToContents = self._tableView.resizeColumnToContents + self.resizeColumnsToContents = self._tableView.resizeColumnsToContents + self.setRowHeight = self._tableView.setRowHeight + self.resizeRowToContents = self._tableView.resizeRowToContents + self.resizeRowsToContents = self._tableView.resizeRowsToContents - # # Forward the signal - # self.itemActivated = self._tableView.itemActivated - # self.itemChanged = self._tableView.itemChanged - # self.itemClicked = self._tableView.itemClicked - # self.itemDoubleClicked = self._tableView.itemDoubleClicked - - # # Forwarded Methods - # #self.setAlignment = self._tableView.setAlignment - # #self.setHeader = self._tableView.setHeader - # self.setHeaderLabels = self._tableView.setHeaderLabels - # #self.setColumnSize = self._tableView.setColumnSize - # #self.setColumnColors = self._tableView.setColumnColors - # #self.appendItem = self._tableView.appendItem - # self.selectedItems = self._tableView.selectedItems - # self.setColumnWidth = self._tableView.setColumnWidth - # self.resizeColumnToContents = self._tableView.resizeColumnToContents - - # self.clear = self._tableView.clear def style(self): if self._tableView: diff --git a/TermTk/TTkWidgets/TTkModelView/tablewidget.py b/TermTk/TTkWidgets/TTkModelView/tablewidget.py index 4398b41b..f34f4591 100644 --- a/TermTk/TTkWidgets/TTkModelView/tablewidget.py +++ b/TermTk/TTkWidgets/TTkModelView/tablewidget.py @@ -164,6 +164,70 @@ class TTkTableWidget(TTkAbstractScrollView): :param dataPadding: the right column padding, defaults to 1 :type dataPadding: int, optional + + +-----------------------------------------------------------------------------------------------+ + | `Signals `_ | + +-----------------------------------------------------------------------------------------------+ + + .. py:method:: cellChanged(row, col) + :signal: + + This signal is emitted whenever the data of the item in the cell specified by row and column has changed. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellClicked(row, col) + :signal: + + This signal is emitted whenever a cell in the table is clicked. + The row and column specified is the cell that was clicked. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellDoubleClicked(row, col) + :signal: + + This signal is emitted whenever a cell in the table is double clicked. + The row and column specified is the cell that was double clicked. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: cellEntered(row, col) + :signal: + + This signal is emitted when the mouse cursor enters a cell. + The cell is specified by row and column. + + :param row: the row + :type row: int + :param col: the column + :type col: int + + .. py:method:: currentCellChanged(currRow, currCol, prevRow, prevCol) + :signal: + + This signal is emitted whenever the current cell changes. + The cell specified by **prevRow** and **prevCol** is the cell that previously had the focus, + the cell specified by **currRow** and **currCol** is the new current cell. + + :param currRow: the current row + :type currRow: int + :param currColumn: the current column + :type currColumn: int + :param prevRow: the previous row + :type prevRow: int + :param prevCol: the previous column + :type prevCol: int + ''' classStyle = { @@ -201,7 +265,11 @@ class TTkTableWidget(TTkAbstractScrollView): '_fastCheck', '_guessDataEdit', '_snapshot', '_snapshotId', # Signals - # 'itemChanged', 'itemClicked', 'itemDoubleClicked', 'itemExpanded', 'itemCollapsed', 'itemActivated' + # 'cellActivated', + 'cellChanged', + 'cellClicked', 'cellDoubleClicked', + 'cellEntered', # 'cellPressed', + 'currentCellChanged', ) def __init__(self, *, @@ -216,6 +284,16 @@ class TTkTableWidget(TTkAbstractScrollView): # self.itemDoubleClicked = pyTTkSignal(TTkTableWidgetItem, int) # self.itemExpanded = pyTTkSignal(TTkTableWidgetItem) # self.itemCollapsed = pyTTkSignal(TTkTableWidgetItem) + + # self.cellActivated = pyTTkSignal(int,int) + self.cellChanged = pyTTkSignal(int,int) + self.cellClicked = pyTTkSignal(int,int) + self.cellDoubleClicked = pyTTkSignal(int,int) + self.cellEntered = pyTTkSignal(int,int) + # self.cellPressed = pyTTkSignal(int,int) + self.currentCellChanged = pyTTkSignal(int,int,int,int) + # self.currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) + self._fastCheck = True self._guessDataEdit = True @@ -269,7 +347,7 @@ class TTkTableWidget(TTkAbstractScrollView): self.setSelection(pos=(col,row),size=(1,1),flags=TTkK.TTkItemSelectionModel.Select) i.dataIndex.setData(i.newData if newData else i.oldData) cpsi:TTkModelIndex = self._snapshot[snapId][0] - self._currentPos = (cpsi.row(),cpsi.col()) + self._setCurrentCell(cpsi.row(),cpsi.col()) self._moveCurrentCell(diff=(0,0)) self.update() @@ -364,6 +442,7 @@ class TTkTableWidget(TTkAbstractScrollView): oldData = self._tableModel.data(row=row,col=col) dataIndex = self._tableModel.index(row=row,col=col) if newData == oldData: continue + self.cellChanged.emit(row,col) snaps.append(self._SnapItem( dataIndex=dataIndex, oldData=oldData, @@ -1013,6 +1092,12 @@ class TTkTableWidget(TTkAbstractScrollView): else: self._editStr(xa,ya,xb-xa,yb-ya,row,col,data) + def _setCurrentCell(self, currRow:int, currCol:int) -> None: + prevRow,prevCol = self._currentPos if self._currentPos else (0,0) + self._currentPos = (currRow,currCol) + if (currRow,currRow)!=(prevRow,prevCol): + self.currentCellChanged.emit(currRow,currCol,prevRow,prevCol) + def _moveCurrentCell(self, col=0, row=0, borderStop=True, diff=None): rows = self._tableModel.rowCount() cols = self._tableModel.columnCount() @@ -1031,7 +1116,7 @@ class TTkTableWidget(TTkAbstractScrollView): if col < 0: col=cols-1 ; row-=1 if row >= rows: row=0 if row < 0: row=rows-1 - self._currentPos = (row,col) + self._setCurrentCell(row,col) # move the offset to include the cell w,h = self.size() ox, oy = self.getViewOffsets() @@ -1137,6 +1222,7 @@ class TTkTableWidget(TTkAbstractScrollView): return True row,col = self._findCell(x,y, headers=False) + self.cellDoubleClicked.emit(row,col) self._editCell(row,col) return True @@ -1167,6 +1253,8 @@ class TTkTableWidget(TTkAbstractScrollView): self._hoverPos = None self.update() return True + if row>=0 and col>>0: + self.cellEntered.emit(row,col) self.update() return True @@ -1242,7 +1330,9 @@ class TTkTableWidget(TTkAbstractScrollView): self.selectColumn(col) else: # Cell Select - self._currentPos = (row,col) + self.cellClicked.emit(row,col) + # self.cellPressed.emit(row,col) + self._setCurrentCell(row,col) self.setSelection(pos = (col,row), size = (1,1), flags = TTkK.TTkItemSelectionModel.Clear if (self._selected[row][col] and _ctrl) else TTkK.TTkItemSelectionModel.Select) self._hoverPos = None diff --git a/tests/t.ui/test.ui.032.table.09.py b/tests/t.ui/test.ui.032.table.09.py new file mode 100755 index 00000000..c91eaa4c --- /dev/null +++ b/tests/t.ui/test.ui.032.table.09.py @@ -0,0 +1,391 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2024 Eugenio Parodi +# +# 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__(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) + + +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) + + +######################### +# 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=51) +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) + +wtb.clicked.connect(_showWinText) + +table.setFocus() + +root.mainloop() \ No newline at end of file