diff --git a/TermTk/TTkWidgets/TTkModelView/tablewidget.py b/TermTk/TTkWidgets/TTkModelView/tablewidget.py index c4f0ea36..abe2abdc 100644 --- a/TermTk/TTkWidgets/TTkModelView/tablewidget.py +++ b/TermTk/TTkWidgets/TTkModelView/tablewidget.py @@ -43,6 +43,35 @@ class _DefaultTableModel(TTkAbstractTableModel): def data(self, row, col): return f"{row}x{col}" +class _HeaderView(): + '''_HeaderView + This is a placeholder for a future "TTkHeaderView" + ''' + __slots__ = ('_visible','visibilityUpdated') + def __init__(self) -> None: + self.visibilityUpdated = pyTTkSignal(bool) + self._visible = True + + @pyTTkSlot(bool) + def setVisible(self, visible: bool): + '''setVisible''' + if self._visible == visible: return + self._visible = visible + self.visibilityUpdated.emit(visible) + + @pyTTkSlot() + def show(self): + '''show''' + self.setVisible(True) + + @pyTTkSlot() + def hide(self): + '''hide''' + self.setVisible(False) + + def isVisible(self) -> bool: + return self._visible + class TTkTableWidget(TTkAbstractScrollView): '''TTkTableWidget''' @@ -64,9 +93,11 @@ class TTkTableWidget(TTkAbstractScrollView): } __slots__ = ( '_tableModel', - '_vHeaderSize', + '_vHeaderSize', '_hHeaderSize', '_showVSeparators', '_showHSeparators', + '_verticalHeader', '_horizontallHeader', '_colsPos', '_rowsPos', + '_internal', '_selected', '_hSeparatorSelected', '_vSeparatorSelected', '_hoverPos', '_dragPos', '_sortColumn', '_sortOrder', @@ -88,9 +119,12 @@ class TTkTableWidget(TTkAbstractScrollView): self._showHSeparators = vSeparator self._showVSeparators = hSeparator + self._verticalHeader = _HeaderView() + self._horizontallHeader = _HeaderView() self._selected = None self._hoverPos = None self._dragPos = None + self._internal = {} self._hSeparatorSelected = None self._vSeparatorSelected = None self._sortColumn = -1 @@ -104,25 +138,28 @@ class TTkTableWidget(TTkAbstractScrollView): # self.clear() self.setPadding(1,0,0,0) self.viewChanged.connect(self._viewChangedHandler) + self._verticalHeader.visibilityUpdated.connect(self.update) + self._horizontallHeader.visibilityUpdated.connect(self.update) def _refreshLayout(self): rows = self._tableModel.rowCount() cols = self._tableModel.columnCount() - self._vHeaderSize = 1+max(len(self._tableModel.headerData(_p, TTkK.VERTICAL)) for _p in range(rows) ) + self._vHeaderSize = vhs= 1+max(len(self._tableModel.headerData(_p, TTkK.VERTICAL)) for _p in range(rows) ) + self._hHeaderSize = hhs= 1 if self._showVSeparators: self._colsPos = [(1+x)*11 for x in range(cols)] else: self._colsPos = [(1+x)*10 for x in range(cols)] if self._showHSeparators: - self._rowsPos = [(1+x)*2 for x in range(rows)] + self._rowsPos = [1+x*2 for x in range(rows)] else: - self._rowsPos = [(1+x) for x in range(rows)] + self._rowsPos = [1+x for x in range(rows)] self._selected = [[False]*cols for _ in range(rows)] # Overridden function def viewFullAreaSize(self) -> tuple[int, int]: w = self._vHeaderSize+self._colsPos[-1] - h = 1+self._rowsPos[-1] + h = self._hHeaderSize+self._rowsPos[-1] return w,h # Overridden function @@ -141,6 +178,11 @@ class TTkTableWidget(TTkAbstractScrollView): x,y = self.getViewOffsets() self.layout().setOffset(-x,-y) + def verticalHeader(self): + pass + def horizontalHeader(self): + pass + def setModel(self, model) -> None: self._tableModel = model self._refreshLayout() @@ -161,24 +203,27 @@ class TTkTableWidget(TTkAbstractScrollView): return super().leaveEvent(evt) def _findCell(self, x, y): - vx = self._vHeaderSize + vhs = self._vHeaderSize + hhs = self._hHeaderSize ox, oy = self.getViewOffsets() - x,y = x+ox-vx, y+oy-1 + x,y = x+ox-vhs, y+oy-hhs rp = self._rowsPos cp = self._colsPos for row,py in enumerate(rp): if py>y: - for col,px in enumerate(cp): - if px>x: - return row,col - return None + break + for col,px in enumerate(cp): + if px>x: + break + return row,col def mouseMoveEvent(self, evt) -> bool: - vx = self._vHeaderSize + vhs = self._vHeaderSize + hhs = self._hHeaderSize x,y = evt.x,evt.y self._hoverPos = None - if x bool: x,y = evt.x, evt.y ox, oy = self.getViewOffsets() - vx = self._vHeaderSize + vhs = self._vHeaderSize + hhs = self._hHeaderSize self._hSeparatorSelected = None self._vSeparatorSelected = None # Handle Header Events - if y == 0: - x += ox-vx + if y < hhs: + x += ox-vhs for i, c in enumerate(self._colsPos): if x == c: # I-th separator selected @@ -208,8 +254,8 @@ class TTkTableWidget(TTkAbstractScrollView): # self.sortItems(i, order) break return True - elif x│<-(1,0)->│<-(2,0)->│<-(3,0)->│ + # 1 ─────────┼─────────┼─────────┼─────────┼ + # <-(0,1)->│<-(1,1)->│<-(2,1)->│<-(3,1)->│ + # 3 ─────────┼─────────┼─────────┼─────────┼ + # <-(0,2)->│<-(1,2)->│<-(2,2)->│<-(3,2)->│ + # 4 ─────────┼─────────┼─────────┼─────────┼ + # <-(0,3)->│<-(1,3)->│<-(2,3)->│<-(3,3)->│ h-cell = 5 = 10-(4+1) + # │ abc │ │ │ + # │ de │ │ │ + # │ │ │ │ + # │ │ │ │ + # 10 ─────────┼─────────┼─────────┼─────────┼ + # <-(0,4)->│<-(1,4)->│<-(2,4)->│<-(3,4)->│ + # 12 ─────────┼─────────┼─────────┼─────────┼ + # <-(0,5)->│<-(1,5)->│<-(2,5)->│<-(3,5)->│ + # 14 ─────────┼─────────┼─────────┼─────────┼ + # + # + # + # + # + # + # + # + # + # + # + # def paintEvent(self, canvas) -> None: style = self.currentStyle() @@ -324,19 +400,22 @@ class TTkTableWidget(TTkAbstractScrollView): cols = self._tableModel.columnCount() rp = self._rowsPos cp = self._colsPos - vx = self._vHeaderSize + vhs = self._vHeaderSize + hhs = self._hHeaderSize # Draw Cells sliceCol=list(zip([-1]+cp,cp)) - sliceRow=list(zip([0]+rp,rp)) + sliceRow=list(zip([-1]+rp,rp)) for row in range(rows): ya,yb = sliceRow[row] - if ya>h+oy: break - if ybh : break + if ybw+ox: break - if xbw : break + if xb h : break - if y < 1: continue + y = rp[row]-oy+hhs + if y > h : break + if y < hhs: continue bgA:TTkColor = c if (c:=color.mod(0,row).background()) else TTkColor.RST bgB:TTkColor = c if (c:=color.mod(0,row+1).background()) else TTkColor.RST lineColorMix:TTkColor = bgA + lineColor if bgA == bgB == TTkColor.RST: if row=rows-1 or bgB==TTkColor.RST: - canvas.fill(char='▀', pos=(vx,y), size=(1-ox+cp[-1],1), color=bgA.invertFgBg()) + canvas.fill(char='▀', pos=(vhs,y), size=(1-ox+cp[-1],1), color=bgA.invertFgBg()) else: - canvas.fill(char='▄', pos=(vx,y), size=(1-ox+cp[-1],1), color=bgA+bgB.invertFgBg()) + canvas.fill(char='▄', pos=(vhs,y), size=(1-ox+cp[-1],1), color=bgA+bgB.invertFgBg()) - canvas.drawTTkString(pos=( vx,y), text=hline) + canvas.drawTTkString(pos=( vhs,y), text=hline) # Draw Top/Left Corner - canvas.drawText(pos=(0,0), text=' ', width=vx, color=separatorColor.invertFgBg() ) + canvas.drawText(pos=(0,0), text=' ', width=vhs, color=separatorColor.invertFgBg() ) selectedColorInv = selectedColor.background().invertFgBg() # Draw Select H-Edges for row in range(rows): - y = rp[row]-oy - if y > h : break - if y < 1: continue + y = rp[row]-oy+hhs + if y > h : break + if y < hhs: continue # Draw Top Line # selMixA:TTkColor = c.background()+selectedColorInv if (row < rows-1 and (c:=color.mod(0,row ).background())) else selectedColorInv # selMixB:TTkColor = c.background()+selectedColorInv if (row < rows-1 and (c:=color.mod(0,row+1).background())) else selectedColorInv @@ -407,10 +486,10 @@ class TTkTableWidget(TTkAbstractScrollView): selMixB:TTkColor = selectedColorInv for col in range(cols): xa,xb = sliceCol[col] - xa = max(vx,vx+xa-ox) - xb = max(vx,vx+xb-ox) + xa = max(vhs,vhs+xa-ox) + xb = max(vhs,vhs+xb-ox) if xa>w: break - if xbh: break - if yb<1: continue + if ybw: break - if x h : break - if y < 1: continue + if y < hhs: continue for col in range(cols): - x = cp[col]-ox+vx + x = cp[col]-ox+vhs if x>w: break - if xh+oy: break - if ybh : break + if yb +# +# 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. + +import timeit + +bigString1 = "abc"*10000+"a" +bigString2 = bigString1 +bigString3 = "abc"*10000+"a" +bigString4 = "abc"*10000+"b" +bigString5 = "abc"*10000+"bc" + +# print(bigString1) + +def test1(): return (bigString1==bigString1,bigString1==bigString1,bigString1==bigString1,bigString1==bigString1) +def test2(): return (bigString1==bigString2,bigString1==bigString2,bigString1==bigString2,bigString1==bigString2) +def test3(): return (bigString1==bigString3,bigString1==bigString3,bigString1==bigString3,bigString1==bigString3) +def test4(): return (bigString1==bigString4,bigString1==bigString4,bigString1==bigString4,bigString1==bigString4) +def test5(): return (bigString1==bigString5,bigString1==bigString5,bigString1==bigString5,bigString1==bigString5) + + +loop = 100000 + +a = {} + +iii = 1 +while (testName := f'test{iii}') and (testName in globals()): + result = timeit.timeit(f'{testName}(*a)', globals=globals(), number=loop) + # print(f"test{iii}) fps {loop / result :.3f} - s {result / loop:.10f} - {result / loop} {globals()[testName](*a)}") + print(f"test{iii:02}) | {result / loop:.10f} sec. | {loop / result : 15.3f} Fps ╞╡-> {globals()[testName](*a)}") + iii+=1