Browse Source

Added abstract for scroll area and scroll view

pull/3/head
Eugenio Parodi 5 years ago
parent
commit
9e1a9e9e1d
  1. 1
      TermTk/TTkAbstract/__init__.py
  2. 92
      TermTk/TTkAbstract/abstractscrollarea.py
  3. 91
      TermTk/TTkAbstract/abstractscrollview.py
  4. 6
      TermTk/TTkCore/constant.py
  5. 29
      TermTk/TTkWidgets/__init__.py
  6. 9
      TermTk/TTkWidgets/scrollbar.py
  7. 272
      TermTk/TTkWidgets/table.py
  8. 260
      TermTk/TTkWidgets/tableview.py
  9. 52
      TermTk/TTkWidgets/texedit.py
  10. 22
      TermTk/TTkWidgets/widget.py
  11. 3
      TermTk/__init__.py
  12. 1
      docs/BUGS.md
  13. 5
      docs/TODO.md

1
TermTk/TTkAbstract/__init__.py

@ -0,0 +1 @@
from .abstractscrollarea import *

92
TermTk/TTkAbstract/abstractscrollarea.py

@ -0,0 +1,92 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from TermTk.TTkCore.constant import TTkConstant, TTkK
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.cfg import *
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.scrollbar import TTkScrollBar
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
class TTkAbstractScrollArea(TTkWidget):
__slots__ = (
'_viewport',
'_verticalScrollBar', '_verticalScrollBarPolicy',
'_horizontalScrollBar', '_horizontalScrollBarPolicy',)
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkAbstractScrollArea')
self.setLayout(TTkGridLayout())
self._verticalScrollBar = TTkScrollBar(orientation=TTkK.VERTICAL)
self._horizontalScrollBar = TTkScrollBar(orientation=TTkK.HORIZONTAL)
self._verticalScrollBarPolicy = TTkK.ScrollBarAsNeeded
self._horizontalScrollBarPolicy = TTkK.ScrollBarAsNeeded
@pyTTkSlot()
def _viewportChanged(self):
fw, fh = self._viewport.viewFullAreaSize()
dw, dh = self._viewport.viewDisplayedSize()
ox, oy = self._viewport.getViewOffsets()
hpage = dw
vpage = dh
hrange = fw - dw
vrange = fh - dh
self._verticalScrollBar.setPageStep(vpage)
self._verticalScrollBar.setRange(0, vrange)
self._verticalScrollBar.setValue(oy)
self._horizontalScrollBar.setPageStep(hpage)
self._horizontalScrollBar.setRange(0, hrange)
self._horizontalScrollBar.setValue(ox)
@pyTTkSlot(int)
def _vscrollMoved(self, val):
ox, _ = self._viewport.getViewOffsets()
self._viewport.viewMoveTo(ox, val)
@pyTTkSlot(int)
def _hscrollMoved(self, val):
_, oy = self._viewport.getViewOffsets()
self._viewport.viewMoveTo(val, oy)
def setViewport(self, viewport):
if not isinstance(viewport, TTkAbstractScrollView):
raise TypeError("TTkAbstractScrollView is required in TTkAbstractScrollArea.setVewport(viewport)")
self._viewport = viewport
self._viewport.viewChanged.connect(self._viewportChanged)
self._verticalScrollBar.sliderMoved.connect(self._vscrollMoved)
self._horizontalScrollBar.sliderMoved.connect(self._hscrollMoved)
self.layout().addWidget(viewport,0,0)
self.layout().addWidget(self._verticalScrollBar,0,1)
self.layout().addWidget(self._horizontalScrollBar,1,0)
def setVerticalScrollBarPolicy(self, policy):
self._verticalScrollBarPolicy = policy
def setHorizontalScrollBarPolicy(self, policy):
self._horizontalScrollBarPolicy = policy

91
TermTk/TTkAbstract/abstractscrollview.py

@ -0,0 +1,91 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from TermTk.TTkCore.constant import TTkConstant, TTkK
from TermTk.TTkCore.cfg import *
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets.widget import TTkWidget
class TTkAbstractScrollView(TTkWidget):
__slots__ = (
'_viewOffsetX', '_viewOffsetY',
# Signals
'viewMovedTo', 'viewSizeChanged', 'viewChanged')
def __init__(self, *args, **kwargs):
super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkAbstractScrollView')
# Signals
self.viewMovedTo = pyTTkSignal(int, int) # x, y
self.viewSizeChanged = pyTTkSignal(int, int) # w, h
self.viewChanged = pyTTkSignal()
self._viewOffsetX = 0
self._viewOffsetY = 0
# Override this function
def viewFullAreaSize(self) -> (int, int):
raise NotImplementedError()
# Override this function
def viewDisplayedSize(self) -> (int, int):
raise NotImplementedError()
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
fw, fh = self.viewFullAreaSize()
dw, dh = self.viewDisplayedSize()
rangex = fw - dw
rangey = fh - dh
# TTkLog.debug(f"x:{x},y:{y}, full:{fw,fh}, display:{dw,dh}, range:{rangex,rangey}")
if x>rangex: x = rangex
if y>rangey: y = rangey
if x<0 : x = 0
if y<0 : y = 0
# TTkLog.debug(f"x:{x},y:{y}, wo:{self._viewOffsetX,self._viewOffsetY}")
if self._viewOffsetX == x and \
self._viewOffsetY == y: # Nothong to do
return
self._viewOffsetX = x
self._viewOffsetY = y
self.viewMovedTo.emit(x,y)
self.viewChanged.emit()
self.update()
def wheelEvent(self, evt):
delta = TTkCfg.scrollDelta
offx, offy = self.getViewOffsets()
if evt.evt == TTkK.WHEEL_Up:
delta = -delta
self.viewMoveTo(offx, offy + delta)
return True
def resizeEvent(self, w, h):
self.viewSizeChanged.emit(w,h)
self.viewChanged.emit()
def getViewOffsets(self):
return self._viewOffsetX, self._viewOffsetY

6
TermTk/TTkCore/constant.py

@ -46,6 +46,12 @@ class TTkConstant:
HORIZONTAL = 0x01 HORIZONTAL = 0x01
VERTICAL = 0x02 VERTICAL = 0x02
# Scroll Bar Policy
ScrollBarAsNeeded = 0x00
ScrollBarAlwaysOff = 0x01
ScrollBarAlwaysOn = 0x02
# Keys # Keys
NoButton = 0x00000000 # The button state does not refer to any button (see QMouseEvent::button()). NoButton = 0x00000000 # The button state does not refer to any button (see QMouseEvent::button()).
AllButtons = 0x07ffffff # This value corresponds to a mask of all possible mouse buttons. Use to set the 'acceptedButtons' property of a MouseArea to accept ALL mouse buttons. AllButtons = 0x07ffffff # This value corresponds to a mask of all possible mouse buttons. Use to set the 'acceptedButtons' property of a MouseArea to accept ALL mouse buttons.

29
TermTk/TTkWidgets/__init__.py

@ -1,16 +1,17 @@
from .widget import * from .widget import *
from .spacer import * from .spacer import *
from .frame import * from .frame import *
from .resizableframe import * from .resizableframe import *
from .splitter import * from .splitter import *
from .label import * from .label import *
from .button import * from .button import *
from .checkbox import * from .checkbox import *
from .radiobutton import * from .radiobutton import *
from .combobox import * from .combobox import *
from .lineedit import * from .lineedit import *
from .texedit import * from .texedit import *
from .scrollbar import * from .scrollbar import *
from .window import * from .window import *
from .table import * from .table import *
from .logviewer import * from .tableview import *
from .logviewer import *

9
TermTk/TTkWidgets/scrollbar.py

@ -196,6 +196,9 @@ class TTkScrollBar(TTkWidget):
@pyTTkSlot(int, int) @pyTTkSlot(int, int)
def setRange(self, min, max): def setRange(self, min, max):
if self._minimum == min and \
self._maximum == max :
return
self.minimum = min self.minimum = min
self.maximum = max self.maximum = max
self.rangeChanged.emit(min, max) self.rangeChanged.emit(min, max)
@ -208,6 +211,8 @@ class TTkScrollBar(TTkWidget):
def minimum(self): return self._minimum def minimum(self): return self._minimum
@minimum.setter @minimum.setter
def minimum(self, v): def minimum(self, v):
if v == self._minimum:
return
if v > self._maximum: if v > self._maximum:
v = self._maximum v = self._maximum
self._minimum = v self._minimum = v
@ -217,6 +222,8 @@ class TTkScrollBar(TTkWidget):
def maximum(self): return self._maximum def maximum(self): return self._maximum
@minimum.setter @minimum.setter
def maximum(self, v): def maximum(self, v):
if v == self._maximum:
return
if v < self._minimum: if v < self._minimum:
v = self._minimum v = self._minimum
self._maximum = v self._maximum = v
@ -236,6 +243,8 @@ class TTkScrollBar(TTkWidget):
def value(self): return self._value def value(self): return self._value
@value.setter @value.setter
def value(self, v): def value(self, v):
if self._value == v:
return
if v > self._maximum: v = self._maximum if v > self._maximum: v = self._maximum
if v < self._minimum: v = self._minimum if v < self._minimum: v = self._minimum
if self._value == v: return if self._value == v: return

272
TermTk/TTkWidgets/table.py

@ -27,288 +27,34 @@ from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.color import TTkColor
from TermTk.TTkWidgets.tableview import TTkTableView
from TermTk.TTkLayouts.gridlayout import TTkGridLayout from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkWidgets.frame import TTkFrame
from TermTk.TTkWidgets.spacer import TTkSpacer
from TermTk.TTkWidgets.scrollbar import TTkScrollBar
class _TTkTableViewHeader(TTkWidget): class TTkTable(TTkAbstractScrollArea):
__slots__ = ( __slots__ = ('_tableView', 'activated')
'_header', '_showHeader',
'_alignments', '_headerColor',
'_columns')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs) TTkAbstractScrollArea.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTableViewHeader' )
self._columns = kwargs.get('columns' , [-1] )
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD )
self.setMaximumHeight(1)
self.setMinimumHeight(1)
def setAlignment(self, alignments):
if len(alignments) != len(self._columns):
return
self._alignments = alignments
def setHeader(self, header):
if len(header) != len(self._columns):
return
self._header = header
def setHeader(self, header):
if len(header) != len(self._columns):
return
self._header = header
def setColumnSize(self, columns):
self._columns = columns
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
def paintEvent(self):
w,h = self.size()
total = 0
variableCols = 0
# Retrieve the free size
for width in self._columns:
if width > 0:
total += width
else:
variableCols += 1
# Define the list of cols sizes
sizes = []
for width in self._columns:
if width > 0:
sizes.append(width)
else:
sizes.append((w-total)//variableCols)
variableCols -= 1
colors = [self._headerColor]*len(self._header)
self._canvas.drawTableLine(pos=(0,0), items=self._header, sizes=sizes, colors=colors, alignments=self._alignments)
class _TTkTableViewData(TTkWidget):
__slots__ = (
'_alignments',
'_columns', '_columnColors',
'_tableData',
'_selectColor', '_moveTo', '_selected')
def __init__(self, *args, **kwargs):
self._moveTo = 0
self._tableData = []
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTableViewData' )
self._columns = kwargs.get('columns' , [-1] )
self._alignments = [TTkK.NONE]*len(self._columns)
self._columnColors = kwargs.get('columnColors' , [TTkColor.RST]*len(self._columns) )
self._selectColor = kwargs.get('selectColor' , TTkColor.BOLD )
self._selected = -1
self.setFocusPolicy(TTkK.ClickFocus)
class _TTkTableView(TTkWidget):
__slots__ = (
'_header', '_showHeader',
'_alignments', '_headerColor',
'_columns', '_columnColors',
'_tableData',
'_selectColor', '_moveTo', '_selected',
# Signals
'activated', 'tableMoved', 'displayedMaxRowsChanged', 'tablePropertiesChanged')
def __init__(self, *args, **kwargs):
self._moveTo = 0
self._tableData = []
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTableView' )
# define signals
self.activated = pyTTkSignal(int) # Value
self.tableMoved = pyTTkSignal(int) # Value
self.displayedMaxRowsChanged = pyTTkSignal(int) # Value
self.tablePropertiesChanged = pyTTkSignal(int, int, int, int) # selected, numItems, displayed lines, offsetView
self._columns = kwargs.get('columns' , [-1] )
self._header = [""]*len(self._columns)
self._showHeader = False
self._alignments = [TTkK.NONE]*len(self._columns)
self._columnColors = kwargs.get('columnColors' , [TTkColor.RST]*len(self._columns) )
self._selectColor = kwargs.get('selectColor' , TTkColor.BOLD )
self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD )
self._selected = -1
self.setFocusPolicy(TTkK.ClickFocus)
def _initSignals(self):
# self.cellActivated(int row, int column)
# self.cellChanged(int row, int column)
# self.cellClicked(int row, int column)
# self.cellDoubleClicked(int row, int column)
# self.cellEntered(int row, int column)
# self.cellPressed(int row, int column)
# self.currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
# self.currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
# self.itemActivated(QTableWidgetItem *item)
# self.itemChanged(QTableWidgetItem *item)
# self.itemClicked(QTableWidgetItem *item)
# self.itemDoubleClicked(QTableWidgetItem *item)
# self.itemEntered(QTableWidgetItem *item)
# self.itemPressed(QTableWidgetItem *item)
# self.itemSelectionChanged()
pass
def items(self): return self._tableData
def setAlignment(self, alignments):
if len(alignments) != len(self._columns):
return
self._alignments = alignments
def setColumnSize(self, columns):
self._columns = columns
self._columnColors = [TTkColor.RST]*len(self._columns)
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
def setColumnColors(self, colors):
if len(colors) != len(self._columns):
return
self._columnColors = colors
def appendItem(self, item):
if len(item) != len(self._columns):
return
self._tableData.append(item)
self.tablePropertiesChanged.emit(self._selected, len(self._tableData), self.height(), self._moveTo)
self.update()
def mousePressEvent(self, evt):
x,y = evt.x, evt.y
if y >= 0:
selected = self._moveTo + y
if selected >= len(self._tableData):
selected = -1
self._selected = selected
self.update()
self.activated.emit(self._selected)
return True
def wheelEvent(self, evt):
delta = TTkCfg.scrollDelta
if evt.evt == TTkK.WHEEL_Up:
delta = -delta
self.scrollTo(self._moveTo + delta)
self.update()
return True
def resizeEvent(self, w, h):
if self._moveTo > len(self._tableData)-h-1:
self._moveTo = len(self._tableData)-h-1
if self._moveTo < 0:
self._moveTo = 0
self.displayedMaxRowsChanged.emit(h)
self.tablePropertiesChanged.emit(self._selected, len(self._tableData), self.height(), self._moveTo)
@pyTTkSlot(int)
def scrollTo(self, to):
# TTkLog.debug(f"to:{to},h{self._height},size:{len(self._tableData)}")
max = len(self._tableData) - self.height()
if to>max: to=max
if to<0: to=0
self._moveTo = to
self.tableMoved.emit(to)
self.tablePropertiesChanged.emit(self._selected, len(self._tableData), self.height(), self._moveTo)
self.update()
def paintEvent(self):
w,h = self.size()
total = 0
variableCols = 0
# Retrieve the free size
for width in self._columns:
if width > 0:
total += width
else:
variableCols += 1
# Define the list of cols sizes
sizes = []
for width in self._columns:
if width > 0:
sizes.append(width)
else:
sizes.append((w-total)//variableCols)
variableCols -= 1
maxItems = len(self._tableData)
itemFrom = self._moveTo
if itemFrom > maxItems-h: itemFrom = maxItems-h
if itemFrom < 0 : itemFrom = 0
itemTo = itemFrom + h
if itemTo > maxItems: itemTo = maxItems
# TTkLog.debug(f"moveto:{self._moveTo}, maxItems:{maxItems}, f:{itemFrom}, t{itemTo}, h:{h}, sel:{self._selected}")
y = 0
for it in range(itemFrom, itemTo):
item = self._tableData[it]
if self._selected > 0:
val = self._selected - itemFrom
else:
val = h//2
if val < 0 : val = 0
if val > h : val = h
if it == self._selected:
colors = [self._selectColor]*len(self._columnColors)
self._canvas.drawTableLine(pos=(0,y), items=item, sizes=sizes, colors=colors, alignments=self._alignments)
else:
colors = [c.modParam(val=-val) for c in self._columnColors]
self._canvas.drawTableLine(pos=(0,y), items=item, sizes=sizes, colors=colors, alignments=self._alignments)
y+=1
class TTkTable(TTkWidget):
__slots__ = ('_vscroller', '_hscroller', '_tableView', '_headerView', '_showHeader', 'activated')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTable' ) self._name = kwargs.get('name' , 'TTkTable' )
if 'parent' in kwargs: if 'parent' in kwargs: kwargs.pop('parent')
kwargs.pop('parent') self._tableView = TTkTableView(*args, **kwargs)
self._tableView = _TTkTableView(*args, **kwargs)
self._headerView = _TTkTableViewHeader(*args, **kwargs)
# Forward the signal # Forward the signal
self.activated = self._tableView.activated self.activated = self._tableView.activated
self._showHeader = kwargs.get('showHeader',True)
self._vscroller = TTkScrollBar(orientation=TTkK.VERTICAL)
self._hscroller = TTkScrollBar(orientation=TTkK.HORIZONTAL)
self.setLayout(TTkGridLayout())
self._vscroller.sliderMoved.connect(self._tableView.scrollTo)
self._tableView.tablePropertiesChanged.connect(self.handleTableProperties)
self.layout().addWidget(self._headerView,0,0)
self.layout().addWidget(self._tableView,1,0)
# self.layout().addWidget(TTkTestWidget(border=True),0,0)
self.layout().addWidget(self._vscroller,1,1)
# self.layout().addWidget(self._hscroller,1,0)
if not self._showHeader:
self._headerView.hide()
self.setFocusPolicy(TTkK.ClickFocus) self.setFocusPolicy(TTkK.ClickFocus)
self.setViewport(self._tableView)
def setAlignment(self, *args, **kwargs) : def setAlignment(self, *args, **kwargs) :
self._tableView.setAlignment(*args, **kwargs) self._tableView.setAlignment(*args, **kwargs)
self._headerView.setAlignment(*args, **kwargs)
def setHeader(self, *args, **kwargs) : def setHeader(self, *args, **kwargs) :
self._headerView.setHeader(*args, **kwargs) self._tableView.setHeader(*args, **kwargs)
def setColumnSize(self, *args, **kwargs) : def setColumnSize(self, *args, **kwargs) :
self._tableView.setColumnSize(*args, **kwargs) self._tableView.setColumnSize(*args, **kwargs)
self._headerView.setColumnSize(*args, **kwargs)
def setColumnColors(self, *args, **kwargs): def setColumnColors(self, *args, **kwargs):
self._tableView.setColumnColors(*args, **kwargs) self._tableView.setColumnColors(*args, **kwargs)
def appendItem(self, *args, **kwargs) : def appendItem(self, *args, **kwargs) :
self._vscroller.setRangeTo(len(self._tableView.items()))
self._tableView.appendItem(*args, **kwargs) self._tableView.appendItem(*args, **kwargs)
@pyTTkSlot(int, int, int, int)
def handleTableProperties(self, selected, items, height, offset):
self._vscroller.setRange(0, items-height)
self._vscroller.setValue(offset)

260
TermTk/TTkWidgets/tableview.py

@ -22,62 +22,121 @@
# 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 TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkCore.color import TTkColor from TermTk.TTkCore.color import TTkColor
from TermTk.TTkLayouts.boxlayout import TTkHBoxLayout
from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.spacer import TTkSpacer from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkWidgets.scrollbar import TTkScrollBar from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
''' class _TTkTableViewHeader(TTkWidget):
__slots__ = ('_header', '_alignments', '_headerColor', '_columns')
def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTableViewHeader' )
self._columns = kwargs.get('columns' , [-1] )
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD )
self.setMaximumHeight(1)
self.setMinimumHeight(1)
def setAlignment(self, alignments):
if len(alignments) != len(self._columns):
return
self._alignments = alignments
def setHeader(self, header):
if len(header) != len(self._columns):
return
self._header = header
def setColumnSize(self, columns):
self._columns = columns
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns)
def paintEvent(self):
w,h = self.size()
total = 0
variableCols = 0
# Retrieve the free size
for width in self._columns:
if width > 0:
total += width
else:
variableCols += 1
# Define the list of cols sizes
sizes = []
for width in self._columns:
if width > 0:
sizes.append(width)
else:
sizes.append((w-total)//variableCols)
variableCols -= 1
colors = [self._headerColor]*len(self._header)
self._canvas.drawTableLine(pos=(0,0), items=self._header, sizes=sizes, colors=colors, alignments=self._alignments)
''' class _TTkTableView(TTkAbstractScrollView):
class _______TTkTable(TTkWidget):
__slots__ = ( __slots__ = (
'_hlayout','_vscroller',
'_header', '_showHeader',
'_alignments', '_headerColor', '_alignments', '_headerColor',
'_columns', '_columnColors', '_columns', '_columnColors',
'_tableData', '_tableData',
'_selectColor', '_moveTo', '_selected') '_selectColor', '_selected',
# Signals
'activated')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._vscroller = None # This is required to avoid crash int he vScroller Tuning
self._moveTo = 0
self._tableData = [] self._tableData = []
TTkWidget.__init__(self, *args, **kwargs) TTkAbstractScrollView.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTable' ) self._name = kwargs.get('name' , '_TTkTableView' )
# define signals
self.activated = pyTTkSignal(int) # Value
self._columns = kwargs.get('columns' , [-1] ) self._columns = kwargs.get('columns' , [-1] )
self._header = [""]*len(self._columns)
self._showHeader = False
self._alignments = [TTkK.NONE]*len(self._columns) self._alignments = [TTkK.NONE]*len(self._columns)
self._columnColors = kwargs.get('columnColors' , [TTkColor.RST]*len(self._columns) ) self._columnColors = kwargs.get('columnColors' , [TTkColor.RST]*len(self._columns) )
self._selectColor = kwargs.get('selectColor' , TTkColor.BOLD ) self._selectColor = kwargs.get('selectColor' , TTkColor.BOLD )
self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD ) self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD )
self._hlayout = TTkHBoxLayout()
self.setLayout(self._hlayout)
self._selected = -1 self._selected = -1
TTkSpacer(parent=self)
self._vscroller = TTkScrollBar(parent=self)
self._vscroller.valueChanged.connect(self.scrollTo)
self.setFocusPolicy(TTkK.ClickFocus) self.setFocusPolicy(TTkK.ClickFocus)
def _initSignals(self):
# self.cellActivated(int row, int column)
# self.cellChanged(int row, int column)
# self.cellClicked(int row, int column)
# self.cellDoubleClicked(int row, int column)
# self.cellEntered(int row, int column)
# self.cellPressed(int row, int column)
# self.currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
# self.currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
# self.itemActivated(QTableWidgetItem *item)
# self.itemChanged(QTableWidgetItem *item)
# self.itemClicked(QTableWidgetItem *item)
# self.itemDoubleClicked(QTableWidgetItem *item)
# self.itemEntered(QTableWidgetItem *item)
# self.itemPressed(QTableWidgetItem *item)
# self.itemSelectionChanged()
pass
def viewFullAreaSize(self) -> (int, int):
return self.width(), len(self._tableData)
def viewDisplayedSize(self) -> (int, int):
return self.size()
def items(self): return self._tableData
def setAlignment(self, alignments): def setAlignment(self, alignments):
if len(alignments) != len(self._columns): if len(alignments) != len(self._columns):
return return
self._alignments = alignments self._alignments = alignments
def setHeader(self, header):
if len(header) != len(self._columns):
return
self._header = header
def setColumnSize(self, columns): def setColumnSize(self, columns):
self._columns = columns self._columns = columns
self._columnColors = [TTkColor.RST]*len(self._columns) self._columnColors = [TTkColor.RST]*len(self._columns)
self._header = [""]*len(self._columns)
self._alignments = [TTkK.NONE]*len(self._columns) self._alignments = [TTkK.NONE]*len(self._columns)
def setColumnColors(self, colors): def setColumnColors(self, colors):
@ -89,107 +148,52 @@ class _______TTkTable(TTkWidget):
if len(item) != len(self._columns): if len(item) != len(self._columns):
return return
self._tableData.append(item) self._tableData.append(item)
self._tuneTheScroller() self.viewChanged.emit()
self.update()
def resizeEvent(self, w, h):
if self._moveTo > len(self._tableData)-h-1:
self._moveTo = len(self._tableData)-h-1
if self._moveTo < 0:
self._moveTo = 0
self._tuneTheScroller()
def _tuneTheScroller(self):
if self._vscroller is None: return
scrollTo = len(self._tableData) - self.height()
self._vscroller.setRange(0, scrollTo)
self._vscroller.pagestep = self.height()
def mousePressEvent(self, evt): def mousePressEvent(self, evt):
x,y = evt.x, evt.y _,y = evt.x, evt.y
if x == self.width() - 1: _, oy = self.getViewOffsets()
return False if y >= 0:
if y > 0: selected = oy + y
self._selected = self._moveTo + y - 1 if selected >= len(self._tableData):
selected = -1
self._selected = selected
self.update() self.update()
self.activated.emit(self._selected)
return True return True
def wheelEvent(self, evt):
# delta = self.height()
delta = 5
if evt.evt == TTkK.WHEEL_Up:
delta = -delta
# self.scrollTo(self._moveTo + delta)
self._vscroller.value = self._moveTo + delta
return True
@pyTTkSlot(int)
def scrollTo(self, to):
max = len(self._tableData) - self.height()
if to>max: to=max
if to<0: to=0
self._moveTo = to
self.update()
def paintEvent(self): def paintEvent(self):
w,h = self.size() w,h = self.size()
_, oy = self.getViewOffsets()
total = 0 total = 0
variableCols = 0 variableCols = 0
slicesize = 0 # Retrieve the free size
for width in self._columns: for width in self._columns:
if width > 0: if width > 0:
total += width total += width
else: else:
variableCols += 1 variableCols += 1
if variableCols > 0: # Define the list of cols sizes
slicesize = int((w-total)/variableCols) sizes = []
# TTkLog.debug(f"ss:{slicesize}, w:{w}") for width in self._columns:
if width > 0:
sizes.append(width)
else:
sizes.append((w-total)//variableCols)
variableCols -= 1
maxItems = len(self._tableData) maxItems = len(self._tableData)
itemFrom = self._moveTo -1 itemFrom = oy
if itemFrom > maxItems-h: itemFrom = maxItems-h if itemFrom > maxItems-h: itemFrom = maxItems-h
if itemFrom < 0 : itemFrom = 0 if itemFrom < 0 : itemFrom = 0
itemTo = itemFrom + h itemTo = itemFrom + h
if itemTo > maxItems: itemTo = maxItems if itemTo > maxItems: itemTo = maxItems
# TTkLog.debug(f"moveto:{self._moveTo}, maxItems:{maxItems}, f:{itemFrom}, t{itemTo}, h:{h}, sel:{self._selected}") # TTkLog.debug(f"moveto:{self._moveTo}, maxItems:{maxItems}, f:{itemFrom}, t{itemTo}, h:{h}, sel:{self._selected}")
def _lineDraw(_y, _val, _item, _inColor=None): y = 0
_x = 0
for i in range(0,len(_item)):
_txt = _item[i]
_width = self._columns[i]
_color = self._columnColors[i]
_align = self._alignments[i]
if _inColor is not None:
_color = _inColor
if _width < 0:
_width = slicesize
if _width > 0:
_line = ""
_lentxt = len(_txt)
if _lentxt > _width:
_line += _txt[0:_width]
else:
_pad = _width-_lentxt
if _align == TTkK.NONE or _align == TTkK.LEFT_ALIGN:
_line += _txt + " "*_pad
elif _align == TTkK.RIGHT_ALIGN:
_line += " "*_pad + _txt
elif _align == TTkK.CENTER_ALIGN:
_p1 = _pad//2
_p2 = _pad-_p1
_line += " "*_p1 + _txt+" "*_p2
elif _align == TTkK.JUSTIFY:
# TODO: Text Justification
_line += _txt + " "*_pad
self._canvas.drawText(pos=(_x,_y), text=_line, color=_color.modParam(val=-_val))
_line += " "
_x += _width + 1
_lineDraw(0,0,self._header,self._headerColor)
y = 1
for it in range(itemFrom, itemTo): for it in range(itemFrom, itemTo):
item = self._tableData[it] item = self._tableData[it]
if self._selected > 0: if self._selected > 0:
val = self._selected - itemFrom val = self._selected - itemFrom
else: else:
@ -197,12 +201,56 @@ class _______TTkTable(TTkWidget):
if val < 0 : val = 0 if val < 0 : val = 0
if val > h : val = h if val > h : val = h
if it == self._selected: if it == self._selected:
_lineDraw(y,val,item,self._selectColor) colors = [self._selectColor]*len(self._columnColors)
self._canvas.drawTableLine(pos=(0,y), items=item, sizes=sizes, colors=colors, alignments=self._alignments)
else: else:
_lineDraw(y,val,item) colors = [c.modParam(val=-val) for c in self._columnColors]
self._canvas.drawTableLine(pos=(0,y), items=item, sizes=sizes, colors=colors, alignments=self._alignments)
y+=1 y+=1
class TTkTableView(TTkAbstractScrollView):
__slots__ = ( '_header', '_tableView', '_showHeader', 'activated')
def __init__(self, *args, **kwargs):
TTkAbstractScrollView.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTableView' )
if 'parent' in kwargs: kwargs.pop('parent')
self._showHeader = kwargs.get('showHeader', True)
self.setLayout(TTkGridLayout())
self._tableView = _TTkTableView(*args, **kwargs)
self._header = _TTkTableViewHeader(*args, **kwargs)
self.layout().addWidget(self._header,0,0)
self.layout().addWidget(self._tableView,1,0)
# Forward the tableSignals
self.viewMovedTo = self._tableView.viewMovedTo
self.viewSizeChanged = self._tableView.viewSizeChanged
self.activated = self._tableView.activated
self.viewChanged = self._tableView.viewChanged
if not self._showHeader:
self._header.hide()
@pyTTkSlot(int, int)
def viewMoveTo(self, x, y):
self._tableView.viewMoveTo(x, y)
def getViewOffsets(self):
return self._tableView.getViewOffsets()
def viewFullAreaSize(self) -> (int, int):
return self._tableView.viewFullAreaSize()
def viewDisplayedSize(self) -> (int, int):
return self._tableView.viewDisplayedSize()
def setAlignment(self, *args, **kwargs) :
self._tableView.setAlignment(*args, **kwargs)
self._header.setAlignment(*args, **kwargs)
def setHeader(self, *args, **kwargs) :
self._header.setHeader(*args, **kwargs)
def setColumnSize(self, *args, **kwargs) :
self._tableView.setColumnSize(*args, **kwargs)
self._header.setColumnSize(*args, **kwargs)
def setColumnColors(self, *args, **kwargs):
self._tableView.setColumnColors(*args, **kwargs)
def appendItem(self, *args, **kwargs) :
self._tableView.appendItem(*args, **kwargs)

52
TermTk/TTkWidgets/texedit.py

@ -27,56 +27,44 @@ from TermTk.TTkCore.log import TTkLog
from TermTk.TTkWidgets.widget import * from TermTk.TTkWidgets.widget import *
from TermTk.TTkLayouts.gridlayout import TTkGridLayout from TermTk.TTkLayouts.gridlayout import TTkGridLayout
from TermTk.TTkWidgets.scrollbar import TTkScrollBar from TermTk.TTkWidgets.scrollbar import TTkScrollBar
from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
class _TTkTextEditView(TTkWidget): class _TTkTextEditView(TTkAbstractScrollView):
__slots__ = ('_lines','_moveTo') __slots__ = ('_lines')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs) super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , '_TTkTextEditView' ) self._name = kwargs.get('name' , '_TTkTextEditView' )
self._lines = [] self._lines = []
self._moveTo = 0
@pyTTkSlot(str) @pyTTkSlot(str)
def setText(self, text): def setText(self, text):
self._lines = text.split('\n') self._lines = text.split('\n')
self.update() self.update()
def viewFullAreaSize(self) -> (int, int):
return self.width(), len(self._lines)
def viewDisplayedSize(self) -> (int, int):
return self.size()
def paintEvent(self): def paintEvent(self):
_, oy = self.getViewOffsets()
y = 0 y = 0
for t in self._lines[self._moveTo:]: for t in self._lines[oy:]:
self._canvas.drawText(pos=(0,y), text=t) self._canvas.drawText(pos=(0,y), text=t)
y+=1 y+=1
def wheelEvent(self, evt): class TTkTextEdit(TTkAbstractScrollArea):
delta = TTkCfg.scrollDelta __slots__ = ('_textEditView')
if evt.evt == TTkK.WHEEL_Up:
delta = -delta
self.scrollTo(self._moveTo + delta)
self.update()
return True
@pyTTkSlot(int)
def scrollTo(self, to):
# TTkLog.debug(f"to:{to},h{self._height},size:{len(self._tableData)}")
max = len(self._lines) - self.height()
if to>max: to=max
if to<0: to=0
self._moveTo = to
self.update()
class TTkTextEdit(TTkWidget):
__slots__ = ('_textEdit', '_vscroller', '_hscroller')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
TTkWidget.__init__(self, *args, **kwargs) super().__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTextEdit' ) self._name = kwargs.get('name' , 'TTkTextEdit' )
self._textEdit = _TTkTextEditView() self._textEditView = _TTkTextEditView()
self._vscroller = TTkScrollBar(orientation=TTkK.VERTICAL) self.setViewport(self._textEditView)
self._hscroller = TTkScrollBar(orientation=TTkK.HORIZONTAL)
self.setLayout(TTkGridLayout())
self.layout().addWidget(self._textEdit,0,0)
self.layout().addWidget(self._vscroller,0,1)
@pyTTkSlot(str) @pyTTkSlot(str)
def setText(self, text): def setText(self, text):
self._textEdit.setText(text) self._textEditView.setText(text)

22
TermTk/TTkWidgets/widget.py

@ -149,7 +149,7 @@ class TTkWidget:
self.resize(w, h) self.resize(w, h)
self.move(x, y) self.move(x, y)
def getPadding(self): def getPadding(self) -> (int, int, int, int) :
return self._padt, self._padb, self._padl, self._padr return self._padt, self._padb, self._padl, self._padr
def setPadding(self, top, bottom, left, right): def setPadding(self, top, bottom, left, right):
@ -161,16 +161,16 @@ class TTkWidget:
self._padr = right self._padr = right
self.update(repaint=True, updateLayout=True) self.update(repaint=True, updateLayout=True)
def mouseDoubleClickEvent(self, evt): return False def mouseDoubleClickEvent(self, evt) -> bool : return False
def mouseMoveEvent(self, evt): return False def mouseMoveEvent(self, evt) -> bool : return False
def mouseDragEvent(self, evt): return False def mouseDragEvent(self, evt) -> bool : return False
def mousePressEvent(self, evt): return False def mousePressEvent(self, evt) -> bool : return False
def mouseReleaseEvent(self, evt): return False def mouseReleaseEvent(self, evt) -> bool : return False
def wheelEvent(self, evt): return False def wheelEvent(self, evt) -> bool : return False
def enterEvent(self, evt): return False def enterEvent(self, evt) -> bool : return False
def leaveEvent(self, evt): return False def leaveEvent(self, evt) -> bool : return False
def keyPressEvent(self, evt): return False def keyPressEvent(self, evt) -> bool : return False
def keyReleaseEvent(self, evt): return False def keyReleaseEvent(self, evt) -> bool : return False
@staticmethod @staticmethod
def _mouseEventLayoutHandle(evt, layout): def _mouseEventLayoutHandle(evt, layout):

3
TermTk/__init__.py

@ -2,4 +2,5 @@ from .TTkCore import *
from .TTkGui import * from .TTkGui import *
from .TTkLayouts import * from .TTkLayouts import *
from .TTkWidgets import * from .TTkWidgets import *
from .TTkTestWidgets import * from .TTkTestWidgets import *
from .TTkAbstract import *

1
docs/BUGS.md

@ -6,3 +6,4 @@
- [Minor] Window does not reshape to the max/min layout during initialization - [Minor] Window does not reshape to the max/min layout during initialization
- [Minor] test.ui.009.py - Starting layout size is wrong - [Minor] test.ui.009.py - Starting layout size is wrong
- [Major] ~~[Canvas Paint] The Top Left corner start with the bottom right corner color~~ - [Major] ~~[Canvas Paint] The Top Left corner start with the bottom right corner color~~
- [Minor] DrawText, handle "tabs"

5
docs/TODO.md

@ -49,6 +49,11 @@
- [ ] Add addLayout method - Nested layouts - [ ] Add addLayout method - Nested layouts
- [x] Add Grid Layout - [x] Add Grid Layout
### AbstractScrollArea
- [ ] Implement something that mimic the QAbstactScrollArea
https://doc.qt.io/qt-5/qabstractscrollarea.html
https://doc.qt.io/qt-5/qscrollarea.html
### Overlay widget ### Overlay widget
- [ ] Rewrite the Handling (ttk.py) - [ ] Rewrite the Handling (ttk.py)
It would be nice to have it as child outside the layour It would be nice to have it as child outside the layour

Loading…
Cancel
Save