Browse Source

Reworked the 'Abstract Scroll' generic initializatioon and documentation

pull/281/head
Eugenio Parodi 1 year ago
parent
commit
6f51b29110
  1. 15
      Makefile
  2. 105
      TermTk/TTkAbstract/abstractscrollview.py
  3. 30
      TermTk/TTkCore/constant.py
  4. 2
      TermTk/TTkTemplates/dragevents.py
  5. 2
      TermTk/TTkTemplates/keyevents.py
  6. 2
      TermTk/TTkTemplates/mouseevents.py
  7. 3
      TermTk/TTkTestWidgets/logviewer.py
  8. 2
      TermTk/TTkTestWidgets/testabstractscroll.py
  9. 6
      TermTk/TTkTestWidgets/tominspector.py
  10. 7
      TermTk/TTkWidgets/Fancy/tableview.py
  11. 2
      TermTk/TTkWidgets/TTkModelView/filetreewidget.py
  12. 6
      TermTk/TTkWidgets/TTkModelView/table.py
  13. 16
      TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py
  14. 23
      TermTk/TTkWidgets/TTkModelView/tablemodellist.py
  15. 46
      TermTk/TTkWidgets/TTkModelView/tablewidget.py
  16. 4
      TermTk/TTkWidgets/TTkModelView/tree.py
  17. 10
      TermTk/TTkWidgets/TTkModelView/treewidget.py
  18. 3
      TermTk/TTkWidgets/TTkPickers/textpicker.py
  19. 35
      TermTk/TTkWidgets/TTkTerminal/terminal.py
  20. 132
      TermTk/TTkWidgets/TTkTerminal/terminalview.py
  21. 23
      TermTk/TTkWidgets/button.py
  22. 22
      TermTk/TTkWidgets/checkbox.py
  23. 25
      TermTk/TTkWidgets/combobox.py
  24. 37
      TermTk/TTkWidgets/container.py
  25. 17
      TermTk/TTkWidgets/frame.py
  26. 61
      TermTk/TTkWidgets/list_.py
  27. 75
      TermTk/TTkWidgets/listwidget.py
  28. 3
      TermTk/TTkWidgets/menu.py
  29. 19
      TermTk/TTkWidgets/radiobutton.py
  30. 3
      TermTk/TTkWidgets/scrollarea.py
  31. 337
      TermTk/TTkWidgets/texedit.py
  32. 91
      TermTk/TTkWidgets/widget.py
  33. 3
      docs/source/conf.py
  34. 8
      docs/source/sphinx_modules/sphinx_PyRefRole_hacked.py
  35. 56
      docs/source/sphinx_modules/sphinx_ext_autosummary_reworked.py
  36. 3
      docs/source/static/theme_overrides.css
  37. 2
      tutorial/000-examples.rst

15
Makefile

@ -85,17 +85,20 @@ deployDoc: pyTermTk-Docs
cd pyTermTk-Docs ; \
git checkout main ; \
git pull ; \
rm -rf _* info tutorial ; \
cp -a ../docs/source/_build/html/* \
../docs/source/_build/html/.buildinfo \
../docs/source/_build/html/.nojekyll \
. ; \
rm -rf _* info tutorial ;
cp -a docs/source/_build/html/* \
docs/source/_build/html/.buildinfo \
docs/source/_build/html/.nojekyll \
pyTermTk-Docs ; \
cd pyTermTk-Docs ; \
git add . ; \
git commit -m "Updated Docs" ; \
git push origin main ; \
git checkout gh-pages ; \
git merge main ; \
git push origin gh-pages ; \
git push origin gh-pages ;
echo "Docs Deployed!!!"
deploySandbox:

105
TermTk/TTkAbstract/abstractscrollview.py

@ -30,25 +30,104 @@ from TermTk.TTkLayouts.layout import TTkLayout
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
class TTkAbstractScrollViewInterface():
'''TTkAbstractScrollViewInterface'''
'''
The :py:class:`TTkAbstractScrollViewInterface` provide the basic interface that can be used in :py:class:`TTkAbstractScrollArea` to enable on-demand scroll bars.
When subclassing :py:class:`TTkAbstractScrollViewInterface`,
you must implement :meth:`viewFullAreaSize`, :meth:`viewDisplayedSize`, :meth:`getViewOffsets`, and :meth:`viewMoveTo`.
This interface is implemented in the following specialised classes:
* :py:class:`TTkAbstractScrollView`
* :py:class:`TTkAbstractScrollViewLayout`
* :py:class:`TTkAbstractScrollViewGridLayout`
'''
def __init__(self) -> None: pass
# Override this function
def viewFullAreaSize(self) -> (int, int):
'''
This method returns the full widget area size of the :py:class:`TTkAbstractScrollViewInterface` implementation.
This is required to `TTkAbstractScrollArea` implementation to handle the on-demand scroll bars.
.. note:: Reimplement this function to handle this event
:return: the full area size as a tuple of 2 int elements (width,height)
:rtype: tuple[int,int]
'''
raise NotImplementedError()
# Override this function
def viewDisplayedSize(self) -> (int, int):
'''
This method returns the displayed size of the :py:class:`TTkAbstractScrollViewInterface` implementation.
.. note::
Reimplement this function to handle this event
This method is already implemented in the following classes:
* :py:class:`TTkAbstractScrollView`
* :py:class:`TTkAbstractScrollViewLayout`
* :py:class:`TTkAbstractScrollViewGridLayout`
Unless a different iplementation is required, by default it should return :py:meth:`TTkWidget.size`
:return: the displayed size as a tuple of 2 int elements (width,height)
:rtype: tuple[int,int]
'''
raise NotImplementedError()
@pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int):
'''
This method is used to set the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface`
.. note::
Reimplement this function to handle this event
This method is already implemented in the following classes:
* :py:class:`TTkAbstractScrollView`
* :py:class:`TTkAbstractScrollViewLayout`
* :py:class:`TTkAbstractScrollViewGridLayout`
:param x: the horizontal position
:type x: int
:param y: the vertical position
:type y: int
'''
raise NotImplementedError()
def getViewOffsets(self):
def getViewOffsets(self) -> tuple:
'''
Retrieve the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface`
.. note::
Reimplement this function to handle this event
This method is already implemented in the following classes:
* :py:class:`TTkAbstractScrollView`
* :py:class:`TTkAbstractScrollViewLayout`
* :py:class:`TTkAbstractScrollViewGridLayout`
:return: the (x,y) offset
:rtype: tuple[int,int]
'''
return self._viewOffsetX, self._viewOffsetY
class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
'''TTkAbstractScrollView'''
'''
The :py:class:`TTkAbstractScrollView` is a :py:class:`TTkContainer` widget that incude :py:class:`TTkAbstractScrollViewInterface` api.
The placement of any widget inside this container will change accordingly to the offset of this view.
This class is used in the convenience widget :py:class:`TTkScrollArea`
'''
viewMovedTo:pyTTkSignal
'''
@ -91,6 +170,9 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
# Do NOT use super()
TTkContainer.__init__(self, **kwargs)
def viewDisplayedSize(self) -> (int, int):
return self.size()
@pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int):
fw, fh = self.viewFullAreaSize()
@ -110,6 +192,9 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
self.viewChanged.emit()
self.update()
def getViewOffsets(self) -> tuple:
return self._viewOffsetX, self._viewOffsetY
def wheelEvent(self, evt):
delta = TTkCfg.scrollDelta
offx, offy = self.getViewOffsets()
@ -129,7 +214,9 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
return super().update(repaint, updateLayout, updateParent)
class TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface):
'''TTkAbstractScrollViewLayout'''
'''
:py:class:`TTkAbstractScrollViewLayout`
'''
viewMovedTo:pyTTkSignal
'''
@ -184,12 +271,17 @@ class TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface):
def viewMoveTo(self, x: int, y: int):
self.setOffset(-x,-y)
def getViewOffsets(self) -> tuple:
return self._viewOffsetX, self._viewOffsetY
def setGeometry(self, x, y, w, h):
TTkLayout.setGeometry(self, x, y, w, h)
self.viewChanged.emit()
class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterface):
'''TTkAbstractScrollViewGridLayout'''
'''
:py:class:`TTkAbstractScrollViewGridLayout`
'''
viewMovedTo:pyTTkSignal
'''
@ -256,6 +348,9 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
self.viewChanged.emit()
self.update()
def getViewOffsets(self) -> tuple:
return self._viewOffsetX, self._viewOffsetY
def setGeometry(self, x, y, w, h):
TTkGridLayout.setGeometry(self, x, y, w, h)
self.viewChanged.emit()

30
TermTk/TTkCore/constant.py

@ -84,12 +84,32 @@ class TTkConstant:
HEADER = 0x0020
FOOTER = 0x0040
class SelectionMode(int):
'''
This class type indicates how the view responds to user selections.
.. autosummary::
NoSelection
SingleSelection
MultiSelection
'''
NoSelection = 0x00
'''Items cannot be selected.'''
SingleSelection = 0x01
'''When the user selects an item, any already-selected item becomes unselected. It is possible for the user to deselect the selected item by pressing the Ctrl key when clicking the selected item.'''
# ContiguousSelection = 0x04
# '''When the user selects an item in the usual way, the selection is cleared and the new item selected. However, if the user presses the Shift key while clicking on an item, all items between the current item and the clicked item are selected or unselected, depending on the state of the clicked item.'''
# ExtendedSelection = 0x03
# '''When the user selects an item in the usual way, the selection is cleared and the new item selected. However, if the user presses the Ctrl key when clicking on an item, the clicked item gets toggled and all other items are left untouched. If the user presses the Shift key while clicking on an item, all items between the current item and the clicked item are selected or unselected, depending on the state of the clicked item. Multiple items can be selected by dragging the mouse over them.'''
MultiSelection = 0x02
'''When the user selects an item in the usual way, the selection status of that item is toggled and the other items are left alone. Multiple items can be toggled by dragging the mouse over them.'''
# SelectionMode
NoSelection = 0x00
SingleSelection = 0x01
MultiSelection = 0x02
ExtendedSelection = 0x03
ContiguousSelection = 0x04
NoSelection = SelectionMode.NoSelection
SingleSelection = SelectionMode.SingleSelection
# ExtendedSelection = SelectionMode.ExtendedSelection
# ContiguousSelection = SelectionMode.ContiguousSelection
MultiSelection = SelectionMode.MultiSelection
# Graph types
FILLED = 0x0001

2
TermTk/TTkTemplates/dragevents.py

@ -23,6 +23,8 @@
__all__ = ['TDragEvents']
class TDragEvents():
def __init__(self) -> None: pass
def dragEnterEvent(self, evt) -> bool:
'''
This event handler, can be reimplemented in a subclass to receive drag events for the widget.

2
TermTk/TTkTemplates/keyevents.py

@ -25,6 +25,8 @@ __all__ = ['TKeyEvents']
from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent
class TKeyEvents():
def __init__(self) -> None: pass
# def keyPressEvent(self, evt:TTkKeyEvent) -> bool :
# '''
# This event handler, can be reimplemented in a subclass to receive key press events for the widget.

2
TermTk/TTkTemplates/mouseevents.py

@ -25,6 +25,8 @@ __all__ = ['TMouseEvents']
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
class TMouseEvents():
def __init__(self) -> None: pass
def mouseTapEvent(self, evt:TTkMouseEvent) -> bool :
'''
This event handler, can be reimplemented in a subclass to receive mouse click events for the widget.

3
TermTk/TTkTestWidgets/logviewer.py

@ -53,9 +53,6 @@ class _TTkLogViewer(TTkAbstractScrollView):
h = len(self._messages)
return w , h
def viewDisplayedSize(self) -> (int, int):
return self.size()
def loggingCallback(self, mode, context, message):
logType = "NONE"
if mode == TTkLog.InfoMsg: logType = TTkString("INFO " ,TTkColor.fg("#00ff00"))

2
TermTk/TTkTestWidgets/testabstractscroll.py

@ -63,7 +63,5 @@ class TTkTestAbstractScrollWidget(TTkAbstractScrollView):
def viewFullAreaSize(self) -> (int, int):
return self._areaSize
def viewDisplayedSize(self) -> (int, int):
return self.size()

6
TermTk/TTkTestWidgets/tominspector.py

@ -73,9 +73,6 @@ class _DetailGridView(TTkAbstractScrollView):
_,_,w,h = self._gridLayout.fullWidgetAreaGeometry()
return w , h
def viewDisplayedSize(self) -> (int, int):
return self.size()
class _DetailLazyFormView(TTkAbstractScrollView):
__slots__ = ('_gridLayout', '_lazyRows', '_lastRow')
def __init__(self, *args, **kwargs) -> None:
@ -122,9 +119,6 @@ class _DetailLazyFormView(TTkAbstractScrollView):
_,_,w,h = self.layout().fullWidgetAreaGeometry()
return w , h+1
def viewDisplayedSize(self) -> (int, int):
return self.size()
def paintEvent(self, canvas):
x,y = self.getViewOffsets()
w,h = self.size()

7
TermTk/TTkWidgets/Fancy/tableview.py

@ -48,10 +48,6 @@ class _TTkFancyTableViewHeader(TTkAbstractScrollView):
def viewFullAreaSize(self) -> (int, int):
return self.size()
# Override this function
def viewDisplayedSize(self) -> (int, int):
return self.size()
@pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int):
pass
@ -197,9 +193,6 @@ class _TTkFancyTableView(TTkAbstractScrollView):
def viewFullAreaSize(self) -> (int, int):
return self._tableWidth, len(self._tableDataText)
def viewDisplayedSize(self) -> (int, int):
return self.size()
# def items(self): return self._tableDataText
def setAlignment(self, alignments):

2
TermTk/TTkWidgets/TTkModelView/filetreewidget.py

@ -64,6 +64,8 @@ class TTkFileTreeWidget(TTkTreeWidget):
+ TTkWidgets/ Folder 2024-11-04 20:37:22
__init__.py 327 bytesFile 2024-11-04 19:56:26
Quickstart:
.. code-block:: python
import TermTk as ttk

6
TermTk/TTkWidgets/TTkModelView/table.py

@ -38,7 +38,7 @@ class TTkTable(TTkAbstractScrollArea):
__slots__ = tuple(
['_tableView'] +
(_forwardedSignals:=[ # Forwarded Signals
(_forwardedSignals:=[ # Forwarded Signals From TTkTable
# 'cellActivated',
'cellChanged',
'cellClicked', 'cellDoubleClicked',
@ -70,6 +70,10 @@ class TTkTable(TTkAbstractScrollArea):
sortingEnabled=False,
dataPadding=1,
**kwargs) -> None:
'''
:param tableWidget: a custom Table Widget to be used instead of the default one.
:type tableWidget: :py:class:`TTkTableWidget`, optional
'''
self._tableView = None
self._tableView:TTkTableWidget = tableWidget if tableWidget else TTkTableWidget(
tableModel=tableModel,

16
TermTk/TTkWidgets/TTkModelView/tablemodelcsv.py

@ -44,14 +44,18 @@ class TTkTableModelCSV(TTkTableModelList):
with open('path/file.csv') as fd:
tm2 = ttk.TTkTableModelCSV(fd=fd)
:param filename: the csv filename, if missing the file descriptor is used instead.
:type filename: str, optional
:param fd: the FileDescriptor
:type fd: io, optional
'''
def __init__(self, *, filename:str=None, fd=None):
def __init__(self, *,
filename:str=None,
fd=None) -> None:
'''
:param filename: the csv filename, if missing the file descriptor is used instead.
:type filename: str, optional
:param fd: the FileDescriptor
:type fd: io, optional
'''
data, head, idx = [[]], [], []
if filename:
with open(filename, "r") as fd:

23
TermTk/TTkWidgets/TTkModelView/tablemodellist.py

@ -50,19 +50,24 @@ class TTkTableModelList(TTkAbstractTableModel):
:py:class:`TTkTableModelList` extends :py:class:`TTkAbstractTableModel`,
including a basic model with a 2d list data structure
:param data: the 2D List model for the view to present.
:type data: list[list]
:param header: the header labels, defaults to the column number.
:type header: list[str], optional
:param indexes: the index labels, defaults to the line number.
:type indexes: list[str], optional
'''
__slots__ = ('_data','_dataOriginal', '_hheader', '_vheader')
def __init__(self, *, data:list[list[object]]=[], header:list[str]=[], indexes:list[str]=[]) -> None:
def __init__(self, *,
data:list[list[object]]=[],
header:list[str]=[],
indexes:list[str]=[]) -> None:
'''
:param data: the 2D List model for the view to present.
:type data: list[list]
:param header: the header labels, defaults to the column number.
:type header: list[str], optional
:param indexes: the index labels, defaults to the line number.
:type indexes: list[str], optional
'''
self._data = self._dataOriginal = data if data else [['']]
self._hheader = header
self._vheader = indexes

46
TermTk/TTkWidgets/TTkModelView/tablewidget.py

@ -150,26 +150,6 @@ class TTkTableWidget(TTkAbstractScrollView):
To distribute the available space according to the space requirement of each column or row,
call the view's :meth:`resizeColumnsToContents` or :meth:`resizeRowsToContents` functions.
:param tableModel: the model for the view to present.
:type tableModel: :py:class:`TTkAbstractTableModel`
:param vSeparator: show the vertical separators, defaults to True
:type vSeparator: bool, optional
:param hSeparator: show the horizontal separators, defaults to True
:type hSeparator: bool, optional
:param vHeader: show the vertical header, defaults to True
:type vHeader: bool, optional
:param hHeader: show the horizontal header, defaults to True
:type hHeader: bool, optional
:param sortingEnabled: enable the column sorting, defaults to False
:type sortingEnabled: bool, optional
:param dataPadding: the right column padding, defaults to 1
:type dataPadding: int, optional
'''
cellChanged:pyTTkSignal
@ -280,6 +260,28 @@ class TTkTableWidget(TTkAbstractScrollView):
sortingEnabled=False,
dataPadding=1,
**kwargs) -> None:
'''
:param tableModel: the model for the view to present.
:type tableModel: :py:class:`TTkAbstractTableModel`
:param vSeparator: show the vertical separators, defaults to True
:type vSeparator: bool, optional
:param hSeparator: show the horizontal separators, defaults to True
:type hSeparator: bool, optional
:param vHeader: show the vertical header, defaults to True
:type vHeader: bool, optional
:param hHeader: show the horizontal header, defaults to True
:type hHeader: bool, optional
:param sortingEnabled: enable the column sorting, defaults to False
:type sortingEnabled: bool, optional
:param dataPadding: the right column padding, defaults to 1
:type dataPadding: int, optional
'''
# Signals
# self.itemActivated = pyTTkSignal(TTkTableWidgetItem, int)
# self.itemChanged = pyTTkSignal(TTkTableWidgetItem, int)
@ -569,10 +571,6 @@ class TTkTableWidget(TTkAbstractScrollView):
h = hhs+self._rowsPos[-1]+1
return w,h
# Overridden function
def viewDisplayedSize(self) -> tuple[int, int]:
return self.size()
def clearSelection(self) -> None:
'''
Deselects all selected items.

4
TermTk/TTkWidgets/TTkModelView/tree.py

@ -49,6 +49,10 @@ class TTkTree(TTkAbstractScrollArea):
def __init__(self, *,
treeWidget:TTkTreeWidget=None,
**kwargs) -> None:
'''
:param treeWidget: a custom Tree Widget to be used instead of the default one.
:type treeWidget: :py:class:`TTkTreeWidget`, optional
'''
super().__init__(**kwargs)
kwargs.pop('parent',None)
kwargs.pop('visible',None)

10
TermTk/TTkWidgets/TTkModelView/treewidget.py

@ -160,7 +160,10 @@ class TTkTreeWidget(TTkAbstractScrollView):
widgets: list
firstLine: bool
def __init__(self, *, header=[], sortingEnabled=True, **kwargs) -> None:
def __init__(self, *,
header=[],
sortingEnabled=True,
**kwargs) -> None:
# Signals
self.itemActivated = pyTTkSignal(TTkTreeWidgetItem, int)
self.itemChanged = pyTTkSignal(TTkTreeWidgetItem, int)
@ -198,11 +201,6 @@ class TTkTreeWidget(TTkAbstractScrollView):
# TTkLog.debug(f"{w=} {h=}")
return w,h
# Overridden function
def viewDisplayedSize(self) -> tuple[int, int]:
# TTkLog.debug(f"{self.size()=}")
return self.size()
def clear(self) -> None:
'''clear'''
# Remove all the widgets

3
TermTk/TTkWidgets/TTkPickers/textpicker.py

@ -111,9 +111,6 @@ class _emojiPickerView(TTkAbstractScrollView):
_,_,w,h = self.layout().fullWidgetAreaGeometry()
return w , h
def viewDisplayedSize(self) -> (int, int):
return self.size()
def maximumWidth(self): return 0x10000
def maximumHeight(self): return 0x10000
def minimumWidth(self): return 0

35
TermTk/TTkWidgets/TTkTerminal/terminal.py

@ -28,20 +28,21 @@ from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkWidgets.TTkTerminal.terminalview import TTkTerminalView
class TTkTerminal(TTkAbstractScrollArea):
'''TTkTerminal
__doc__ = '''
:py:class:`TTkTerminal` is a container widget which place :py:class:`TTkTerminalView` in a scrolling area with on-demand scroll bars.
This is a terminal emulator fot TermTk
''' + TTkTerminalView.__doc__
.. warning::
This is an Alpha feature, it is not optimized and the API definition may change in the future
__slots__ = tuple(
['_terminalView'] +
(_forwardedSignals:=[# Forwarded Signals From TTkTreeWidget
'bell', 'titleChanged', 'terminalClosed', 'textSelected',
'termData', 'termResized']) +
(_forwardedMethods:=[# Forwarded Methods From TTkTreeWidget
'termWrite', 'termSize'])
)
_forwardWidget = TTkTerminalView
'''
__slots__ = ('_terminalView',
# Exported methods
'termWrite', 'termSize',
# Exported Signals
'titleChanged', 'bell', 'terminalClosed', 'textSelected',
'termData', 'termResized')
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
kwargs.pop('parent',None)
@ -50,16 +51,8 @@ class TTkTerminal(TTkAbstractScrollArea):
self.setFocusPolicy(TTkK.ClickFocus)
self.setViewport(self._terminalView)
# Export Methods
self.termSize = self._terminalView.termSize
self.termWrite = self._terminalView.termWrite
# Export Signals
self.bell = self._terminalView.bell
self.termData = self._terminalView.termData
self.termResized = self._terminalView.termResized
self.titleChanged = self._terminalView.titleChanged
self.textSelected = self._terminalView.textSelected
for _attr in self._forwardedSignals+self._forwardedMethods:
setattr(self,_attr,getattr(self._terminalView,_attr))
self.terminalClosed = pyTTkSignal(TTkTerminal)
self._terminalView.terminalClosed.connect(lambda : self.terminalClosed.emit(self))

132
TermTk/TTkWidgets/TTkTerminal/terminalview.py

@ -61,6 +61,66 @@ class _termLog():
mouse = lambda _:None
class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
'''
:py:class:`TTkTerminalView` is a terminal emulator fot TermTk
.. warning::
This is an Alpha feature, it is not optimized and the API definition may change in the future
TermShot from: `tests/t.pty/test.pty.006.terminal.07.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tests/t.pty/test.pty.006.terminal.07.py>`_
::
Terminello n.1 [_][^][x]
$ neofetch
`.-::---.. PierCecco@FrankenstOne
.:++++ooooosssoo:. ---------------------------
.+o++::. `.:oos+. OS: LMDE 6 (faye) x86_64
:oo:.` -+oo: Host: Lemur Pro lemp11
`+o/` .::::::-. .++-` Kernel: 6.1.0-26-amd64
`/s/ .yyyyyyyyyyo: +o-` Uptime: 27 days, 2 hours, 33 mins
`so .ss ohyo` :s-: Packages: 2787 (dpkg), 38 (flatpak)
`s/ .ss h m myy/ /s`` Shell: bash 5.2.15
`s: `oo s m Myy+-o:` Resolution: 1920x1080, 3440x1440
`oo :+sdoohyoydyso/. DE: Cinnamon 6.2.9
:o. .:////////++: WM: Mutter (Muffin)
`/++ -:::::- WM Theme: Mint-Y-Dark-Orange (Mint-Y)
`++- Theme: Mint-L-Dark [GTK2/3]
`/+- Icons: Mint-X-Orange [GTK2/3]
.+/. Terminal: tmux
.:+-. CPU: 12th Gen Intel i7-1255U (12) @ 4.700GHz
`--.`` GPU: Intel Alder Lake-UP3 GT2 [Iris Xe Graphics]
Memory: 32111MiB / 39956MiB
23 PierCecco FrankenstOne < 12:24 > (1052) ~/github/Varie/pyTermTk
$
Quickstart:
.. code-block:: python
import TermTk as ttk
root = ttk.TTk(mouseTrack=True)
win = ttk.TTkWindow(parent=root, title="Terminal", size=(80+2,24+4), layout=ttk.TTkGridLayout())
term = ttk.TTkTerminal(parent=win)
th = ttk.TTkTerminalHelper(term=term)
th.runShell()
root.mainloop()
More examples are available :ref:`here <Examples-Terminal>`.
'''
@dataclass
class _Terminal():
bracketedMode: bool = False
@ -78,6 +138,55 @@ class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
reportMove: bool = False
sgrMode: bool = False
bell:pyTTkSignal
'''
This signal is emitted when the `bell <https://en.wikipedia.org/wiki/Bell_character>`__ is received.
'''
terminalClosed:pyTTkSignal
'''
This signal is emitted when the terminl is closed.
'''
titleChanged:pyTTkSignal
'''
This signal is emitted when the terminal title change through OSC "ESC ]0;"
:param title: the new title
:type title: str
'''
textSelected:pyTTkSignal
'''
This signal is emitted when a text is selected.
:param text: the selected text
:type text: :py:class:`ttkString`
'''
termData:pyTTkSignal
'''
This signal is emitted when data event fires.
This happens for example when the user types or pastes into the terminal.
The event value is whatever 'str' results, in a typical setup,
this should be passed on to the backing pty.
This signal is used in :py:class:`TTkTerminalHelper` through :py:meth:`TTkTerminalHelper.attachTTkTerminal`
to frward all the terminal events to the pty interface.
:param data: the event data
:type data: str
'''
termResized:pyTTkSignal
'''
This signal is emitted when the terminal is resized.
:param size: the new size [width, height] of the terminal
:type size: (int,int)
'''
__slots__ = (
'_termLoop', '_newSize',
'_clipboard', '_selecting',
@ -89,13 +198,14 @@ class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
'titleChanged', 'terminalClosed', 'textSelected',
'termData','termResized')
def __init__(self, **kwargs) -> None:
#Signals
self.bell = pyTTkSignal()
self.terminalClosed = pyTTkSignal()
self.titleChanged = pyTTkSignal(str)
self.textSelected = pyTTkSignal(str)
self.textSelected = pyTTkSignal(TTkString)
self.termData = pyTTkSignal(str)
self.termResized = pyTTkSignal(int,int)
self._newSize = None
self._terminal = TTkTerminalView._Terminal()
self._keyboard = TTkTerminalView._Keyboard()
@ -114,6 +224,7 @@ class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
next(self._termLoop)
self._termLoop.send("")
# Do NOT use super() due to the multiple imports
TTkAbstractScrollView.__init__(self, **kwargs)
w,h = self.size()
@ -140,10 +251,13 @@ class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
h += len(self._screen_current._bufferedLines)
return w,h
def viewDisplayedSize(self) -> (int, int):
return self.size()
def termSize(self) -> tuple[int,int]:
'''
This property holds the size of the terminal
def termSize(self):
:return: a tuple of 2 integers (width, height)
:rtype: tuple
'''
return self.size()
def resizeEvent(self, w: int, h: int):
@ -172,7 +286,13 @@ class TTkTerminalView(TTkAbstractScrollView, _TTkTerminal_CSI_DEC):
re_XTWINOPS = re.compile(r'^')
def termWrite(self, data):
def termWrite(self, data:str) -> None:
'''
Write data to the terminal.
:params data: the data to write
:type data: str
'''
if data:
self._termLoop.send(data)

23
TermTk/TTkWidgets/button.py

@ -55,17 +55,6 @@ class TTkButton(TTkWidget):
Line
Demo: `formwidgets.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/formwidgets.py>`_
:param str text: the text shown on the button, defaults to ""
:type text: str, optional
:param bool border: the border of the button, defaults to "False"
:type border: bool, optional
:param bool checked: checked status if the button is checkable, defaults to "False"
:type checked: bool, optional
:param bool checkable: define if the button is checkable, defaults to "False"
:type checkable: bool, optional
'''
clicked:pyTTkSignal
@ -118,6 +107,18 @@ class TTkButton(TTkWidget):
checked:bool=False,
checkable:bool=False,
**kwargs) -> None:
'''
:param str text: the text shown on the button, defaults to ""
:type text: str, optional
:param bool border: the border of the button, defaults to "False"
:type border: bool, optional
:param bool checked: checked status if the button is checkable, defaults to "False"
:type checked: bool, optional
:param bool checkable: define if the button is checkable, defaults to "False"
:type checkable: bool, optional
'''
self._text = TTkString(text).split('\n')
textWidth = max(t.termWidth() for t in self._text)
self._border = border

22
TermTk/TTkWidgets/checkbox.py

@ -50,16 +50,6 @@ class TTkCheckbox(TTkWidget):
[/]CheckBox
:Demo: `formwidgets.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/formwidgets.py>`_
:param str text: the text shown on the checkbox, defaults to ""
:type text: str, optional
:param checked: Checked status, defaults to "False"
:type checked: bool, optional
:param checkStatus: If defined, override the option defined in the 'checked' field otherwise defaults to :py:class:`TTkK.CheckState.Checked` or :py:class:`TTkK.CheckState.Unchecked` based on the checked status
:type checkStatus: :py:class:`TTkK.CheckState` , optional
:param tristate: Tristate status, if enabled the checkbox is able to assume the :py:class:`TTkK.CheckState.PartiallyChecked` status, defaults to "False"
:type tristate: bool, optional
'''
clicked:pyTTkSignal
@ -108,6 +98,18 @@ class TTkCheckbox(TTkWidget):
checkStatus:TTkK.CheckState = None,
tristate:bool=False,
**kwargs) -> None:
'''
:param str text: the text shown on the checkbox, defaults to ""
:type text: str, optional
:param checked: Checked status, defaults to "False"
:type checked: bool, optional
:param checkStatus: If defined, override the option defined in the 'checked' field otherwise defaults to :py:class:`TTkK.CheckState.Checked` or :py:class:`TTkK.CheckState.Unchecked` based on the checked status
:type checkStatus: :py:class:`TTkK.CheckState` , optional
:param tristate: Tristate status, if enabled the checkbox is able to assume the :py:class:`TTkK.CheckState.PartiallyChecked` status, defaults to "False"
:type tristate: bool, optional
'''
# Define Signals
self.stateChanged = pyTTkSignal(TTkK.CheckState)
self.clicked = pyTTkSignal(bool)

25
TermTk/TTkWidgets/combobox.py

@ -52,17 +52,6 @@ class TTkComboBox(TTkContainer):
[ Text [^]
:param list: the list of the items selectable by this combobox, defaults to "[]"
:type list: list(str), optional
:param insertPolicy: the policy used to determine where user-inserted items should appear in the combobox, defaults to :py:class:`TTkConstant.InsertPolicy.InsertAtBottom`
:type insertPolicy: :py:class:`TTkConstant.InsertPolicy`, optional
:param textAlign: This enum type is used to define the text alignment, defaults to :py:class:`TTkConstant.Alignment.CENTER_ALIGN`
:tye textAlign: :py:class:`TTkConstant.Alignment`, optional
:param editable: This property holds whether the combo box can be edited by the user, defaults to False
:type editable: bool, optional
'''
classStyle = {
@ -84,6 +73,20 @@ class TTkComboBox(TTkContainer):
textAlign:TTkK.Alignment = TTkK.CENTER_ALIGN,
editable:bool = False,
**kwargs) -> None:
'''
:param list: the list of the items selectable by this combobox, defaults to "[]"
:type list: list(str), optional
:param insertPolicy: the policy used to determine where user-inserted items should appear in the combobox, defaults to :py:class:`TTkConstant.InsertPolicy.InsertAtBottom`
:type insertPolicy: :py:class:`TTkConstant.InsertPolicy`, optional
:param textAlign: This enum type is used to define the text alignment, defaults to :py:class:`TTkConstant.Alignment.CENTER_ALIGN`
:tye textAlign: :py:class:`TTkConstant.Alignment`, optional
:param editable: This property holds whether the combo box can be edited by the user, defaults to False
:type editable: bool, optional
'''
# Define Signals
self.currentIndexChanged = pyTTkSignal(int)
self.currentTextChanged = pyTTkSignal(str)

37
TermTk/TTkWidgets/container.py

@ -96,23 +96,6 @@ class TTkContainer(TTkWidget):
root.mainloop()
:param layout: the layout of this widget, optional, defaults to :py:class:`TTkLayout`
:type layout: :mod:`TermTk.TTkLayouts`
:param padding: the padding (top, bottom, left, right) of the widget, defaults to (0,0,0,0)
:type padding: :py:class:`TTkPadding`
:param paddingTop: the Top padding, override Top padding if already defined, optional, default=0 if padding is not defined
:type paddingTop: int
:param paddingBottom: the Bottom padding, override Bottom padding if already defined, optional, default=0 if padding is not defined
:type paddingBottom: int
:param paddingLeft: the Left padding, override Left padding if already defined, optional, default=0 if padding is not defined
:type paddingLeft: int
:param paddingRight: the Right padding, override Right padding if already defined, optional, default=0 if padding is not defined
:type paddingRight: int
:param bool forwardStyle: [**Experimental**] any change of style will reflect the children, defaults to False
:type forwardStyle: bool
'''
__slots__ = (
@ -129,7 +112,25 @@ class TTkContainer(TTkWidget):
paddingRight:int = 0,
forwardStyle:bool = False,
**kwargs) -> None:
'''
:param layout: the layout of this widget, optional, defaults to :py:class:`TTkLayout`
:type layout: :mod:`TermTk.TTkLayouts`
:param padding: the padding (top, bottom, left, right) of the widget, defaults to (0,0,0,0)
:type padding: :py:class:`TTkPadding`
:param paddingTop: the Top padding, override Top padding if already defined, optional, default=0 if padding is not defined
:type paddingTop: int
:param paddingBottom: the Bottom padding, override Bottom padding if already defined, optional, default=0 if padding is not defined
:type paddingBottom: int
:param paddingLeft: the Left padding, override Left padding if already defined, optional, default=0 if padding is not defined
:type paddingLeft: int
:param paddingRight: the Right padding, override Right padding if already defined, optional, default=0 if padding is not defined
:type paddingRight: int
:param bool forwardStyle: [**Experimental**] any change of style will reflect the children, defaults to False
:type forwardStyle: bool
'''
self._forwardStyle = forwardStyle
if padding:
self._padt = padding[0]

17
TermTk/TTkWidgets/frame.py

@ -44,15 +44,7 @@ class TTkFrame(TTkContainer):
Demo2: `splitter.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/splitter.py>`_
:param title: the title displayed at the top border of the frame, defaults to ""
:type title: TTkString, optional
:param titleAlign: the position of the title, defaults to :py:class:`TTkK.Alignment.CENTER_ALIGN`
:type titleAlign: :py:class:`TTkK.Alignment`, optional
:param border: Enable/Disable the border, defaults to **True**
:type border: bool, optional
'''
classStyle = {
'default': {'color': TTkColor.fg("#dddddd")+TTkColor.bg("#222222"),
'borderColor': TTkColor.RST},
@ -68,6 +60,15 @@ class TTkFrame(TTkContainer):
border:bool=True,
titleAlign:TTkK.Alignment=TTkK.CENTER_ALIGN,
**kwargs) -> None:
'''
:param title: the title displayed at the top border of the frame, defaults to ""
:type title: TTkString, optional
:param titleAlign: the position of the title, defaults to :py:class:`TTkK.Alignment.CENTER_ALIGN`
:type titleAlign: :py:class:`TTkK.Alignment`, optional
:param border: Enable/Disable the border, defaults to **True**
:type border: bool, optional
'''
self._titleAlign = titleAlign
self._title = TTkString(title)
self._border = border

61
TermTk/TTkWidgets/list_.py

@ -27,51 +27,42 @@ from TermTk.TTkWidgets.listwidget import TTkListWidget
from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
class TTkList(TTkAbstractScrollArea):
'''TTkList'''
__slots__ = (
'_listView', 'itemClicked', 'textClicked',
# Forwarded Methods
'items',
'dragDropMode', 'setDragDropMode',
'addItem', 'addItemAt', 'addItems', 'addItemsAt',
'indexOf', 'itemAt', 'moveItem',
'removeAt', 'removeItem', 'removeItems',
'selectionMode', 'setSelectionMode', 'selectedItems', 'selectedLabels',
'setCurrentRow', 'setCurrentItem', )
__doc__ = '''
:py:class:`TTkList` is a container widget which place :py:class:`TTkListWidget` in a scrolling area with on-demand scroll bars.
''' + TTkListWidget.__doc__
__slots__ = tuple(
['_listView'] +
(_forwardedSignals:=[ # Forwarded Signals From TTkTable
'itemClicked', 'textClicked']) +
(_forwardedMethods:=[ # Forwarded Methods From TTkTable
'items',
'dragDropMode', 'setDragDropMode',
'addItem', 'addItemAt', 'addItems', 'addItemsAt',
'indexOf', 'itemAt', 'moveItem',
'removeAt', 'removeItem', 'removeItems',
'selectionMode', 'setSelectionMode', 'selectedItems', 'selectedLabels',
'setCurrentRow', 'setCurrentItem'])
)
_forwardWidget = TTkListWidget
def __init__(self, *,
listWidget:TTkListWidget=None,
selectionMode:int=TTkK.SingleSelection,
dragDropMode:TTkK.DragDropMode=TTkK.DragDropMode.NoDragDrop,
**kwargs) -> None:
TTkAbstractScrollArea.__init__(self, **kwargs)
'''
:param listWidget: a custom List Widget to be used instead of the default one.
:type listWidget: :py:class:`TTkListWidget`, optional
'''
self._listView = listWidget if listWidget else TTkListWidget(
selectionMode=selectionMode,
dragDropMode=dragDropMode,
**kwargs|{'parent':None,'visible':True})
super().__init__(**kwargs)
self.setViewport(self._listView)
self.itemClicked = self._listView.itemClicked
self.textClicked = self._listView.textClicked
# self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
# Forwearded Methods
self.items = self._listView.items
self.indexOf = self._listView.indexOf
self.itemAt = self._listView.itemAt
self.moveItem = self._listView.moveItem
self.removeAt = self._listView.removeAt
self.removeItem = self._listView.removeItem
self.removeItems = self._listView.removeItems
self.addItem = self._listView.addItem
self.addItems = self._listView.addItems
self.addItemAt = self._listView.addItemAt
self.addItemsAt = self._listView.addItemsAt
self.selectionMode = self._listView.selectionMode
self.setSelectionMode = self._listView.setSelectionMode
self.selectedItems = self._listView.selectedItems
self.selectedLabels = self._listView.selectedLabels
self.setCurrentRow = self._listView.setCurrentRow
self.setCurrentItem = self._listView.setCurrentItem
self.dragDropMode = self._listView.dragDropMode
self.setDragDropMode = self._listView.setDragDropMode
for _attr in self._forwardedSignals+self._forwardedMethods:
setattr(self,_attr,getattr(self._listView,_attr))

75
TermTk/TTkWidgets/listwidget.py

@ -108,20 +108,81 @@ class TTkAbstractListItem(TTkWidget):
canvas.drawTTkString(pos=(0,0), width=w, color=color ,text=self._text)
class TTkListWidget(TTkAbstractScrollView):
'''
A :py:class:`TTkListWidget` implements a list view that displays a set of selectable items.
::
S-0) --Zero-3- officia
S-1) ad ipsum
S-2) irure nisi
S-3) minim --Zero-3-
S-4) ea sunt
S-5) qui mollit
S-6) magna sunt
S-7) sunt officia
Quickstart:
.. code-block:: python
import TermTk as ttk
root = ttk.TTk(layout=ttk.TTkHBoxLayout(), mouseTrack=True)
ttk.TTkList(parent=root, items=["Item 1","Item 2","Item 3"])
ttk.TTkList(parent=root, items=[f"Item 0x{i:03X}" for i in range(100)])
ttkList = ttk.TTkList(parent=root)
ttkList.addItems([f"Item 0x{i:04X}" for i in range(200)])
root.mainloop()
'''
itemClicked:pyTTkSignal
'''
This signal is emitted whenever an item is clicked.
:param item: the item selected
:type item: :py:class:`TTkAbstractListItem`
'''
textClicked:pyTTkSignal
'''
This signal is emitted whenever an item is clicked.
:param text: the text of the item selected
:type text: str
'''
@dataclass(frozen=True)
class _DropListData:
widget: TTkAbstractScrollView
items: list
'''TTkListWidget'''
__slots__ = ('itemClicked', 'textClicked',
'_selectedItems', '_selectionMode',
'_highlighted', '_items',
'_dragPos', '_dndMode')
def __init__(self, *,
selectionMode:int=TTkK.SingleSelection,
items:list[str]=[],
selectionMode:int=TTkK.SelectionMode.SingleSelection,
dragDropMode:TTkK.DragDropMode=TTkK.DragDropMode.NoDragDrop,
**kwargs) -> None:
'''
:param items: Use this field to intialize the :py:class:`TTkListWidget` with the entries in the items list, defaults to "[]"
:type items: list[str], optional
:param selectionMode: This property controls whether the user can select one or many items, defaults to :py:class:`TTkK.SelectionMode.SingleSelection`.
:type selectionMode: :py:class:`TTkK.SelectionMode`, optional
:param dragDropMode: This property holds the drag and drop event the view will act upon, defaults to :py:class:`TTkK.DragDropMode.NoDragDrop`.
:type dragDropMode: :py:class:`TTkK.DragDropMode`, optional
'''
# Signals
self.itemClicked = pyTTkSignal(TTkAbstractListItem)
self.textClicked = pyTTkSignal(str)
# Default Class Specific Values
self._selectionMode = selectionMode
self._selectedItems = []
@ -129,11 +190,9 @@ class TTkListWidget(TTkAbstractScrollView):
self._highlighted = None
self._dragPos = None
self._dndMode = dragDropMode
# Signals
self.itemClicked = pyTTkSignal(TTkAbstractListItem)
self.textClicked = pyTTkSignal(str)
# Init Super
super().__init__(**kwargs)
self.addItems(items)
self.viewChanged.connect(self._viewChangedHandler)
self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
@ -207,16 +266,14 @@ class TTkListWidget(TTkAbstractScrollView):
_,_,w,h = self.layout().fullWidgetAreaGeometry()
return w, h
def viewDisplayedSize(self) -> (int, int):
return self.size()
def addItem(self, item, data=None):
'''addItem'''
self.addItemAt(item, len(self._items), data)
def addItems(self, items):
'''addItems'''
self.addItemAt(items, len(self._items))
for item in items:
self.addItem(item)
def _placeItems(self):
minw = self.width()

3
TermTk/TTkWidgets/menu.py

@ -346,9 +346,6 @@ class _TTkMenuAreaWidget(TTkAbstractScrollView):
_,_,w,h = self.layout().fullWidgetAreaGeometry()
return w , h
def viewDisplayedSize(self) -> tuple:
return self.size()
def maximumWidth(self): return 0x10000
def maximumHeight(self): return 0x10000
def minimumWidth(self): return 0

19
TermTk/TTkWidgets/radiobutton.py

@ -43,14 +43,6 @@ class TTkRadioButton(TTkWidget):
Demo: `formwidgets.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/formwidgets.py>`_
:param str text: the text shown on the radio button, defaults to ""
:type text: str, optional
:param str radiogroup: the text used to group the RadioButtons, only one checked status is allowed in between all the radio buttons with the same radiogroup, defaults to "DefaultGroup"
:type radiogroup: str, optional
:param bool checked: Checked status, defaults to "False"
:type checked: bool, optional
:param checkStatus: If defined, override the option defined in the 'checked' field otherwise defaults to :py:class:`TTkK.CheckState.Checked` or :py:class:`TTkK.CheckState.Unchecked` based on the checked status
:type checkStatus: :py:class:`TTkK.CheckState` , optional
'''
clicked:pyTTkSignal
@ -82,6 +74,17 @@ class TTkRadioButton(TTkWidget):
checkStatus:TTkK.CheckState = None,
text:TTkString='',
**kwargs) -> None:
'''
:param str text: the text shown on the radio button, defaults to ""
:type text: str, optional
:param str radiogroup: the text used to group the RadioButtons, only one checked status is allowed in between all the radio buttons with the same radiogroup, defaults to "DefaultGroup"
:type radiogroup: str, optional
:param bool checked: Checked status, defaults to "False"
:type checked: bool, optional
:param checkStatus: If defined, override the option defined in the 'checked' field otherwise defaults to :py:class:`TTkK.CheckState.Checked` or :py:class:`TTkK.CheckState.Unchecked` based on the checked status
:type checkStatus: :py:class:`TTkK.CheckState` , optional
'''
# Define Signals
self.clicked = pyTTkSignal()
self.toggled = pyTTkSignal(bool)

3
TermTk/TTkWidgets/scrollarea.py

@ -43,9 +43,6 @@ class _TTkAreaWidget(TTkAbstractScrollView):
_,_,w,h = self.layout().fullWidgetAreaGeometry()
return w , h
def viewDisplayedSize(self) -> (int, int):
return self.size()
def maximumWidth(self): return 0x10000
def maximumHeight(self): return 0x10000
def minimumWidth(self): return 0

337
TermTk/TTkWidgets/texedit.py

@ -77,9 +77,6 @@ class _TTkTextEditViewLineNumber(TTkAbstractScrollView):
else:
return self.size()
def viewDisplayedSize(self) -> (int, int):
return self.size()
def paintEvent(self, canvas):
if not self._textWrap: return
_, oy = self.getViewOffsets()
@ -104,7 +101,62 @@ class _TTkTextEditViewLineNumber(TTkAbstractScrollView):
canvas.drawChar(pos=(w-1,y), char='', color=separatorColor)
class TTkTextEditView(TTkAbstractScrollView):
'''TTkTextEditView'''
'''
:py:class:`TTkTextEditView`
::
0"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ╥ ║
<incididunt ut labore et dolore magna aliqua.
1
2Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqu
<ip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
<velit esse cillum dolore eu fugiat nulla pariatur.
3
4Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deseru
<nt mollit anim id est laborum." ╨ ║
Demo: `textedit.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/textedit.py>`_
(`Try Online <https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html?fileUri=https://raw.githubusercontent.com/ceccopierangiolieugenio/pyTermTk/main/demo/showcase/textedit.py>`__)
`ttkdesigner Tutorial <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tutorial/ttkDesigner/textEdit>`_
'''
currentColorChanged:pyTTkSignal
'''
This signal is emitted if the current character color has changed,
for example caused by a change of the cursor position.
:param color: the new color
:type color: :py:class:`TTkColor`
'''
undoAvailable:pyTTkSignal
'''
This signal is emitted whenever undo operations become available (available is true)
or unavailable (available is false).
:param available: the availability of undo
:type available: bool
'''
redoAvailable:pyTTkSignal
'''
This signal is emitted whenever redo operations become available (available is true)
or unavailable (available is false).
:param available: the availability of redo
:type available: bool
'''
textChanged:pyTTkSignal
'''
This signal is emitted whenever the document's content changes;
for example, when text is inserted or deleted, or when formatting is applied.
'''
classStyle = {
'default': {'color': TTkColor.fg("#dddddd")+TTkColor.bg("#222222"),
@ -135,24 +187,38 @@ class TTkTextEditView(TTkAbstractScrollView):
'undoAvailable', 'redoAvailable',
'textChanged'
)
'''
in order to support the line wrap, I need to divide the full data text in;
_textDocument = the entire text divided in lines, easy to add/remove/append lines
_textWrap._lines = an array of tuples for each displayed line with a pointer to a
specific line and its slice to be shown at this coordinate;
[ (line, (posFrom, posTo)), ... ]
This is required to support the wrap feature
'''
# in order to support the line wrap, I need to divide the full data text in;
# _textDocument = the entire text divided in lines, easy to add/remove/append lines
# _textWrap._lines = an array of tuples for each displayed line with a pointer to a
# specific line and its slice to be shown at this coordinate;
# [ (line, (posFrom, posTo)), ... ]
# This is required to support the wrap feature
def __init__(self, *,
readOnly:bool=False,
multiLine:bool=True,
document:TTkTextDocument=None,
**kwargs) -> None:
super().__init__(**kwargs)
'''
:param lineNumber: Show the line number on the left, defaults to **False**
:type lineNumber: bool, optional
:param readOnly: In a read-only text edit the user can only navigate through the text and select text; modifying the text is not possible, defaults to **True**
:type readOnly: bool, optional
:param multiLine: In a multiline text edit the user can split the text in multiple lines, defaults to **True**
:type multiLine: bool, optional
:param document: If required an external Document can be used in this text editor, this option is useful if multiple editors share the same document as in the `demo <https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html?fileUri=https://raw.githubusercontent.com/ceccopierangiolieugenio/pyTermTk/main/demo/showcase/textedit.py>`__, defaults to a new Document
:type document: :py:class:`TTkTextDocument`, optional
'''
self.currentColorChanged = pyTTkSignal(TTkColor)
self.undoAvailable = pyTTkSignal(bool)
self.redoAvailable = pyTTkSignal(bool)
self.textChanged = pyTTkSignal()
self._readOnly = readOnly
self._multiLine = multiLine
self._multiCursor = True
@ -165,6 +231,9 @@ class TTkTextEditView(TTkAbstractScrollView):
self._textCursor = None
self._textWrap = None
self._clipboard = TTkClipboard()
super().__init__(**kwargs)
self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
self.setDocument(document if document else TTkTextDocument())
self.disableWidgetCursor(self._readOnly)
@ -366,9 +435,6 @@ class TTkTextEditView(TTkAbstractScrollView):
elif self.lineWrapMode() == TTkK.FixedWidth:
return self.wrapWidth(), self._textWrap.size()
def viewDisplayedSize(self) -> (int, int):
return self.size()
def _pushCursor(self):
ox, oy = self.getViewOffsets()
@ -625,181 +691,20 @@ class TTkTextEditView(TTkAbstractScrollView):
canvas.drawVLine(pos=(self._textWrap._wrapWidth,0), size=h, color=lineColor)
class TTkTextEdit(TTkAbstractScrollArea):
'''TTkTextEdit
::
0"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ╥ ║
<incididunt ut labore et dolore magna aliqua.
1
2Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliqu
<ip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
<velit esse cillum dolore eu fugiat nulla pariatur.
3
4Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deseru
<nt mollit anim id est laborum." ╨ ║
Demo: `textedit.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/demo/showcase/textedit.py>`_
(`Try Online <https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html?fileUri=https://raw.githubusercontent.com/ceccopierangiolieugenio/pyTermTk/main/demo/showcase/textedit.py>`__)
`ttkdesigner Tutorial <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tutorial/ttkDesigner/textEdit>`_
:param lineNumber: Show the line number on the left, defaults to **False**
:type lineNumber: bool, optional
:param readOnly: In a read-only text edit the user can only navigate through the text and select text; modifying the text is not possible, defaults to **True**
:type readOnly: bool, optional
:param multiLine: In a multiline text edit the user can split the text in multiple lines, defaults to **True**
:type multiLine: bool, optional
:param document: If required an external Document can be used in this text editor, this option is useful if multiple editors share the same document as in the `demo <https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html?fileUri=https://raw.githubusercontent.com/ceccopierangiolieugenio/pyTermTk/main/demo/showcase/textedit.py>`__, defaults to a new Document
:type document: :py:class:`TTkTextDocument`, optional
__doc__ = '''
:py:class:`TTkTextEdit` is a container widget which place :py:class:`TTkTextEditView` in a scrolling area with on-demand scroll bars.
.. py:method:: toAnsi()
.. py:method:: clear()
This method is forwarded to :meth:`TTkTextEditView.clear`
.. py:method:: setText(text)
This method is forwarded to :meth:`TTkTextEditView.setText`
.. py:method:: append(text)
This method is forwarded to :meth:`TTkTextEditView.append`
.. py:method:: isReadOnly()
This method is forwarded to :meth:`TTkTextEditView.isReadOnly`
.. py:method:: setReadOnly(ro)
This method is forwarded to :meth:`TTkTextEditView.setReadOnly`
.. py:method:: document()
This method is forwarded to :meth:`TTkTextEditView.document`
.. py:method:: wrapWidth()
This method is forwarded to :meth:`TTkTextEditView.wrapWidth`
.. py:method:: setWrapWidth(width)
This method is forwarded to :meth:`TTkTextEditView.setWrapWidth`
.. py:method:: multiLine()
This method is forwarded to :meth:`TTkTextEditView.multiLine`
.. py:method:: lineWrapMode()
This method is forwarded to :meth:`TTkTextEditView.lineWrapMode`
.. py:method:: setLineWrapMode(mode)
This method is forwarded to :meth:`TTkTextEditView.setLineWrapMode`
.. py:method:: wordWrapMode()
This method is forwarded to :meth:`TTkTextEditView.wordWrapMode`
.. py:method:: setWordWrapMode(mode)
This method is forwarded to :meth:`TTkTextEditView.setWordWrapMode`
.. py:method:: textCursor()
This method is forwarded to :meth:`TTkTextEditView.textCursor`
.. py:method:: setColor(color)
This method is forwarded to :meth:`TTkTextEditView.setColor`
.. py:method:: cut()
This method is forwarded to :meth:`TTkTextEditView.cut`
.. py:method:: copy()
This method is forwarded to :meth:`TTkTextEditView.copy`
.. py:method:: paste()
This method is forwarded to :meth:`TTkTextEditView.paste`
.. py:method:: undo()
This method is forwarded to :meth:`TTkTextEditView.undo`
.. py:method:: redo()
This method is forwarded to :meth:`TTkTextEditView.redo`
.. py:method:: isUndoAvailable()
This method is forwarded to :meth:`TTkTextEditView.isUndoAvailable`
.. py:method:: isRedoAvailable()
This method is forwarded to :meth:`TTkTextEditView.isRedoAvailable`
.. py:method:: toAnsi()
This method is forwarded to :meth:`TTkTextEditView.toAnsi`
.. py:method:: toRawText()
This method is forwarded to :meth:`TTkTextEditView.toRawText`
.. py:method:: toPlainText()
This method is forwarded to :meth:`TTkTextEditView.toPlainText`
'''
currentColorChanged:pyTTkSignal
'''
This signal is emitted if the current character color has changed,
for example caused by a change of the cursor position.
:param color: the new color
:type color: :py:class:`TTkColor`
'''
undoAvailable:pyTTkSignal
'''
This signal is emitted whenever undo operations become available (available is true)
or unavailable (available is false).
:param available: the availability of undo
:type available: bool
'''
redoAvailable:pyTTkSignal
'''
This signal is emitted whenever redo operations become available (available is true)
or unavailable (available is false).
:param available: the availability of redo
:type available: bool
'''
textChanged:pyTTkSignal
'''
This signal is emitted whenever the document's content changes;
for example, when text is inserted or deleted, or when formatting is applied.
'''
''' + TTkTextEditView.__doc__
__slots__ = (
'_textEditView',
'_lineNumberView', '_lineNumber',
['_textEditView',
'_lineNumberView', '_lineNumber'] +
(_forwardedSignals:=[ # Forwarded Signals From TTkTexteditView
# Signals
'focusChanged', 'currentColorChanged',
'undoAvailable', 'redoAvailable',
'textChanged']) +
(_forwardedMethods:=[ # Forwarded Methods From TTkTexteditView
# Forwarded Methods
'clear', 'setText', 'append', 'isReadOnly', 'setReadOnly', 'document',
'wrapWidth', 'setWrapWidth',
@ -811,11 +716,10 @@ class TTkTextEdit(TTkAbstractScrollArea):
'undo', 'redo', 'isUndoAvailable', 'isRedoAvailable',
# Export Methods,
'toAnsi', 'toRawText', 'toPlainText', # 'toHtml', 'toMarkdown',
# Signals
'focusChanged', 'currentColorChanged',
'undoAvailable', 'redoAvailable',
'textChanged'
])
)
_forwardWidget = TTkTextEditView
def __init__(self, *,
# TTkWidget init
parent:TTkWidget=None,
@ -831,6 +735,14 @@ class TTkTextEdit(TTkAbstractScrollArea):
lineNumber:bool=False,
lineNumberStarting:int=0,
**kwargs) -> None:
'''
:param textEditView: a custom TextEdit View to be used instead of the default one.
:type textEditView: :py:class:`TTkTextEditView`, optional
:param lineNumber: show the line number on the left side, defaults to False
:type lineNumber: bool, optional
:param lineNumberStarting: set the starting number of the left line number column, defaults to 0
:type lineNumberStarting: int, optional
'''
super().__init__(parent=parent, visible=visible, **kwargs)
self._textEditView = textEditView if textEditView else TTkTextEditView(readOnly=readOnly, multiLine=multiLine, document=document)
# self.setFocusPolicy(self._textEditView.focusPolicy())
@ -844,44 +756,11 @@ class TTkTextEdit(TTkAbstractScrollArea):
textEditLayout.addWidget(self._lineNumberView,0,0)
self.setViewport(textEditLayout)
self.clear = self._textEditView.clear
self.setText = self._textEditView.setText
self.append = self._textEditView.append
self.document = self._textEditView.document
self.isReadOnly = self._textEditView.isReadOnly
self.setReadOnly = self._textEditView.setReadOnly
self.textCursor = self._textEditView.textCursor
self.setFocus = self._textEditView.setFocus
self.multiLine = self._textEditView.multiLine
self.setColor = self._textEditView.setColor
self.cut = self._textEditView.cut
self.copy = self._textEditView.copy
self.paste = self._textEditView.paste
self.undo = self._textEditView.undo
self.redo = self._textEditView.redo
self.isUndoAvailable = self._textEditView.isUndoAvailable
self.isRedoAvailable = self._textEditView.isRedoAvailable
# Forward export methods
self.toAnsi = self._textEditView.toAnsi
self.toRawText = self._textEditView.toRawText
self.toPlainText = self._textEditView.toPlainText
# self.toHtml = self._textEditView.toHtml
# self.toMarkdown = self._textEditView.toMarkdown
# Forward Wrap Methods
self.wrapWidth = self._textEditView.wrapWidth
self.setWrapWidth = self._textEditView.setWrapWidth
self.lineWrapMode = self._textEditView.lineWrapMode
self.setLineWrapMode = self._textEditView.setLineWrapMode
self.wordWrapMode = self._textEditView.wordWrapMode
self.setWordWrapMode = self._textEditView.setWordWrapMode
# Forward Signals
self.focusChanged = self._textEditView.focusChanged
self.currentColorChanged = self._textEditView.currentColorChanged
self.undoAvailable = self._textEditView.undoAvailable
self.redoAvailable = self._textEditView.redoAvailable
self.textChanged = self._textEditView.textChanged
for _attr in self._forwardedSignals+self._forwardedMethods:
setattr(self,_attr,getattr(self._textEditView,_attr))
def textEditView(self):
'''textEditView'''
return self._textEditView
def getLineNumber(self):
@ -894,10 +773,12 @@ class TTkTextEdit(TTkAbstractScrollArea):
self._lineNumberView.setVisible(ln)
def lineNumberStarting(self):
'''lineNumberStarting'''
return self._lineNumberView._startingNumber
@pyTTkSlot(int)
def setLineNumberStarting(self, starting):
'''setLineNumberStarting'''
self._lineNumberView._startingNumber = starting
self._lineNumberView._wrapChanged()

91
TermTk/TTkWidgets/widget.py

@ -61,50 +61,6 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
The TTkWidget class is the base class of all user interface objects
:param name: the name of the widget, defaults to ""
:type name: str, optional
:param parent: the parent widget, defaults to None
:type parent: :py:class:`TTkWidget`, optional
:param x: the x position, defaults to 0
:type x: int, optional
:param y: the y position, defaults to 0
:type y: int, optional
:param pos: the [x,y] position (override the previously defined x, y), defaults to (x,y)
:type pos: (int,int), optional
:param width: the width of the widget, defaults to 0
:type width: int, optional
:param height: the height of the widget, defaults to 0
:type height: int, optional
:param size: the size [width, height] of the widget (override the previously defined sizes), defaults to (width,height)
:type size: (int,int), optional
:param maxWidth: the maxWidth of the widget, defaults to 0x10000
:type maxWidth: int, optional
:param maxHeight: the maxHeight of the widget, defaults to 0x10000
:type maxHeight: int, optional
:param maxSize: the max [width,height] of the widget, optional, defaults to (maxWidth,maxHeight)
:type maxSize: (int,int), optional
:param minWidth: the minWidth of the widget, defaults to 0
:type minWidth: int, optional
:param minHeight: the minHeight of the widget, defaults to 0
:type minHeight: int, optional
:param minSize: the minSize [width,height] of the widget, optional, defaults to (minWidth,minHeight)
:type minSize: (int,int), optional
:param toolTip: This property holds the widget's tooltip, defaults to ''
:type toolTip: :py:class:`TTkString`, optional
:param style: this field hold the custom style to be used by this widget
:type style: dict, optional
:param addStyle: this field is required to override/merge the new style on top of the current one, useful if only few params need to be changed
:type addStyle: dict, optional
:param visible: the visibility, optional, defaults to True
:type visible: bool, optional
:param enabled: the ability to handle input events, optional, defaults to True
:type enabled: bool, optional
'''
focusChanged:pyTTkSignal
@ -182,7 +138,52 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
style : dict = None,
addStyle : dict = None,
**kwargs) -> None:
'''
:param name: the name of the widget, defaults to ""
:type name: str, optional
:param parent: the parent widget, defaults to None
:type parent: :py:class:`TTkWidget`, optional
:param x: the x position, defaults to 0
:type x: int, optional
:param y: the y position, defaults to 0
:type y: int, optional
:param pos: the [x,y] position (override the previously defined x, y), defaults to (x,y)
:type pos: (int,int), optional
:param width: the width of the widget, defaults to 0
:type width: int, optional
:param height: the height of the widget, defaults to 0
:type height: int, optional
:param size: the size [width, height] of the widget (override the previously defined sizes), defaults to (width,height)
:type size: (int,int), optional
:param maxWidth: the maxWidth of the widget, defaults to 0x10000
:type maxWidth: int, optional
:param maxHeight: the maxHeight of the widget, defaults to 0x10000
:type maxHeight: int, optional
:param maxSize: the max [width,height] of the widget, optional, defaults to (maxWidth,maxHeight)
:type maxSize: (int,int), optional
:param minWidth: the minWidth of the widget, defaults to 0
:type minWidth: int, optional
:param minHeight: the minHeight of the widget, defaults to 0
:type minHeight: int, optional
:param minSize: the minSize [width,height] of the widget, optional, defaults to (minWidth,minHeight)
:type minSize: (int,int), optional
:param toolTip: This property holds the widget's tooltip, defaults to ''
:type toolTip: :py:class:`TTkString`, optional
:param style: this field hold the custom style to be used by this widget
:type style: dict, optional
:param addStyle: this field is required to override/merge the new style on top of the current one, useful if only few params need to be changed
:type addStyle: dict, optional
:param visible: the visibility, optional, defaults to True
:type visible: bool, optional
:param enabled: the ability to handle input events, optional, defaults to True
:type enabled: bool, optional
'''
if kwargs:
TTkLog.warn(f"Unhandled init params {self.__class__.__name__} -> {kwargs}")

3
docs/source/conf.py

@ -130,7 +130,8 @@ html_permalinks_icon = '<span>🌶</span>'
html_theme_options = {
"home_page_in_toc": True,
"use_fullscreen_button": True,
"show_toc_level": 2,
# "toc_title": "{your-title}",
"show_toc_level": 3,
"repository_url": "https://github.com/ceccopierangiolieugenio/pyTermTk",
}

8
docs/source/sphinx_modules/sphinx_PyRefRole_hacked.py

@ -57,20 +57,20 @@ def setup(app: Sphinx) -> ExtensionMetadata:
for _module in sys.modules:
_parseModules(sys.modules[_module])
for x in modules.items():
print(x)
# for x in modules.items():
# print(x)
def _resolve(txt) -> str:
oldTxt = txt
if txt in modules:
txt = f"~{modules[txt]}.{txt}"
print(f"-----------> {oldTxt=} -> {txt=}")
# print(f"-----------> {oldTxt=} -> {txt=}")
else:
txts = txt.split('.')
if txts[0] in modules:
txts[0] = f"~{modules[txts[0]]}.{txts[0]}"
txt = '.'.join(txts)
print(f"-----------> {oldTxt=} -> {txt=}")
# print(f"-----------> {oldTxt=} -> {txt=}")
return txt
_process_link_bk = sphinxPythonDomain.PyXRefRole.process_link

56
docs/source/sphinx_modules/sphinx_ext_autosummary_reworked.py

@ -115,6 +115,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
ttkAllSlots={}
ttkAllMembers={} # List all the member of a class
ttkInherited={} # List of the inherited classes for each class
ttkClasses={}
def _getMethodsAndSlots(_obj):
retSlots = []
@ -198,6 +199,8 @@ def setup(app: Sphinx) -> ExtensionMetadata:
modStyles[_name] = _get_classStyleCode(_obj)
if _name not in modules:
modules[_name] = _mod.__name__
if _name not in ttkClasses:
ttkClasses[_name] = _obj
modules[_name] = _mod.__name__ if len(_mod.__name__)>len(modules[_name]) else modules[_name]
print(f" * {_name=} = {_obj}")
@ -236,6 +239,55 @@ def setup(app: Sphinx) -> ExtensionMetadata:
for (x,y) in ttkInherited.items():
print(f"Inherited {x} -> {y}")
###############################################
# Rework inherited init params in the __doc__ #
###############################################
# ttk.TTkTemplates.dragevents.TDragEvents.__init__.__doc__=''
# ttk.TTkTemplates.mouseevents.TMouseEvents.__init__.__doc__=''
# ttk.TTkTemplates.keyevents.TKeyEvents.__init__.__doc__=''
for _name in ttkClasses:
def _mergeDoc(_da,_db,_title=''):
if not _da and not _db:
return ''
if not _da:
return _db
if not _db:
return _da
_dal = _da.split('\n')
_dbl = _db.split('\n')
_mina = min(len(_l) - len(_l.lstrip()) for _l in _dal if _l and _l.lstrip())
_minb = min(len(_l) - len(_l.lstrip()) for _l in _dbl if _l and _l.lstrip())
if _title:
_dbl = [' '*_minb+_l for _l in _title.split('\n')] + _dbl
if _mina<_minb:
_diff = _minb-_mina
_dc = '\n'.join([(' '*_diff)+_l for _l in _dal]+_dbl)
else:
_diff = _mina-_minb
_dc = '\n'.join(_dal + [(' '*_diff)+_l for _l in _dbl])
# for _l in _dal+_dbl:
# print(f"--{_l}--")
# print(f"{_mina=} {_minb=}")
# print(_dc)
return _dc
_obj = ttkClasses[_name]
if _obj.__doc__ and ":param" in _obj.__doc__:
print(ttk.TTkString(f"[{_name}] Params in the class docstring", ttk.TTkColor.BG_RED + ttk.TTkColor.FG_YELLOW).toAnsi())
if hasattr(_obj,'__init__'):
_obj.__doc__ = _mergeDoc(_obj.__doc__, _obj.__init__.__doc__)
if hasattr(_obj,'_forwardWidget') and hasattr(_obj._forwardWidget,'__init__'):
_obj.__doc__ = _mergeDoc(_obj.__doc__,
_obj._forwardWidget.__init__.__doc__,
f"\n:py:class:`{_obj._forwardWidget.__name__}`'s forwarded init params:\n")
for _iname in ttkInherited[_name]:
if _iname not in ttkClasses:
continue
_iobj = ttkClasses[_iname]
if hasattr(_iobj,'__init__'):
_obj.__doc__ = _mergeDoc(_obj.__doc__, _iobj.__init__.__doc__, f"\n:py:class:`{_iname}`'s inherited init params:\n")
# print(modStyles)
# raise Exception
@ -287,7 +339,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
_baseClass = _allForwarded['baseClass']
_allSlots = set(ttkAllSlots.get(_baseClass,[]))
_allMethods = set(ttkAllMethods.get(_baseClass,[]))
print(f"{_name=} {_baseClass=}\n{_allForwarded=}\n{_allSlots=}\n{_allMethods=}")
# print(f"{_name=} {_baseClass=}\n{_allForwarded=}\n{_allSlots=}\n{_allMethods=}")
_fwSlots = {
'baseClass': _baseClass,
'methods': sorted([_m for _m in _allForwarded['methods'] if _m in _allSlots])}
@ -338,7 +390,7 @@ def setup(app: Sphinx) -> ExtensionMetadata:
'TTkMethodsInherited': ttkInheritedMethods ,
}
print('\n'.join([f" * {x}={context[x]}" for x in context]))
# print('\n'.join([f" * {x}={context[x]}" for x in context]))
return generate_autosummary_content_old(
name, obj, parent, template, template_name, imported_members,

3
docs/source/static/theme_overrides.css

@ -20,7 +20,8 @@ html[data-theme="dark"] .bd-content img:not(.only-dark, .dark-light) {
}
@media (min-width: 992px) {
.bd-sidebar-primary {
flex-basis: 18%;
flex-basis: 17%;
padding: 0.5rem;
}
}

2
tutorial/000-examples.rst

@ -40,6 +40,8 @@ Events
`TTkLineEdit/SetGet.01.py <https://github.com/ceccopierangiolieugenio/pyTermTk/blob/main/tutorial/examples/TTkLineEdit/Events.01.py>`_ -
(`tryItOnline <https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html?filePath=tutorial/examples/TTkLineEdit/Events.01.py>`__):
.. _Examples-Terminal:
TTkTerminal
===========

Loading…
Cancel
Save