Browse Source

chore: improve typing (#562)

pull/515/head^2
Pier CeccoPierangioliEugenio 3 months ago committed by GitHub
parent
commit
b066ee0ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 99
      libs/pyTermTk/TermTk/TTkAbstract/abstractscrollarea.py
  2. 291
      libs/pyTermTk/TermTk/TTkAbstract/abstractscrollview.py
  3. 13
      libs/pyTermTk/TermTk/TTkAbstract/abstracttablemodel.py
  4. 4
      libs/pyTermTk/TermTk/TTkCore/constant.py
  5. 6
      libs/pyTermTk/TermTk/TTkTestWidgets/tominspector.py
  6. 2
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tree.py
  7. 26
      libs/pyTermTk/TermTk/TTkWidgets/TTkTerminal/debugterminal.py
  8. 2
      tests/pytest/modelView/test_tablemodelcsv.py
  9. 2
      tests/pytest/modelView/test_tablemodellist.py

99
libs/pyTermTk/TermTk/TTkAbstract/abstractscrollarea.py

@ -23,7 +23,7 @@
__all__ = ['TTkAbstractScrollArea'] __all__ = ['TTkAbstractScrollArea']
from dataclasses import dataclass from dataclasses import dataclass
from typing import List,Any,Type from typing import List,Any,Type,Optional
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
# from TermTk.TTkCore.log import TTkLog # from TermTk.TTkCore.log import TTkLog
@ -31,6 +31,7 @@ from TermTk.TTkCore.signal import pyTTkSlot
from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.container import TTkContainer from TermTk.TTkWidgets.container import TTkContainer
from TermTk.TTkWidgets.scrollbar import TTkScrollBar from TermTk.TTkWidgets.scrollbar import TTkScrollBar
from TermTk.TTkLayouts.layout import TTkLayout
from TermTk.TTkLayouts.gridlayout import TTkGridLayout from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollViewInterface from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollViewInterface
@ -42,12 +43,25 @@ class _ForwardData():
methods: List[str] methods: List[str]
class TTkAbstractScrollArea(TTkContainer): class TTkAbstractScrollArea(TTkContainer):
'''
The :py:class:`TTkAbstractScrollArea` provides a scrolling area with on-demand scroll bars.
This abstract class manages vertical and horizontal scroll bars that appear automatically
based on the viewport size and content.
'''
__slots__ = ( __slots__ = (
'_processing', # this flag is required to avoid unnecessary loop on edge cases '_processing', # this flag is required to avoid unnecessary loop on edge cases
'_viewport', '_viewport',
'_verticalScrollBar', '_verticalScrollBarPolicy', '_verticalScrollBar', '_verticalScrollBarPolicy',
'_horizontalScrollBar', '_horizontalScrollBarPolicy',) '_horizontalScrollBar', '_horizontalScrollBarPolicy',)
_processing:bool
_viewport:Optional[TTkAbstractScrollViewInterface]
_verticalScrollBar:TTkScrollBar
_horizontalScrollBar:TTkScrollBar
_verticalScrollBarPolicy:TTkK.ScrollBarPolicy
_horizontalScrollBarPolicy:TTkK.ScrollBarPolicy
def __init__(self, *, def __init__(self, *,
verticalScrollBarPolicy:TTkK.ScrollBarPolicy=TTkK.ScrollBarPolicy.ScrollBarAsNeeded, verticalScrollBarPolicy:TTkK.ScrollBarPolicy=TTkK.ScrollBarPolicy.ScrollBarAsNeeded,
horizontalScrollBarPolicy:TTkK.ScrollBarPolicy=TTkK.ScrollBarPolicy.ScrollBarAsNeeded, horizontalScrollBarPolicy:TTkK.ScrollBarPolicy=TTkK.ScrollBarPolicy.ScrollBarAsNeeded,
@ -63,8 +77,10 @@ class TTkAbstractScrollArea(TTkContainer):
self.layout().addWidget(self._verticalScrollBar) self.layout().addWidget(self._verticalScrollBar)
self.layout().addWidget(self._horizontalScrollBar) self.layout().addWidget(self._horizontalScrollBar)
def _resizeEvent(self): def _resizeEvent(self) -> None:
if self._processing: return ''' Internal method to handle resize events and reposition scroll bars '''
if self._processing:
return
self._processing = True self._processing = True
w,h = self.size() w,h = self.size()
vert = 1 if self._verticalScrollBar.isVisible() else 0 vert = 1 if self._verticalScrollBar.isVisible() else 0
@ -73,16 +89,24 @@ class TTkAbstractScrollArea(TTkContainer):
self._verticalScrollBar.setGeometry(w-1,0,1,h-hori) self._verticalScrollBar.setGeometry(w-1,0,1,h-hori)
if hori: if hori:
self._horizontalScrollBar.setGeometry(0,h-1,w-vert,1) self._horizontalScrollBar.setGeometry(0,h-1,w-vert,1)
if self._viewport: if isinstance(self._viewport, (TTkWidget, TTkLayout)):
self._viewport.setGeometry(0,0,w-vert,h-hori) self._viewport.setGeometry(0,0,w-vert,h-hori)
self._processing = False self._processing = False
def resizeEvent(self, w: int, h: int): def resizeEvent(self, w: int, h: int) -> None:
''' Handle resize events
:param w: the new width
:type w: int
:param h: the new height
:type h: int
'''
self._resizeEvent() self._resizeEvent()
@pyTTkSlot() @pyTTkSlot()
def _viewportChanged(self): def _viewportChanged(self) -> None:
if not self.isVisible(): return ''' Internal slot to handle viewport changes and update scroll bar ranges and visibility '''
if not self._viewport or not self.isVisible(): return
w,h = self.size() w,h = self.size()
fw, fh = self._viewport.viewFullAreaSize() fw, fh = self._viewport.viewFullAreaSize()
dw, dh = self._viewport.viewDisplayedSize() dw, dh = self._viewport.viewDisplayedSize()
@ -125,16 +149,37 @@ class TTkAbstractScrollArea(TTkContainer):
self._resizeEvent() self._resizeEvent()
@pyTTkSlot(int) @pyTTkSlot(int)
def _vscrollMoved(self, val): def _vscrollMoved(self, val: int) -> None:
''' Internal slot to handle vertical scroll bar movement
:param val: the new vertical scroll position
:type val: int
'''
if not self._viewport:
return
ox, _ = self._viewport.getViewOffsets() ox, _ = self._viewport.getViewOffsets()
self._viewport.viewMoveTo(ox, val) self._viewport.viewMoveTo(ox, val)
@pyTTkSlot(int) @pyTTkSlot(int)
def _hscrollMoved(self, val): def _hscrollMoved(self, val: int) -> None:
''' Internal slot to handle horizontal scroll bar movement
:param val: the new horizontal scroll position
:type val: int
'''
if not self._viewport:
return
_, oy = self._viewport.getViewOffsets() _, oy = self._viewport.getViewOffsets()
self._viewport.viewMoveTo(val, oy) self._viewport.viewMoveTo(val, oy)
def setViewport(self, viewport): def setViewport(self, viewport: TTkAbstractScrollViewInterface) -> None:
''' Set the viewport widget
:param viewport: the viewport widget implementing TTkAbstractScrollViewInterface
:type viewport: :py:class:`TTkAbstractScrollViewInterface`
:raises TypeError: if viewport does not implement TTkAbstractScrollViewInterface
'''
if not isinstance(viewport, TTkAbstractScrollViewInterface): if not isinstance(viewport, TTkAbstractScrollViewInterface):
raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollArea.setVewport(viewport)") raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollArea.setVewport(viewport)")
if self._viewport: if self._viewport:
@ -157,20 +202,44 @@ class TTkAbstractScrollArea(TTkContainer):
self.layout().addItem(viewport) self.layout().addItem(viewport)
self._resizeEvent() self._resizeEvent()
def setVerticalScrollBarPolicy(self, policy): def setVerticalScrollBarPolicy(self, policy: TTkK.ScrollBarPolicy) -> None:
''' Set the vertical scroll bar policy
:param policy: the scroll bar policy (ScrollBarAsNeeded, ScrollBarAlwaysOn, ScrollBarAlwaysOff)
:type policy: :py:class:`TTkK.ScrollBarPolicy`
'''
if policy != self._verticalScrollBarPolicy: if policy != self._verticalScrollBarPolicy:
self._verticalScrollBarPolicy = policy self._verticalScrollBarPolicy = policy
self._viewportChanged() self._viewportChanged()
def setHorizontalScrollBarPolicy(self, policy): def setHorizontalScrollBarPolicy(self, policy: TTkK.ScrollBarPolicy) -> None:
''' Set the horizontal scroll bar policy
:param policy: the scroll bar policy (ScrollBarAsNeeded, ScrollBarAlwaysOn, ScrollBarAlwaysOff)
:type policy: :py:class:`TTkK.ScrollBarPolicy`
'''
if policy != self._horizontalScrollBarPolicy: if policy != self._horizontalScrollBarPolicy:
self._horizontalScrollBarPolicy = policy self._horizontalScrollBarPolicy = policy
self._viewportChanged() self._viewportChanged()
def viewport(self) -> TTkAbstractScrollViewInterface: def viewport(self) -> Optional[TTkAbstractScrollViewInterface]:
''' Return the current viewport
:return: the viewport widget or None
:rtype: :py:class:`TTkAbstractScrollViewInterface`, optional
'''
return self._viewport return self._viewport
def update(self, repaint=True, updateLayout=False, updateParent=False): def update(self, repaint: bool = True, updateLayout: bool = False, updateParent: bool = False) -> None:
if self._viewport: ''' Update the scroll area and viewport
:param repaint: trigger a repaint, defaults to True
:type repaint: bool, optional
:param updateLayout: trigger a layout update, defaults to False
:type updateLayout: bool, optional
:param updateParent: trigger a parent update, defaults to False
:type updateParent: bool, optional
'''
if isinstance(self._viewport, (TTkWidget, TTkLayout)):
self._viewport.update(repaint, updateLayout, updateParent=False) self._viewport.update(repaint, updateLayout, updateParent=False)
return super().update(repaint, updateLayout, updateParent) return super().update(repaint, updateLayout, updateParent)

291
libs/pyTermTk/TermTk/TTkAbstract/abstractscrollview.py

@ -20,7 +20,9 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
__all__ = ['TTkAbstractScrollViewInterface', 'TTkAbstractScrollView', 'TTkAbstractScrollViewGridLayout'] __all__ = ['TTkAbstractScrollViewInterface', 'TTkAbstractScrollView', 'TTkAbstractScrollViewLayout', 'TTkAbstractScrollViewGridLayout']
from typing import Tuple
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.cfg import TTkCfg
@ -46,10 +48,37 @@ class TTkAbstractScrollViewInterface():
* :py:class:`TTkAbstractScrollViewGridLayout` * :py:class:`TTkAbstractScrollViewGridLayout`
''' '''
def __init__(self) -> None: pass viewMovedTo:pyTTkSignal
'''
This signal is emitted when the view content move to a new position (x,y),
:param x: the new horizontal offset
:type x: int
:param y: the new vertical offset
:type y: int
'''
viewSizeChanged:pyTTkSignal
'''
This signal is emitted when the view content size changed
:param width: the new width
:type width: int
:param height: the new height
:type height: int
'''
viewChanged:pyTTkSignal
'''
This signal is emitted whenever there is a change in the view content topology (size,pos)
.. note:: This signal must be implemented in any implementation
of :py:class:`TTkAbstractScrollView` to notify that the view content boudaries changed
'''
def __init__(self) -> None:
pass
# Override this function # Override this function
def viewFullAreaSize(self) -> tuple[int,int]: def viewFullAreaSize(self) -> Tuple[int,int]:
''' '''
This method returns the full widget area size of the :py:class:`TTkAbstractScrollViewInterface` implementation. This method returns the full widget area size of the :py:class:`TTkAbstractScrollViewInterface` implementation.
@ -63,7 +92,7 @@ class TTkAbstractScrollViewInterface():
raise NotImplementedError() raise NotImplementedError()
# Override this function # Override this function
def viewDisplayedSize(self) -> tuple[int,int]: def viewDisplayedSize(self) -> Tuple[int,int]:
''' '''
This method returns the displayed size of the :py:class:`TTkAbstractScrollViewInterface` implementation. This method returns the displayed size of the :py:class:`TTkAbstractScrollViewInterface` implementation.
@ -85,7 +114,7 @@ class TTkAbstractScrollViewInterface():
raise NotImplementedError() raise NotImplementedError()
@pyTTkSlot(int, int) @pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int): def viewMoveTo(self, x: int, y: int) -> None:
''' '''
This method is used to set the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface` This method is used to set the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface`
@ -105,7 +134,7 @@ class TTkAbstractScrollViewInterface():
''' '''
raise NotImplementedError() raise NotImplementedError()
def getViewOffsets(self) -> tuple: def getViewOffsets(self) -> Tuple[int,int]:
''' '''
Retrieve the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface` Retrieve the vertical and horizontal offsets of the :py:class:`TTkAbstractScrollViewInterface`
@ -121,7 +150,7 @@ class TTkAbstractScrollViewInterface():
:return: the (x,y) offset :return: the (x,y) offset
:rtype: tuple[int,int] :rtype: tuple[int,int]
''' '''
return self._viewOffsetX, self._viewOffsetY raise NotImplementedError()
class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface): class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
''' '''
@ -147,7 +176,7 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
:param width: the new width :param width: the new width
:type width: int :type width: int
:param height: the new heighht :param height: the new height
:type height: int :type height: int
''' '''
viewChanged:pyTTkSignal viewChanged:pyTTkSignal
@ -173,16 +202,33 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
# Do NOT use super() # Do NOT use super()
TTkContainer.__init__(self, **kwargs) TTkContainer.__init__(self, **kwargs)
def viewDisplayedSize(self) -> tuple[int,int]: def viewDisplayedSize(self) -> Tuple[int,int]:
''' Return the displayed size of this view
:return: the (width, height) of the view
:rtype: tuple[int,int]
'''
return self.size() return self.size()
def viewFullAreaSize(self) -> tuple[int,int]: def viewFullAreaSize(self) -> Tuple[int,int]:
''' Return the full area size including padding
:return: the (width, height) of the full area
:rtype: tuple[int,int]
'''
t,b,l,r = self.getPadding() t,b,l,r = self.getPadding()
_,_,w,h = self.layout().fullWidgetAreaGeometry() _,_,w,h = self.layout().fullWidgetAreaGeometry()
return w+l+r, h+t+b return w+l+r, h+t+b
@pyTTkSlot(int, int) @pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int): def viewMoveTo(self, x: int, y: int) -> None:
''' Move the view to the specified offset position
:param x: the horizontal offset
:type x: int
:param y: the vertical offset
:type y: int
'''
fw, fh = self.viewFullAreaSize() fw, fh = self.viewFullAreaSize()
dw, dh = self.viewDisplayedSize() dw, dh = self.viewDisplayedSize()
rangex = fw - dw rangex = fw - dw
@ -191,8 +237,8 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
x = max(0,min(rangex,x)) x = max(0,min(rangex,x))
y = max(0,min(rangey,y)) y = max(0,min(rangey,y))
# TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}") # TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}")
if self._viewOffsetX == x and \ if ( self._viewOffsetX == x and
self._viewOffsetY == y: # Nothong to do self._viewOffsetY == y ): # Nothing to do
return return
self._viewOffsetX = x self._viewOffsetX = x
self._viewOffsetY = y self._viewOffsetY = y
@ -200,10 +246,15 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
self.viewChanged.emit() self.viewChanged.emit()
self.update() self.update()
def getViewOffsets(self) -> tuple: def wheelEvent(self, evt: TTkMouseEvent) -> bool:
return self._viewOffsetX, self._viewOffsetY ''' Handle mouse wheel events for scrolling
:param evt: the mouse event
:type evt: :py:class:`TTkMouseEvent`
def wheelEvent(self, evt:TTkMouseEvent) -> bool: :return: True if the event was handled
:rtype: bool
'''
delta = TTkCfg.scrollDelta delta = TTkCfg.scrollDelta
offx, offy = self.getViewOffsets() offx, offy = self.getViewOffsets()
if evt.evt == TTkK.WHEEL_Up: if evt.evt == TTkK.WHEEL_Up:
@ -216,20 +267,65 @@ class TTkAbstractScrollView(TTkContainer, TTkAbstractScrollViewInterface):
self.viewMoveTo(offx + delta, offy) self.viewMoveTo(offx + delta, offy)
return True return True
def resizeEvent(self, w, h): def resizeEvent(self, w: int, h: int) -> None:
''' Handle resize events
:param w: the new width
:type w: int
:param h: the new height
:type h: int
'''
self.viewMoveTo(self._viewOffsetX, self._viewOffsetY) self.viewMoveTo(self._viewOffsetX, self._viewOffsetY)
self.viewSizeChanged.emit(w,h) self.viewSizeChanged.emit(w,h)
self.viewChanged.emit() self.viewChanged.emit()
def update(self, repaint=True, updateLayout=False, updateParent=False): def update(self, repaint: bool = True, updateLayout: bool = False, updateParent: bool = False) -> None:
''' Update the widget and emit viewChanged signal if layout is updated
:param repaint: trigger a repaint, defaults to True
:type repaint: bool, optional
:param updateLayout: trigger a layout update, defaults to False
:type updateLayout: bool, optional
:param updateParent: trigger a parent update, defaults to False
:type updateParent: bool, optional
'''
if updateLayout: if updateLayout:
self.viewChanged.emit() self.viewChanged.emit()
return super().update(repaint, updateLayout, updateParent) return super().update(repaint, updateLayout, updateParent)
def setPadding(self, top, bottom, left, right) -> None: def setPadding(self, top: int, bottom: int, left: int, right: int) -> None:
''' Set the padding and emit viewChanged signal
:param top: the top padding
:type top: int
:param bottom: the bottom padding
:type bottom: int
:param left: the left padding
:type left: int
:param right: the right padding
:type right: int
'''
super().setPadding(top, bottom, left, right) super().setPadding(top, bottom, left, right)
self.viewChanged.emit() self.viewChanged.emit()
def getViewOffsets(self) -> Tuple[int,int]:
'''
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 TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface): class TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface):
''' '''
:py:class:`TTkAbstractScrollViewLayout` :py:class:`TTkAbstractScrollViewLayout`
@ -250,7 +346,7 @@ class TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface):
:param width: the new width :param width: the new width
:type width: int :type width: int
:param height: the new heighht :param height: the new height
:type height: int :type height: int
''' '''
viewChanged:pyTTkSignal viewChanged:pyTTkSignal
@ -276,25 +372,68 @@ class TTkAbstractScrollViewLayout(TTkLayout, TTkAbstractScrollViewInterface):
self._excludeEvent = False self._excludeEvent = False
TTkLayout.__init__(self, *args, **kwargs) TTkLayout.__init__(self, *args, **kwargs)
def viewFullAreaSize(self) -> tuple[int,int]: def viewFullAreaSize(self) -> Tuple[int,int]:
''' Return the full area size of the layout
:return: the (width, height) of the full area
:rtype: tuple[int,int]
'''
_,_,w,h = self.fullWidgetAreaGeometry() _,_,w,h = self.fullWidgetAreaGeometry()
return w,h return w,h
def viewDisplayedSize(self) -> tuple[int,int]: def viewDisplayedSize(self) -> Tuple[int,int]:
''' Return the displayed size of the layout
:return: the (width, height) of the displayed area
:rtype: tuple[int,int]
'''
_,_,w,h = self.geometry() _,_,w,h = self.geometry()
return w,h return w,h
@pyTTkSlot(int, int) @pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int): def viewMoveTo(self, x: int, y: int) -> None:
''' Move the view to the specified offset position
:param x: the horizontal offset
:type x: int
:param y: the vertical offset
:type y: int
'''
self.setOffset(-x,-y) self.setOffset(-x,-y)
def getViewOffsets(self) -> tuple: def setGeometry(self, x: int, y: int, w: int, h: int) -> None:
return self._viewOffsetX, self._viewOffsetY ''' Set the geometry and emit viewChanged signal
def setGeometry(self, x, y, w, h): :param x: the horizontal position
:type x: int
:param y: the vertical position
:type y: int
:param w: the width
:type w: int
:param h: the height
:type h: int
'''
TTkLayout.setGeometry(self, x, y, w, h) TTkLayout.setGeometry(self, x, y, w, h)
self.viewChanged.emit() self.viewChanged.emit()
def getViewOffsets(self) -> Tuple[int,int]:
'''
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 TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterface): class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterface):
''' '''
:py:class:`TTkAbstractScrollViewGridLayout` :py:class:`TTkAbstractScrollViewGridLayout`
@ -315,7 +454,7 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
:param width: the new width :param width: the new width
:type width: int :type width: int
:param height: the new heighht :param height: the new height
:type height: int :type height: int
''' '''
viewChanged:pyTTkSignal viewChanged:pyTTkSignal
@ -343,7 +482,14 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
TTkGridLayout.__init__(self, *args, **kwargs) TTkGridLayout.__init__(self, *args, **kwargs)
@pyTTkSlot(int, int) @pyTTkSlot(int, int)
def viewMoveTo(self, x: int, y: int): def viewMoveTo(self, x: int, y: int) -> None:
''' Move the view to the specified offset position and propagate to child widgets
:param x: the horizontal offset
:type x: int
:param y: the vertical offset
:type y: int
'''
fw, fh = self.viewFullAreaSize() fw, fh = self.viewFullAreaSize()
dw, dh = self.viewDisplayedSize() dw, dh = self.viewDisplayedSize()
rangex = fw - dw rangex = fw - dw
@ -353,7 +499,7 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
y = max(0,min(rangey,y)) y = max(0,min(rangey,y))
# TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}") # TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}")
if self._viewOffsetX == x and \ if self._viewOffsetX == x and \
self._viewOffsetY == y: # Nothong to do self._viewOffsetY == y: # Nothing to do
return return
self._excludeEvent = True self._excludeEvent = True
for widget in self.iterWidgets(): for widget in self.iterWidgets():
@ -365,24 +511,55 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
self.viewChanged.emit() self.viewChanged.emit()
self.update() self.update()
def getViewOffsets(self) -> tuple: def setGeometry(self, x: int, y: int, w: int, h: int) -> None:
return self._viewOffsetX, self._viewOffsetY ''' Set the geometry and emit viewChanged signal
def setGeometry(self, x, y, w, h): :param x: the horizontal position
:type x: int
:param y: the vertical position
:type y: int
:param w: the width
:type w: int
:param h: the height
:type h: int
'''
TTkGridLayout.setGeometry(self, x, y, w, h) TTkGridLayout.setGeometry(self, x, y, w, h)
self.viewChanged.emit() self.viewChanged.emit()
@pyTTkSlot() @pyTTkSlot()
def _viewChanged(self): def _viewChanged(self) -> None:
''' Internal slot to handle viewChanged signal from child widgets '''
if self._excludeEvent: return if self._excludeEvent: return
self.viewChanged.emit() self.viewChanged.emit()
@pyTTkSlot(int,int) @pyTTkSlot(int,int)
def _viewMovedTo(self, x, y): def _viewMovedTo(self, x: int, y: int) -> None:
''' Internal slot to handle viewMovedTo signal from child widgets
:param x: the horizontal offset
:type x: int
:param y: the vertical offset
:type y: int
'''
if self._excludeEvent: return if self._excludeEvent: return
self.viewMoveTo(x, y) self.viewMoveTo(x, y)
def addWidget(self, widget, row=None, col=None, rowspan=1, colspan=1): def addWidget(self, widget, row=None, col=None, rowspan=1, colspan=1):
''' Add a widget that implements :py:class:`TTkAbstractScrollViewInterface` to the grid layout
:param widget: the widget to be added (must implement TTkAbstractScrollViewInterface)
:type widget: :py:class:`TTkWidget`
:param row: the row position, optional, defaults to None
:type row: int, optional
:param col: the column position, optional, defaults to None
:type col: int, optional
:param rowspan: number of rows to span, defaults to 1
:type rowspan: int, optional
:param colspan: number of columns to span, defaults to 1
:type colspan: int, optional
:raises TypeError: if widget does not implement TTkAbstractScrollViewInterface
'''
if not issubclass(type(widget),TTkAbstractScrollViewInterface): if not issubclass(type(widget),TTkAbstractScrollViewInterface):
raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollViewGridLayout.addWidget(...)") raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollViewGridLayout.addWidget(...)")
widget.viewChanged.connect(self._viewChanged) widget.viewChanged.connect(self._viewChanged)
@ -390,12 +567,32 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
return TTkGridLayout.addWidget(self, widget, row, col, rowspan, colspan) return TTkGridLayout.addWidget(self, widget, row, col, rowspan, colspan)
def addItem(self, item, row=None, col=None, rowspan=1, colspan=1): def addItem(self, item, row=None, col=None, rowspan=1, colspan=1):
''' Add a layout item that implements :py:class:`TTkAbstractScrollViewInterface` to the grid layout
:param item: the layout item to be added (must implement TTkAbstractScrollViewInterface)
:type item: :py:class:`TTkLayoutItem`
:param row: the row position, optional, defaults to None
:type row: int, optional
:param col: the column position, optional, defaults to None
:type col: int, optional
:param rowspan: number of rows to span, defaults to 1
:type rowspan: int, optional
:param colspan: number of columns to span, defaults to 1
:type colspan: int, optional
:raises TypeError: if item does not implement TTkAbstractScrollViewInterface
'''
if not issubclass(type(item),TTkAbstractScrollViewInterface): if not issubclass(type(item),TTkAbstractScrollViewInterface):
raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollViewGridLayout.addItem(...)") raise TypeError("TTkAbstractScrollViewInterface is required in TTkAbstractScrollViewGridLayout.addItem(...)")
return TTkGridLayout.addItem(self, item, row, col, rowspan, colspan) return TTkGridLayout.addItem(self, item, row, col, rowspan, colspan)
# Override this function # Override this function
def viewFullAreaSize(self) -> tuple[int,int]: def viewFullAreaSize(self) -> Tuple[int,int]:
''' Return the full area size by computing the maximum size from all child widgets
:return: the (width, height) of the full area
:rtype: tuple[int,int]
'''
w,h=0,0 w,h=0,0
for widget in self.iterWidgets(): for widget in self.iterWidgets():
ww,wh = widget.viewFullAreaSize() ww,wh = widget.viewFullAreaSize()
@ -404,7 +601,12 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
return w,h return w,h
# Override this function # Override this function
def viewDisplayedSize(self) -> tuple[int,int]: def viewDisplayedSize(self) -> Tuple[int,int]:
''' Return the displayed size by computing the maximum displayed size from all child widgets
:return: the (width, height) of the displayed area
:rtype: tuple[int,int]
'''
w,h=0,0 w,h=0,0
for widget in self.iterWidgets(): for widget in self.iterWidgets():
ww,wh = widget.viewDisplayedSize() ww,wh = widget.viewDisplayedSize()
@ -412,3 +614,20 @@ class TTkAbstractScrollViewGridLayout(TTkGridLayout, TTkAbstractScrollViewInterf
h = max(h,wh) h = max(h,wh)
return w,h return w,h
def getViewOffsets(self) -> Tuple[int,int]:
'''
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

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

@ -20,6 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
from __future__ import annotations
__all__ = ['TTkAbstractTableModel','TTkModelIndex'] __all__ = ['TTkAbstractTableModel','TTkModelIndex']
from typing import Tuple,Any from typing import Tuple,Any
@ -52,7 +54,7 @@ class TTkModelIndex():
:return: int :return: int
''' '''
pass raise NotImplementedError()
def col(self) -> int: def col(self) -> int:
''' '''
@ -60,7 +62,8 @@ class TTkModelIndex():
:return: int :return: int
''' '''
pass raise NotImplementedError()
def data(self) -> object: def data(self) -> object:
''' '''
@ -68,7 +71,7 @@ class TTkModelIndex():
:return: object :return: object
''' '''
pass raise NotImplementedError()
def setData(self, data:object) -> None: def setData(self, data:object) -> None:
''' '''
@ -77,11 +80,11 @@ class TTkModelIndex():
:param data: the data to be set in the (row,col) position of the table :param data: the data to be set in the (row,col) position of the table
:type data: object :type data: object
''' '''
pass raise NotImplementedError()
class _TTkModelIndexList(TTkModelIndex): class _TTkModelIndexList(TTkModelIndex):
__slots__ = ('_col','_row','_model') __slots__ = ('_col','_row','_model')
def __init__(self, row:int, col:list, model) -> None: def __init__(self, row:int, col:int, model:TTkAbstractTableModel) -> None:
self._col = col self._col = col
self._row = row self._row = row
self._model = model self._model = model

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

@ -169,7 +169,7 @@ class TTkConstant:
HORIZONTAL = Direction.HORIZONTAL HORIZONTAL = Direction.HORIZONTAL
VERTICAL = Direction.VERTICAL VERTICAL = Direction.VERTICAL
class ScrollBarPolicy(int): class ScrollBarPolicy(IntEnum):
ScrollBarAsNeeded = 0x00 ScrollBarAsNeeded = 0x00
ScrollBarAlwaysOff = 0x01 ScrollBarAlwaysOff = 0x01
ScrollBarAlwaysOn = 0x02 ScrollBarAlwaysOn = 0x02
@ -543,7 +543,7 @@ class TTkConstant:
ClearAndSelect = Clear | Select ClearAndSelect = Clear | Select
'''A combination of Clear and Select, provided for convenience.''' '''A combination of Clear and Select, provided for convenience.'''
class ItemFlag(int): class ItemFlag(Flag):
''':py:class:`ItemFlag` describes the properties of an item ''':py:class:`ItemFlag` describes the properties of an item
.. autosummary:: .. autosummary::

6
libs/pyTermTk/TermTk/TTkTestWidgets/tominspector.py

@ -22,6 +22,8 @@
__all__ = ['TTkTomInspector'] __all__ = ['TTkTomInspector']
from typing import List
from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.helper import TTkHelper from TermTk.TTkCore.helper import TTkHelper
@ -75,6 +77,7 @@ class _DetailGridView(TTkAbstractScrollView):
class _DetailLazyFormView(TTkAbstractScrollView): class _DetailLazyFormView(TTkAbstractScrollView):
__slots__ = ('_gridLayout', '_lazyRows', '_lastRow') __slots__ = ('_gridLayout', '_lazyRows', '_lastRow')
_lazyRows:List[List]
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.setPadding(1,0,0,0) self.setPadding(1,0,0,0)
@ -149,7 +152,8 @@ class TTkTomInspector(TTkContainer):
self._domTree = TTkTree() self._domTree = TTkTree()
self._domTree.setHeaderLabels(["Object", "Class", "Visibility", "Layout"]) self._domTree.setHeaderLabels(["Object", "Class", "Visibility", "Layout"])
self._domTree.addTopLevelItem(TTkTomInspector._getTomTreeItem(TTkHelper._rootWidget._widgetItem)) if TTkHelper._rootWidget:
self._domTree.addTopLevelItem(TTkTomInspector._getTomTreeItem(TTkHelper._rootWidget._widgetItem))
self._detail = TTkFrame() self._detail = TTkFrame()

2
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tree.py

@ -25,7 +25,7 @@ __all__ = ['TTkTree']
from typing import List,Optional from typing import List,Optional
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString from TermTk.TTkCore.string import TTkString, TTkStringType
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkWidgets.TTkModelView.treewidget import TTkTreeWidget from TermTk.TTkWidgets.TTkModelView.treewidget import TTkTreeWidget
from TermTk.TTkWidgets.TTkModelView.treewidgetitem import TTkTreeWidgetItem from TermTk.TTkWidgets.TTkModelView.treewidgetitem import TTkTreeWidgetItem

26
libs/pyTermTk/TermTk/TTkWidgets/TTkTerminal/debugterminal.py

@ -20,7 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
__all__ = ['DebugTTkTerminal'] # __all__ = ['DebugTTkTerminal']
__all__ = []
import os, pty, threading import os, pty, threading
import struct, fcntl, termios import struct, fcntl, termios
@ -48,6 +49,7 @@ from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView, TTkAbstractScrollViewGridLayout from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView, TTkAbstractScrollViewGridLayout
from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.TTkTerminal import TTkTerminal
from TermTk.TTkWidgets.TTkTerminal.mode import TTkTerminalModes from TermTk.TTkWidgets.TTkTerminal.mode import TTkTerminalModes
from TermTk.TTkWidgets.TTkTerminal.vt102 import TTkVT102 from TermTk.TTkWidgets.TTkTerminal.vt102 import TTkVT102
@ -55,14 +57,16 @@ from TermTk.TTkWidgets.TTkTerminal.vt102 import TTkVT102
from TermTk.TTkCore.TTkTerm.colors import TTkTermColor from TermTk.TTkCore.TTkTerm.colors import TTkTermColor
from TermTk.TTkCore.TTkTerm.colors_ansi_map import ansiMap16, ansiMap256 from TermTk.TTkCore.TTkTerm.colors_ansi_map import ansiMap16, ansiMap256
class DebugTTkTerminal(TTkWidget): # class DebugTTkTerminal(TTkWidget):
__slots__ = ('_terminal') # __slots__ = ('_terminal')
def __init__(self, terminal, **kwargs) -> None: # _terminal:TTkTerminal
super().__init__(**kwargs) # def __init__(self, terminal, **kwargs) -> None:
# self._terminal = terminal
# super().__init__(**kwargs)
def paintEvent(self, canvas: TTkCanvas): # def paintEvent(self, canvas: TTkCanvas):
t = self._terminal # t = self._terminal
canvas.drawText(pos=(0,0), text=f"{t=}") # canvas.drawText(pos=(0,0), text=f"{t=}")
canvas.drawText(pos=(0,1), text=f"{t._terminalCursor=}") # canvas.drawText(pos=(0,1), text=f"{t._terminalCursor=}")
canvas.drawText(pos=(0,2), text=f"{t._scrollingRegion=}") # canvas.drawText(pos=(0,2), text=f"{t._scrollingRegion=}")
canvas.drawText(pos=(0,3), text=f"{len(t._bufferedLines)=}") # canvas.drawText(pos=(0,3), text=f"{len(t._bufferedLines)=}")

2
tests/pytest/modelView/test_tablemodelcsv.py

@ -306,7 +306,7 @@ class TestTTkTableModelCSV:
model = ttk.TTkTableModelCSV(fd=csv_fd) model = ttk.TTkTableModelCSV(fd=csv_fd)
# Test inherited methods # Test inherited methods
assert isinstance(model.flags(0, 0), int) assert isinstance(model.flags(0, 0), ttk.TTkK.ItemFlag)
# Test setData (inherited) # Test setData (inherited)
original_value = model.data(0, 0) original_value = model.data(0, 0)

2
tests/pytest/modelView/test_tablemodellist.py

@ -514,7 +514,7 @@ def test_integration_with_table_widget():
assert isinstance(model.rowCount(), int) assert isinstance(model.rowCount(), int)
assert isinstance(model.columnCount(), int) assert isinstance(model.columnCount(), int)
assert isinstance(model.data(0, 0), (str, int, float, type(None))) assert isinstance(model.data(0, 0), (str, int, float, type(None)))
assert isinstance(model.flags(0, 0), int) # Flags are integer bitmasks assert isinstance(model.flags(0, 0), ttk.TTkK.ItemFlag) # Flags are integer bitmasks
if __name__ == '__main__': if __name__ == '__main__':

Loading…
Cancel
Save