Browse Source

TTkTabWidget: Added closable feature

pull/44/head
Eugenio Parodi 4 years ago
parent
commit
5694b77ef9
  1. 8
      TermTk/TTkCore/canvas.py
  2. 92
      TermTk/TTkWidgets/tabwidget.py
  3. 5
      TermTk/TTkWidgets/texedit.py
  4. 2
      demo/showcase/tab.py
  5. 12
      demo/ttkode.py
  6. 25
      docs/MDNotes/Resources.md

8
TermTk/TTkCore/canvas.py

@ -215,7 +215,10 @@ class TTkCanvas:
for i in range(max(0,-x), min(len(txt),self._width-x)): for i in range(max(0,-x), min(len(txt),self._width-x)):
#self._set(y, x+i, txt[i-x], colors[i-x]) #self._set(y, x+i, txt[i-x], colors[i-x])
self._data[y][x+i] = txt[i] self._data[y][x+i] = txt[i]
self._colors[y][x+i] = colors[i].mod(x+i,y) if colors[i] == TTkColor.RST != color:
self._colors[y][x+i] = color.mod(x+i,y)
else:
self._colors[y][x+i] = colors[i].mod(x+i,y)
else: else:
text = text.replace('\t',' ') text = text.replace('\t',' ')
if lentxt < width: if lentxt < width:
@ -368,10 +371,11 @@ class TTkCanvas:
textPos = (x,y+1) textPos = (x,y+1)
self.drawText(pos=textPos, text=text, color=color) self.drawText(pos=textPos, text=text, color=color)
def drawTabButton(self, pos, size, label, sideEnd, small, status, color=TTkColor.RST): def drawTabButton(self, pos, size, sideEnd, small, status, color=TTkColor.RST):
x,y = pos x,y = pos
w,h = size w,h = size
tt = TTkCfg.theme.tab tt = TTkCfg.theme.tab
label = ' '*(w-2)
if small: if small:
if status == TTkK.Checked: if status == TTkK.Checked:
txtCenter = tt[10] + label + tt[10] txtCenter = tt[10] + label + tt[10]

92
TermTk/TTkWidgets/tabwidget.py

@ -58,20 +58,28 @@ class _TTkTabBarDragData():
def tabBar(self): return self._tabBar def tabBar(self): return self._tabBar
class TTkTabButton(TTkButton): class TTkTabButton(TTkButton):
__slots__ = ('_sideEnd', '_tabStatus') __slots__ = ('_sideEnd', '_tabStatus', '_closable', 'closeClicked', '_closeButton')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._sideEnd = TTkK.NONE self._sideEnd = TTkK.NONE
self._tabStatus = TTkK.Unchecked self._tabStatus = TTkK.Unchecked
self._closable = kwargs.get('closable', False)
self.closeClicked = pyTTkSignal()
TTkButton.__init__(self, *args, **kwargs) TTkButton.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTabButton' ) self._name = kwargs.get('name' , 'TTkTabButton' )
size = len(self.text) + 2
if self._closable:
size += 3
self._closeButton = TTkButton(parent=self, border=False, text="x", pos=(size-4,1 if self._border else 0), size=(3,1))
self._closeButton.setFocusPolicy(TTkK.ParentFocus)
self._closeButton.clicked.connect(self.closeClicked.emit)
if self._border: if self._border:
self.resize(len(self._text)+2, 3) self.resize(size, 3)
self.setMinimumSize(2+len(self._text), 3) self.setMinimumSize(size, 3)
self.setMaximumSize(2+len(self._text), 3) self.setMaximumSize(size, 3)
else: else:
self.resize(len(self._text)+2, 2) self.resize(size, 2)
self.setMinimumSize(len(self._text)+2, 2) self.setMinimumSize(size, 2)
self.setMaximumSize(len(self._text)+2, 2) self.setMaximumSize(size, 2)
self.setFocusPolicy(TTkK.ParentFocus) self.setFocusPolicy(TTkK.ParentFocus)
def sideEnd(self): def sideEnd(self):
@ -91,6 +99,9 @@ class TTkTabButton(TTkButton):
# This is a hack to force the action aftet the keypress # This is a hack to force the action aftet the keypress
# And not key release as normally happen to the button # And not key release as normally happen to the button
def mousePressEvent(self, evt): def mousePressEvent(self, evt):
if self._closable and evt.key == TTkK.MidButton:
self.closeClicked.emit()
return True
return super().mouseReleaseEvent(evt) return super().mouseReleaseEvent(evt)
def mouseReleaseEvent(self, evt): def mouseReleaseEvent(self, evt):
return False return False
@ -115,7 +126,7 @@ class TTkTabButton(TTkButton):
pos=(0,0), size=self.size(), pos=(0,0), size=self.size(),
small=(not self._border), small=(not self._border),
sideEnd=self._sideEnd, status=self._tabStatus, sideEnd=self._sideEnd, status=self._tabStatus,
label=self.text, color=self._borderColor ) color=self._borderColor )
self._canvas.drawText(pos=(1,1 if self._border else 0), text=self.text, color=self.color()) self._canvas.drawText(pos=(1,1 if self._border else 0), text=self.text, color=self.color())
class _TTkTabMenuButton(TTkMenuButton): class _TTkTabMenuButton(TTkMenuButton):
@ -211,21 +222,22 @@ _labels= │◀│La│Label1║Label2║Label3│Label4│▶│
class TTkTabBar(TTkWidget): class TTkTabBar(TTkWidget):
__slots__ = ( __slots__ = (
'_tabButtons', '_small', '_tabButtons', '_tabData', '_small',
'_highlighted', '_currentIndex','_lastIndex', '_highlighted', '_currentIndex','_lastIndex',
'_leftScroller', '_rightScroller', '_leftScroller', '_rightScroller',
'_borderColor', '_borderColor', '_tabClosable',
'_sideEnd', '_sideEnd',
#Signals #Signals
'currentChanged', 'tabBarClicked') 'currentChanged', 'tabBarClicked', 'tabCloseRequested')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._tabButtons = [] self._tabButtons = []
self._tabData = []
self._currentIndex = -1 self._currentIndex = -1
self._lastIndex = -1 self._lastIndex = -1
self._highlighted = -1 self._highlighted = -1
self._tabMovable = False self._tabMovable = False
self._tabClosable = False self._tabClosable = kwargs.get('closable',False)
self._sideEnd = TTkK.LEFT | TTkK.RIGHT self._sideEnd = TTkK.LEFT | TTkK.RIGHT
self._borderColor = TTkCfg.theme.tabBorderColor self._borderColor = TTkCfg.theme.tabBorderColor
self._small = kwargs.get('small',True) self._small = kwargs.get('small',True)
@ -245,8 +257,9 @@ class TTkTabBar(TTkWidget):
self.layout().addWidget(self._rightScroller) self.layout().addWidget(self._rightScroller)
# Signals # Signals
self.currentChanged = pyTTkSignal(int) self.currentChanged = pyTTkSignal(int)
self.tabBarClicked = pyTTkSignal(int) self.tabBarClicked = pyTTkSignal(int)
self.tabCloseRequested = pyTTkSignal(int)
self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus) self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
@ -259,27 +272,37 @@ class TTkTabBar(TTkWidget):
self._leftScroller.setSideEnd(sideEnd&TTkK.LEFT) self._leftScroller.setSideEnd(sideEnd&TTkK.LEFT)
self._updateTabs() self._updateTabs()
def addTab(self, label): def addTab(self, label, data=None):
self.insertTab(len(self._tabButtons), label) self.insertTab(len(self._tabButtons), label)
def insertTab(self, index, label): def insertTab(self, index, label, data=None):
button = TTkTabButton(parent=self, text=label, border=not self._small) button = TTkTabButton(parent=self, text=label, border=not self._small, closable=self._tabClosable)
button._borderColor = self._borderColor button._borderColor = self._borderColor
self._tabButtons.insert(index,button) self._tabButtons.insert(index,button)
self._tabData.insert(index,data)
button.clicked.connect(lambda :self.setCurrentIndex(self._tabButtons.index(button))) button.clicked.connect(lambda :self.setCurrentIndex(self._tabButtons.index(button)))
button.closeClicked.connect(lambda :self.tabCloseRequested.emit(self._tabButtons.index(button)))
self._updateTabs() self._updateTabs()
@pyTTkSlot(int)
def removeTab(self, index): def removeTab(self, index):
button = self._tabButtons[index] button = self._tabButtons[index]
self.layout().removeWidget(button) self.layout().removeWidget(button)
self._tabButtons = self._tabButtons[:index] + self._tabButtons[index+1:] self._tabButtons.pop(index)
self._tabData.pop(index)
if self._currentIndex == index: if self._currentIndex == index:
self._lastIndex = -1 self._lastIndex = -2
if self._currentIndex >= index: if self._currentIndex >= index:
self._currentIndex -= 1 self._currentIndex -= 1
self._highlighted = self._currentIndex self._highlighted = self._currentIndex
self._updateTabs() self._updateTabs()
def data(self, index):
return self._tabData[index]
def setData(self, index, data):
self._tabData[index] = data
def borderColor(self): def borderColor(self):
return self._borderColor return self._borderColor
@ -291,6 +314,12 @@ class TTkTabBar(TTkWidget):
self._rightScroller.setBorderColor(color) self._rightScroller.setBorderColor(color)
self.update() self.update()
def tabsClosable(self):
return self._tabClosable
def setTabsClosable(self, closable):
self._tabClosable = closable
def currentIndex(self): def currentIndex(self):
return self._currentIndex return self._currentIndex
@ -444,6 +473,8 @@ class TTkTabWidget(TTkFrame):
# Forward Signals # Forward Signals
'currentChanged', 'tabBarClicked', 'currentChanged', 'tabBarClicked',
# forward methods # forward methods
'tabsClosable', 'setTabsClosable',
'data', 'setData',
'currentIndex', 'setCurrentIndex') 'currentIndex', 'setCurrentIndex')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -454,7 +485,7 @@ class TTkTabWidget(TTkFrame):
TTkFrame.__init__(self, *args, **kwargs) TTkFrame.__init__(self, *args, **kwargs)
self._name = kwargs.get('name' , 'TTkTabWidget') self._name = kwargs.get('name' , 'TTkTabWidget')
self._tabBar = TTkTabBar(small = not self.border()) self._tabBar = TTkTabBar(small = not self.border(), closable=kwargs.get('closable', False))
self._topLeftLayout = None self._topLeftLayout = None
self._topRightLayout = None self._topRightLayout = None
self._tabBarTopLayout.addWidget(self._tabBar,0,1,3 if self.border() else 2,1) self._tabBarTopLayout.addWidget(self._tabBar,0,1,3 if self.border() else 2,1)
@ -474,13 +505,21 @@ class TTkTabWidget(TTkFrame):
self.rootLayout().addItem(self._tabBarTopLayout) self.rootLayout().addItem(self._tabBarTopLayout)
self._tabBarTopLayout.setGeometry(0,0,self._width,self._padt) self._tabBarTopLayout.setGeometry(0,0,self._width,self._padt)
self._tabBar.tabCloseRequested.connect(self.removeTab)
# forwarded methods # forwarded methods
self.currentIndex = self._tabBar.currentIndex self.currentIndex = self._tabBar.currentIndex
self.setCurrentIndex = self._tabBar.setCurrentIndex self.setCurrentIndex = self._tabBar.setCurrentIndex
self.data = self._tabBar.data
self.setData = self._tabBar.setData
self.tabsClosable = self._tabBar.tabsClosable
self.setTabsClosable = self._tabBar.setTabsClosable
# forwarded Signals # forwarded Signals
self.currentChanged = self._tabBar.currentChanged self.currentChanged = self._tabBar.currentChanged
self.tabBarClicked = self._tabBar.tabBarClicked self.tabBarClicked = self._tabBar.tabBarClicked
def widget(self, index):
return self._tabWidgets[index]
def currentWidget(self): def currentWidget(self):
for w in self._tabWidgets: for w in self._tabWidgets:
if w.isVisible(): if w.isVisible():
@ -539,7 +578,7 @@ class TTkTabWidget(TTkFrame):
tw.removeTab(index) tw.removeTab(index)
self.insertTab(newIndex, widget, tb.text) self.insertTab(newIndex, widget, tb.text)
self.setCurrentIndex(newIndex) self.setCurrentIndex(newIndex)
self._tabChanged(newIndex) #self._tabChanged(newIndex)
elif tw != self: elif tw != self:
tw.removeTab(index) tw.removeTab(index)
newIndex = len(self._tabWidgets) newIndex = len(self._tabWidgets)
@ -566,21 +605,22 @@ class TTkTabWidget(TTkFrame):
layout.addWidget(button) layout.addWidget(button)
return button return button
def addTab(self, widget, label): def addTab(self, widget, label, data=None):
widget.hide() widget.hide()
self._tabWidgets.append(widget) self._tabWidgets.append(widget)
self.layout().addWidget(widget) self.layout().addWidget(widget)
self._tabBar.addTab(label) self._tabBar.addTab(label, data)
def insertTab(self, index, widget, label): def insertTab(self, index, widget, label, data=None):
widget.hide() widget.hide()
self._tabWidgets.insert(index, widget) self._tabWidgets.insert(index, widget)
self.layout().addWidget(widget) self.layout().addWidget(widget)
self._tabBar.insertTab(index, label) self._tabBar.insertTab(index, label, data)
@pyTTkSlot(int)
def removeTab(self, index): def removeTab(self, index):
self.layout().removeWidget(self._tabWidgets[index]) self.layout().removeWidget(self._tabWidgets[index])
self._tabWidgets = self._tabWidgets[:index] + self._tabWidgets[index+1:] self._tabWidgets.pop(index)
self._tabBar.removeTab(index) self._tabBar.removeTab(index)
def resizeEvent(self, w, h): def resizeEvent(self, w, h):

5
TermTk/TTkWidgets/texedit.py

@ -504,6 +504,8 @@ class TTkTextEdit(TTkAbstractScrollArea):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._name = kwargs.get('name' , 'TTkTextEdit' ) self._name = kwargs.get('name' , 'TTkTextEdit' )
self._textEditView = _TTkTextEditView() self._textEditView = _TTkTextEditView()
# self.setFocusPolicy(self._textEditView.focusPolicy())
# self._textEditView.setFocusPolicy(TTkK.ParentFocus)
self.setViewport(self._textEditView) self.setViewport(self._textEditView)
self.clear = self._textEditView.clear self.clear = self._textEditView.clear
self.setText = self._textEditView.setText self.setText = self._textEditView.setText
@ -517,4 +519,5 @@ class TTkTextEdit(TTkAbstractScrollArea):
self.setLineWrapMode = self._textEditView.setLineWrapMode self.setLineWrapMode = self._textEditView.setLineWrapMode
self.wordWrapMode = self._textEditView.wordWrapMode self.wordWrapMode = self._textEditView.wordWrapMode
self.setWordWrapMode = self._textEditView.setWordWrapMode self.setWordWrapMode = self._textEditView.setWordWrapMode
# Forward Signals
self.focusChanged = self._textEditView.focusChanged

2
demo/showcase/tab.py

@ -28,7 +28,7 @@ sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk import TermTk as ttk
def demoTab(root=None, border=True): def demoTab(root=None, border=True):
tabWidget1 = ttk.TTkTabWidget(parent=root, border=border) tabWidget1 = ttk.TTkTabWidget(parent=root, border=border, closable=True)
tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.1"), "Label 1.1") tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.1"), "Label 1.1")
tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.2"), "Label 1.2") tabWidget1.addTab(ttk.TTkTestWidgetSizes(border=True, title="Frame1.2"), "Label 1.2")
tabWidget1.addTab(ttk.TTkTestWidget( border=True, title="Frame1.3"), "Label Test 1.3") tabWidget1.addTab(ttk.TTkTestWidget( border=True, title="Frame1.3"), "Label Test 1.3")

12
demo/ttkode.py

@ -136,12 +136,14 @@ class KodeTab(TTkTabWidget):
else: else:
ret = super().dropEvent(evt) ret = super().dropEvent(evt)
# Remove the widget and/or all the cascade empty splitters
if not tw._tabWidgets: if not tw._tabWidgets:
splitter = tw.parentWidget() widget = tw
if splitter.count() == 1: splitter = widget.parentWidget()
splitter.parentWidget().removeWidget(splitter) while splitter.count() == 1:
else: widget = splitter
splitter.removeWidget(tw) splitter = widget.parentWidget()
splitter.removeWidget(widget)
return ret return ret

25
docs/MDNotes/Resources.md

@ -23,4 +23,27 @@ https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
#### Domains - docstring syntax #### Domains - docstring syntax
https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-py-class https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#directive-py-class
#### ReStructuredText #### ReStructuredText
https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#explicit-markup-blocks https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#explicit-markup-blocks
## Disable SIGSTOP triggered by CTRL+S
```python
import sys, termios
attr = termios.tcgetattr(sys.stdin)
# Save the value to be restored
bak = attr[6][termios.VSTOP]
# Disable SIGSTOP triggered by CTRL+S
attr[6][termios.VSTOP]=0
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
'''. . . do stuff . . .'''
# reEnable SIGSTOP triggered by CTRL+S
attr[6][termios.VSTOP]=bak
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr)
```
### Terminal Mapping:
- CTRL-C -> termios.VINTR
- CTRL-S -> termios.VSTOP
- CTRL-Z -> termios.VSUSP
Loading…
Cancel
Save