Browse Source

chore: improve typing (#572)

pull/573/head
Pier CeccoPierangioliEugenio 3 months ago committed by GitHub
parent
commit
db386ca52a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      libs/pyTermTk/TermTk/TTkCore/constant.py
  2. 4
      libs/pyTermTk/TermTk/TTkWidgets/Fancy/tableview.py
  3. 12
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/table.py
  4. 21
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/table_edit_proxy.py
  5. 19
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py
  6. 31
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodellist.py
  7. 28
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodelsqlite3.py
  8. 31
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablewidget.py
  9. 9
      libs/pyTermTk/TermTk/TTkWidgets/combobox.py
  10. 0
      tests/timeit/02.array.01.copy.01.py
  11. 57
      tests/timeit/02.array.01.copy.02.py

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

@ -60,7 +60,7 @@ class TTkConstant:
ColorModifier = 0x08
'''The :py:class:`TTkColor` include a color modifier based on :py:class:`TTkColorModifier`'''
class FocusPolicy(int, Flag):
class FocusPolicy(Flag):
'''
This Class type defines the various policies a widget
can have with respect to acquiring keyboard focus.

4
libs/pyTermTk/TermTk/TTkWidgets/Fancy/tableview.py

@ -443,8 +443,8 @@ class TTkFancyTableView(TTkAbstractScrollView):
self._viewOffsetY == y: # Nothong to do
return
self._excludeEvent = True
for widget in self.layout().iterWidgets():
widget.viewMoveTo(x,y)
# for widget in self.layout().iterWidgets():
# widget.viewMoveTo(x,y)
self._excludeEvent = False
self._viewOffsetX = x
self._viewOffsetY = y

12
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/table.py

@ -35,7 +35,7 @@ class TTkTable(TTkAbstractScrollArea):
__doc__ = '''
:py:class:`TTkTable` is a container widget which place :py:class:`TTkTableWidget` in a scrolling area with on-demand scroll bars.
''' + TTkTableWidget.__doc__
''' + str(TTkTableWidget.__doc__)
classStyle = TTkTableWidget.classStyle
@ -216,18 +216,20 @@ class TTkTable(TTkAbstractScrollArea):
'''
.. seealso:: this method is forwarded to :py:meth:`TTkTableWidget.isUndoAvailable`
isUndoAvailable
Returns True if undo is available, False otherwise.
:return: bool
:return: True if undo is available
:rtype: bool
'''
return self._tableView.isUndoAvailable()
def isRedoAvailable(self) -> bool:
'''
.. seealso:: this method is forwarded to :py:meth:`TTkTableWidget.isRedoAvailable`
isRedoAvailable
Returns True if redo is available, False otherwise.
:return: bool
:return: True if redo is available
:rtype: bool
'''
return self._tableView.isRedoAvailable()
@pyTTkSlot()

21
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/table_edit_proxy.py

@ -158,7 +158,7 @@ class TTkCellListType(TTkCellListTypeBase):
def value(self) -> Any:
return self._value
def serValue(self, val: Any) -> None:
def setValue(self, val: Any) -> None:
'''
Set the value
@ -166,7 +166,7 @@ class TTkCellListType(TTkCellListTypeBase):
:type val: Any
'''
if val not in self._items:
raise ValueError(f"{val} not included in {self._list}")
raise ValueError(f"{val} not included in {self._items}")
self._value = val
def factory(self, value:Any, items:List[Any]) -> TTkCellListTypeBase:
@ -253,6 +253,12 @@ class TTkTableProxyEditWidget(TTkWidget):
self.close()
def isModal(self) -> bool:
'''
Check if the editor should be displayed modally.
:return: True if the editor requires modal display, False otherwise
:rtype: bool
'''
return False
@ -323,7 +329,8 @@ class _ListBaseProxy(TTkResizableFrame, TTkTableProxyEditWidget):
def setFocus(self) -> None:
''' Set focus to the internal list widget
'''
return self._list.viewport().setFocus()
if (viewport := self._list.viewport()) and isinstance(viewport, TTkWidget):
viewport.setFocus()
def keyEvent(self, evt:TTkKeyEvent) -> bool:
return self._list.keyEvent(evt=evt)
@ -343,15 +350,15 @@ class _ListBaseProxy(TTkResizableFrame, TTkTableProxyEditWidget):
sb = _ListBaseProxy(items=data.items(), value=data)
return sb
def getCellData(self) -> Union[float, int]:
def getCellData(self) -> TTkCellListTypeBase:
''' Get the current selected value from the list
:return: The selected item value
:rtype: Union[float, int]
'''
self.dataChanged.emit(self._value.factory(
return self._value.factory(
value=self._list.selectedItems()[0].data(),
items=self._items))
items=self._items)
class _BoolListProxy(_ListBaseProxy):
''' Boolean editor for table cells
@ -385,7 +392,7 @@ class _BoolListProxy(_ListBaseProxy):
sb = _BoolListProxy(value=value, items=[True,False])
return sb
def getCellData(self) -> Union[float, int]:
def getCellData(self) -> Any:
''' Get the current boolean value from the list
:return: The selected boolean value (True or False)

19
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py

@ -24,12 +24,15 @@ __all__=['TTkTableModelCSV']
import csv
from typing import Tuple,List,Optional,cast
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkStringType
from TermTk.TTkWidgets.TTkModelView.tablemodellist import TTkTableModelList
class TTkTableModelCSV(TTkTableModelList):
'''
:py:class:`TTkTableModelCSV` extends :py:class:`TTkTableModelList` with cvs loading helpers.
:py:class:`TTkTableModelCSV` extends :py:class:`TTkTableModelList` with csv loading helpers.
You can address the csv file through the Filename (filename) or the FileDescriptor (fd).
@ -47,7 +50,7 @@ class TTkTableModelCSV(TTkTableModelList):
'''
def __init__(self, *,
filename:str=None,
filename:Optional[str]=None,
fd=None) -> None:
'''
:param filename: the csv filename, if missing the file descriptor is used instead.
@ -56,7 +59,9 @@ class TTkTableModelCSV(TTkTableModelList):
:param fd: the FileDescriptor
:type fd: io, optional
'''
data, head, idx = [['']], [], []
data:List[List[TTkStringType]] = [['']]
head:List[TTkStringType] = []
idx:List[TTkStringType] = []
if filename:
with open(filename, "r") as fd:
data, head, idx = self._csvImport(fd)
@ -64,8 +69,10 @@ class TTkTableModelCSV(TTkTableModelList):
data, head, idx = self._csvImport(fd)
super().__init__(data=data,header=head,indexes=idx)
def _csvImport(self, fd) -> tuple[list,list,list[list]]:
data, head, idx = [], [], []
def _csvImport(self, fd) -> Tuple[List[List[TTkStringType]],List[TTkStringType],List[TTkStringType]]:
data:List[List[TTkStringType]] = []
head:List[TTkStringType] = []
idx:List[TTkStringType] = []
sniffer = csv.Sniffer()
try:
has_header = sniffer.has_header(fd.read(2048))
@ -74,7 +81,7 @@ class TTkTableModelCSV(TTkTableModelList):
fd.seek(0)
csvreader = csv.reader(fd)
for row in csvreader:
data.append(row)
data.append(list(row))
if has_header:
head = data.pop(0)
# check if the first column include an index:

31
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodellist.py

@ -22,14 +22,15 @@
__all__=['TTkTableModelList']
from typing import Any
from typing import Any,List,Optional
from TermTk.TTkCore.string import TTkString, TTkStringType
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel, TTkModelIndex
class _TTkModelIndexList(TTkModelIndex):
__slots__ = ('_col','_rowId','_rowCb')
def __init__(self, col:int, rowId:list, rowCb) -> None:
def __init__(self, col:int, rowId:List, rowCb) -> None:
self._col = col
self._rowId = rowId
self._rowCb = rowCb
@ -50,35 +51,35 @@ class _TTkModelIndexList(TTkModelIndex):
class TTkTableModelList(TTkAbstractTableModel):
'''
:py:class:`TTkTableModelList` extends :py:class:`TTkAbstractTableModel`,
including a basic model with a 2d list data structure
including a basic model with a 2d List data structure
'''
__slots__ = ('_data','_dataOriginal', '_hheader', '_vheader')
def __init__(self, *,
data:list[list[object]]=None,
header:list[str]=None,
indexes:list[str]=None) -> None:
data:Optional[List[List[Any]]]=None,
header:Optional[List[TTkStringType]]=None,
indexes:Optional[List[TTkStringType]]=None) -> None:
'''
:param data: the 2D List model for the view to present.
:type data: list[list]
:type data: List[List]
:param header: the header labels, defaults to the column number.
:type header: list[str], optional
:type header: List[str], optional
:param indexes: the index labels, defaults to the line number.
:type indexes: list[str], optional
:type indexes: List[str], optional
'''
self._data = self._dataOriginal = data if data else []
self._hheader = header if header else []
self._vheader = indexes if indexes else []
super().__init__()
def modelList(self) -> list[list]:
def modelList(self) -> List[List]:
return self._data
def setModelList(self, modelList:list[list]) -> None:
def setModelList(self, modelList:List[List]) -> None:
if modelList == self._data: return
self._data = modelList
self.modelChanged.emit()
@ -102,18 +103,18 @@ class TTkTableModelList(TTkAbstractTableModel):
return None
return col_data[col]
def setData(self, row:int, col:int, data:object) -> None:
def setData(self, row:int, col:int, data:object) -> bool:
self._data[row][col] = data
self.dataChanged.emit((row,col),(1,1))
return True
def headerData(self, num:int, orientation:int):
def headerData(self, num:int, orientation:TTkK.Direction) -> TTkString:
if orientation == TTkK.HORIZONTAL:
if self._hheader:
return self._hheader[num]
return TTkString(self._hheader[num])
if orientation == TTkK.VERTICAL:
if self._vheader:
return self._vheader[num]
return TTkString(self._vheader[num])
return super().headerData(num, orientation)
def flags(self, row:int, col:int) -> TTkK.ItemFlag:

28
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tablemodelsqlite3.py

@ -20,20 +20,28 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import annotations
__all__=['TTkTableModelSQLite3']
import sqlite3
import threading
from typing import Any
from typing import Any,Dict,List
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkAbstract.abstracttablemodel import TTkAbstractTableModel, TTkModelIndex
class _TTkModelIndexSQLite3(TTkModelIndex):
__slots__ = ('_col','_rowId','_sqModel')
def __init__(self, col:int, rowId:str, sqModel) -> None:
_col:int
_rowId:str
_sqModel:TTkTableModelSQLite3
def __init__(self, col:int, rowId:str, sqModel:TTkTableModelSQLite3) -> None:
self._col = col
self._rowId = rowId
self._sqModel = sqModel
@ -44,11 +52,11 @@ class _TTkModelIndexSQLite3(TTkModelIndex):
def col(self) -> int:
return self._col
def data(self) -> object:
def data(self) -> Any:
return self._sqModel.data(row=self.row(),col=self.col())
def setData(self, data: object) -> None:
return self._sqModel.setData(row=self.row(),col=self.col(),data=data)
def setData(self, data: Any) -> None:
self._sqModel.setData(row=self.row(),col=self.col(),data=data)
class TTkTableModelSQLite3(TTkAbstractTableModel):
'''
@ -59,7 +67,7 @@ class TTkTableModelSQLite3(TTkAbstractTableModel):
In This example i assume i have a database named **sqlite.database.db** which contain a table **users**
Please refer to `test.ui.032.table.10.sqlite.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tests/t.ui/test.ui.032.table.10.sqlite.py>`_ for working eample.
Please refer to `test.ui.032.table.10.sqlite.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tests/t.ui/test.ui.032.table.10.sqlite.py>`_ for working example.
.. code-block:: python
@ -85,6 +93,8 @@ class TTkTableModelSQLite3(TTkAbstractTableModel):
'_sqliteMutex',
'_idMap')
_idMap:Dict[str,int]
def __init__(self, *,
fileName:str,
table:str,
@ -143,7 +153,7 @@ class TTkTableModelSQLite3(TTkAbstractTableModel):
f"SELECT {self._key} FROM {self._table} "
f"{self._sort} "
f"LIMIT 1 OFFSET {row}")
key = None if not (_fetch:=res.fetchone()) else _fetch[0]
key:str = '' if not (_fetch:=res.fetchone()) else _fetch[0]
return _TTkModelIndexSQLite3(col=col,rowId=key,sqModel=self)
def data(self, row:int, col:int) -> Any:
@ -154,7 +164,7 @@ class TTkTableModelSQLite3(TTkAbstractTableModel):
f"LIMIT 1 OFFSET {row}")
return None if not (_fetch:=res.fetchone()) else _fetch[0]
def setData(self, row:int, col:int, data:object) -> None:
def setData(self, row:int, col:int, data:object) -> bool:
with self._sqliteMutex:
res = self._cur.execute(
f"SELECT {self._key} FROM {self._table} "
@ -170,7 +180,7 @@ class TTkTableModelSQLite3(TTkAbstractTableModel):
self._refreshIdMap()
return True
def headerData(self, num:int, orientation:int):
def headerData(self, num:int, orientation:TTkK.Direction) -> TTkString:
if orientation == TTkK.HORIZONTAL:
if self._columns:
return self._columns[num]

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

@ -58,22 +58,37 @@ class TTkHeaderView():
@pyTTkSlot(bool)
def setVisible(self, visible: bool) -> None:
'''setVisible'''
'''
Sets the visibility of the header.
:param visible: True to show the header, False to hide it
:type visible: bool
'''
if self._visible == visible: return
self._visible = visible
self.visibilityUpdated.emit(visible)
@pyTTkSlot()
def show(self) -> None:
'''show'''
'''
Shows the header by setting its visibility to True.
'''
self.setVisible(True)
@pyTTkSlot()
def hide(self) -> None:
'''hide'''
'''
Hides the header by setting its visibility to False.
'''
self.setVisible(False)
def isVisible(self) -> bool:
'''
Returns True if the header is visible, False otherwise.
:return: the visibility status
:rtype: bool
'''
return self._visible
_ClipboardTableData = List[List[Tuple[int,int,Any]]]
@ -564,17 +579,19 @@ class TTkTableWidget(TTkAbstractScrollView):
def isUndoAvailable(self) -> bool:
'''
isUndoAvailable
Returns True if undo is available, False otherwise.
:return: bool
:return: True if undo is available
:rtype: bool
'''
return self._snapshotId > 0
def isRedoAvailable(self) -> bool:
'''
isRedoAvailable
Returns True if redo is available, False otherwise.
:return: bool
:return: True if redo is available
:rtype: bool
'''
return self._snapshotId < len(self._snapshot)

9
libs/pyTermTk/TermTk/TTkWidgets/combobox.py

@ -33,6 +33,7 @@ from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.container import TTkContainer
from TermTk.TTkWidgets.list_ import TTkList
from TermTk.TTkWidgets.lineedit import TTkLineEdit
@ -90,7 +91,7 @@ class _TTkComboBoxPopup(TTkResizableFrame):
super().__init__(**kwargs|{'layout':TTkGridLayout()})
# Create internal list with search disabled - we'll render search text manually
self._list:TTkList = TTkList(parent=self, showSearch=False)
self._list.addItems(items)
self._list.addItems(list(items))
# Trigger repaint when search changes to update our custom search overlay
self._list.searchModified.connect(self.update)
@ -103,7 +104,9 @@ class _TTkComboBoxPopup(TTkResizableFrame):
def keyEvent(self, evt:TTkKeyEvent) -> bool:
'''Forward all key events to the list viewport for navigation and search'''
return self._list.viewport().keyEvent(evt)
if (viewport := self._list.viewport()) and isinstance(viewport, TTkWidget):
return viewport.keyEvent(evt)
return False
def paintEvent(self, canvas:TTkCanvas) -> None:
'''Custom paint event that overlays search text on top of the frame.
@ -180,7 +183,9 @@ class TTkComboBox(TTkContainer):
'''
_list:List[str]
_lineEdit:Optional[TTkLineEdit]
_popupFrame:Optional[_TTkComboBoxPopup]
def __init__(self, *,
list:Optional[List[str]] = None,
index:int = -1,

0
tests/timeit/02.array.01.copy.py → tests/timeit/02.array.01.copy.01.py

57
tests/timeit/02.array.01.copy.02.py

@ -0,0 +1,57 @@
#!/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.
# flake8: noqa
# This file uses match statements (Python 3.10+) which cause syntax errors in Python 3.9
from __future__ import annotations
import sys, os
from dataclasses import dataclass
from enum import Enum,Flag,auto
import timeit
from typing import List, Tuple, Iterator
test_list = [_i for _i in range(10000)]
def test_ti_01_01():
return len(test_list.copy())
def test_ti_01_02():
return len([_x for _x in test_list])
def test_ti_01_03():
return len(list(test_list))
loop = 10000
a:dict = {}
for testName in sorted([tn for tn in globals() if tn.startswith('test_ti_')]):
result = timeit.timeit(f'{testName}(*a)', globals=globals(), number=loop)
# print(f"test{iii}) fps {loop / result :.3f} - s {result / loop:.10f} - {result / loop} {globals()[testName](*a)}")
print(f"{testName} | {result / loop:.10f} sec. | {loop / result : 15.3f} Fps ╞╡-> {globals()[testName](*a)}")
Loading…
Cancel
Save