From 210340ad9bc6bb28ccee8fb7072b280b4cf15ab4 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Tue, 2 Mar 2021 00:39:13 +0000 Subject: [PATCH] Added Tree Widget, input events forwarded in the proper order --- TermTk/TTkAbstract/abstractscrollarea.py | 16 ++ TermTk/TTkAbstract/abstractscrollview.py | 5 +- TermTk/TTkTypes/__init__.py | 2 +- .../{treeitem.py => treewidgetitem.py} | 33 +++- TermTk/TTkWidgets/__init__.py | 2 + TermTk/TTkWidgets/checkbox.py | 4 +- TermTk/TTkWidgets/tableview.py | 138 ++++++++++++++-- TermTk/TTkWidgets/tree.py | 63 ++++++++ TermTk/TTkWidgets/treewidget.py | 147 ++++++++++++++++++ TermTk/TTkWidgets/widget.py | 14 +- docs/TODO.md | 2 +- tests/test.ui.011.tree.py | 88 +++++++++++ 12 files changed, 487 insertions(+), 27 deletions(-) rename TermTk/TTkTypes/{treeitem.py => treewidgetitem.py} (70%) create mode 100644 TermTk/TTkWidgets/tree.py create mode 100644 TermTk/TTkWidgets/treewidget.py create mode 100755 tests/test.ui.011.tree.py diff --git a/TermTk/TTkAbstract/abstractscrollarea.py b/TermTk/TTkAbstract/abstractscrollarea.py index bcc17c7b..6b1ddc61 100644 --- a/TermTk/TTkAbstract/abstractscrollarea.py +++ b/TermTk/TTkAbstract/abstractscrollarea.py @@ -62,6 +62,22 @@ class TTkAbstractScrollArea(TTkWidget): self._horizontalScrollBar.setRange(0, hrange) self._horizontalScrollBar.setValue(ox) + if self._verticalScrollBarPolicy == TTkK.ScrollBarAsNeeded: + if vrange<=0: self._verticalScrollBar.hide() + else: self._verticalScrollBar.show() + elif self._verticalScrollBarPolicy == TTkK.ScrollBarAlwaysOn: + self._verticalScrollBar.show() + else: + self._verticalScrollBar.show() + + if self._horizontalScrollBarPolicy == TTkK.ScrollBarAsNeeded: + if hrange<=0: self._horizontalScrollBar.hide() + else: self._horizontalScrollBar.show() + elif self._horizontalScrollBarPolicy == TTkK.ScrollBarAlwaysOn: + self._horizontalScrollBar.show() + else: + self._horizontalScrollBar.show() + @pyTTkSlot(int) def _vscrollMoved(self, val): ox, _ = self._viewport.getViewOffsets() diff --git a/TermTk/TTkAbstract/abstractscrollview.py b/TermTk/TTkAbstract/abstractscrollview.py index 68b46f2d..1e081705 100644 --- a/TermTk/TTkAbstract/abstractscrollview.py +++ b/TermTk/TTkAbstract/abstractscrollview.py @@ -35,12 +35,13 @@ class TTkAbstractScrollView(TTkWidget): '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() + super().__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkAbstractScrollView') + self._viewOffsetX = 0 self._viewOffsetY = 0 diff --git a/TermTk/TTkTypes/__init__.py b/TermTk/TTkTypes/__init__.py index fbbe5a9b..976bfc8b 100644 --- a/TermTk/TTkTypes/__init__.py +++ b/TermTk/TTkTypes/__init__.py @@ -1,2 +1,2 @@ from .viewitem import * -from .treeitem import * +from .treewidgetitem import * diff --git a/TermTk/TTkTypes/treeitem.py b/TermTk/TTkTypes/treewidgetitem.py similarity index 70% rename from TermTk/TTkTypes/treeitem.py rename to TermTk/TTkTypes/treewidgetitem.py index 3cb01f86..a70ab5d2 100644 --- a/TermTk/TTkTypes/treeitem.py +++ b/TermTk/TTkTypes/treewidgetitem.py @@ -26,13 +26,30 @@ from TermTk.TTkCore.cfg import TTkCfg from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal -from TermTk.TTkCore.color import TTkColor -from TermTk.TTkWidgets.tableview import TTkTableView -class TTkTreeItem(): - __slots__ = () +class TTkTreeWidgetItem(): + __slots__ = ('_parent', '_data', '_childs', '_expand') def __init__(self, *args, **kwargs): - self._tableData = [] - TTkAbstractScrollView.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , '_TTkTableView' ) - \ No newline at end of file + self._data = args[0] + self._childs = [] + self._expand = False + self._parent = kwargs.get("parent", None) + + def setExpand(self, status): + self._expand = status + + def expand(self): + return self._expand + + def addChild(self, item): + self._childs.append(item) + item._parent = self + + def data(self): + return self._data + + def parent(self): + return self._parent + + def childs(self): + return self._childs \ No newline at end of file diff --git a/TermTk/TTkWidgets/__init__.py b/TermTk/TTkWidgets/__init__.py index 5781cae3..a4d70c46 100644 --- a/TermTk/TTkWidgets/__init__.py +++ b/TermTk/TTkWidgets/__init__.py @@ -14,5 +14,7 @@ from .scrollbar import * from .window import * from .table import * from .tableview import * +from .tree import * from .treeview import * +from .treewidget import * from .logviewer import * diff --git a/TermTk/TTkWidgets/checkbox.py b/TermTk/TTkWidgets/checkbox.py index 41ba2099..e35f8fe5 100644 --- a/TermTk/TTkWidgets/checkbox.py +++ b/TermTk/TTkWidgets/checkbox.py @@ -29,12 +29,13 @@ from TermTk.TTkCore.color import TTkColor from TermTk.TTkWidgets.widget import * class TTkCheckbox(TTkWidget): - __slots__ = ('_checked') + __slots__ = ('_checked', 'clicked') def __init__(self, *args, **kwargs): TTkWidget.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkCheckbox' ) # Define Signals # self.cehcked = pyTTkSignal() + self.clicked = pyTTkSignal(bool) self._checked = kwargs.get('checked', False ) self.setMinimumSize(3, 1) self.setMaximumHeight(1) @@ -56,5 +57,6 @@ class TTkCheckbox(TTkWidget): def mousePressEvent(self, evt): self._checked = not self._checked + self.clicked.emit(self._checked) self.update() return True diff --git a/TermTk/TTkWidgets/tableview.py b/TermTk/TTkWidgets/tableview.py index 7ce218e3..d4b644b3 100644 --- a/TermTk/TTkWidgets/tableview.py +++ b/TermTk/TTkWidgets/tableview.py @@ -55,7 +55,7 @@ class _TTkTableViewHeader(TTkWidget): def setColumnSize(self, columns): self._columns = columns - self._header = [""]*len(self._columns) + self._header += [""]*(len(self._columns)-len(self._header)) self._alignments = [TTkK.NONE]*len(self._columns) def paintEvent(self): @@ -84,12 +84,15 @@ class _TTkTableView(TTkAbstractScrollView): __slots__ = ( '_alignments', '_headerColor', '_columns', '_columnColors', - '_tableData', + '_tableDataId', '_tableDataText', '_tableDataWidget', '_shownWidgets', '_selectColor', '_selected', # Signals 'activated') def __init__(self, *args, **kwargs): - self._tableData = [] + self._tableDataId = [] + self._tableDataText = [] + self._tableDataWidget = [] + self._shownWidgets = [] TTkAbstractScrollView.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , '_TTkTableView' ) # define signals @@ -102,6 +105,7 @@ class _TTkTableView(TTkAbstractScrollView): self._headerColor = kwargs.get('headerColor' , TTkColor.BOLD ) self._selected = -1 self.setFocusPolicy(TTkK.ClickFocus) + self.viewChanged.connect(self._viewChangedHandler) def _initSignals(self): # self.cellActivated(int row, int column) @@ -121,13 +125,63 @@ class _TTkTableView(TTkAbstractScrollView): # self.itemSelectionChanged() pass + @pyTTkSlot() + def _viewChangedHandler(self): + w,h = self.size() + _, oy = self.getViewOffsets() + 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._tableDataText) + itemFrom = oy + if itemFrom > maxItems-h: itemFrom = maxItems-h + if itemFrom < 0 : itemFrom = 0 + itemTo = itemFrom + h + if itemTo > maxItems: itemTo = maxItems + + widgetsToHide = self._shownWidgets + self._shownWidgets = [] + y=0 + for it in range(itemFrom, itemTo): + x = 0 + item = self._tableDataWidget[it] + for iw, widget in enumerate(item): + size = sizes[iw] + if widget is not None: + if widget.parentWidget() != self: + self.addWidget(widget) + widget.setGeometry(x,y,size,1) + widget.show() + self._shownWidgets.append(widget) + x+=size+1 + y+=1 + for widget in widgetsToHide: + if widget not in self._shownWidgets: + widget.hide() + + + def viewFullAreaSize(self) -> (int, int): - return self.width(), len(self._tableData) + return self.width(), len(self._tableDataText) def viewDisplayedSize(self) -> (int, int): return self.size() - def items(self): return self._tableData + def items(self): return self._tableDataText def setAlignment(self, alignments): if len(alignments) != len(self._columns): @@ -144,19 +198,71 @@ class _TTkTableView(TTkAbstractScrollView): return self._columnColors = colors - def appendItem(self, item): + def appendItem(self, item, id=None): if len(item) != len(self._columns): return - self._tableData.append(item) + textItem = [i if isinstance(i,str) else "" for i in item] + widgetItem = [i if isinstance(i,TTkWidget) else None for i in item] + if id is not None: + self._tableDataId.append(id) + else: + self._tableDataId.append(item) + self._tableDataText.append(textItem) + self._tableDataWidget.append(widgetItem) + self.viewChanged.emit() + self.update() + + def insertItem(self, index, item, id=None): + if len(item) != len(self._columns): + return# + textItem = [i if isinstance(i,str) else "" for i in item] + widgetItem = [i if isinstance(i,TTkWidget) else None for i in item] + if id is not None: + self._tableDataId.insert(index, id) + else: + self._tableDataId.insert(index, item) + self._tableDataText.insert(index, textItem) + self._tableDataWidget.insert(index, widgetItem) self.viewChanged.emit() self.update() + def removeItem(self, item): + index = self.indexOf(item) + self.removeItemAt(index) + self.viewChanged.emit() + self.update() + + def removeItemAt(self, index): + if self._selected == index: + self._selected = -1 + del self._tableDataId[index] + del self._tableDataText[index] + del self._tableDataWidget[index] + self.viewChanged.emit() + self.update() + + def removeItemsFrom(self, index): + if self._selected >= index: + self._selected = -1 + self._tableDataId = self._tableDataId[:index] + self._tableDataText = self._tableDataText[:index] + self._tableDataWidget = self._tableDataWidget[:index] + self.viewChanged.emit() + self.update() + + def indexOf(self, id) -> int: + for index, value in enumerate(self._tableDataId): + if id is value: + return index + return -1 + + def mousePressEvent(self, evt): _,y = evt.x, evt.y _, oy = self.getViewOffsets() if y >= 0: selected = oy + y - if selected >= len(self._tableData): + if selected >= len(self._tableDataText): selected = -1 self._selected = selected self.update() @@ -183,7 +289,7 @@ class _TTkTableView(TTkAbstractScrollView): sizes.append((w-total)//variableCols) variableCols -= 1 - maxItems = len(self._tableData) + maxItems = len(self._tableDataText) itemFrom = oy if itemFrom > maxItems-h: itemFrom = maxItems-h if itemFrom < 0 : itemFrom = 0 @@ -193,7 +299,7 @@ class _TTkTableView(TTkAbstractScrollView): y = 0 for it in range(itemFrom, itemTo): - item = self._tableData[it] + item = self._tableDataText[it] if self._selected > 0: val = self._selected - itemFrom else: @@ -212,7 +318,7 @@ class TTkTableView(TTkAbstractScrollView): __slots__ = ( '_header', '_tableView', '_showHeader', 'activated') def __init__(self, *args, **kwargs): - TTkAbstractScrollView.__init__(self, *args, **kwargs) + super().__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTableView' ) if 'parent' in kwargs: kwargs.pop('parent') self._showHeader = kwargs.get('showHeader', True) @@ -254,3 +360,13 @@ class TTkTableView(TTkAbstractScrollView): self._tableView.setColumnColors(*args, **kwargs) def appendItem(self, *args, **kwargs) : self._tableView.appendItem(*args, **kwargs) + def indexOf(self, *args, **kwargs) : + return self._tableView.indexOf(*args, **kwargs) + def insertItem(self, *args, **kwargs) : + self._tableView.insertItem(*args, **kwargs) + def removeItem(self, *args, **kwargs) : + self._tableView.removeItem(*args, **kwargs) + def removeItemAt(self, *args, **kwargs) : + self._tableView.removeItemAt(*args, **kwargs) + def removeItemsFrom(self, *args, **kwargs) : + self._tableView.removeItemsFrom(*args, **kwargs) diff --git a/TermTk/TTkWidgets/tree.py b/TermTk/TTkWidgets/tree.py new file mode 100644 index 00000000..2ee60fcd --- /dev/null +++ b/TermTk/TTkWidgets/tree.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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.cfg import TTkCfg +from TermTk.TTkCore.constant import TTkK +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal +from TermTk.TTkCore.color import TTkColor +from TermTk.TTkWidgets.treewidget import TTkTreeWidget +from TermTk.TTkLayouts.gridlayout import TTkGridLayout +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea + +class TTkTree(TTkAbstractScrollArea): + __slots__ = ('_treeView', 'activated') + def __init__(self, *args, **kwargs): + TTkAbstractScrollArea.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkTree' ) + if 'parent' in kwargs: kwargs.pop('parent') + self._treeView = TTkTreeWidget(*args, **kwargs) + # Forward the signal + self.activated = self._treeView.activated + + self.setFocusPolicy(TTkK.ClickFocus) + self.setViewport(self._treeView) + + def setAlignment(self, *args, **kwargs) : + self._treeView.setAlignment(*args, **kwargs) + def setHeader(self, *args, **kwargs) : + self._treeView.setHeader(*args, **kwargs) + def setHeaderLabels(self, *args, **kwargs) : + self._treeView.setHeaderLabels(*args, **kwargs) + def setColumnSize(self, *args, **kwargs) : + self._treeView.setColumnSize(*args, **kwargs) + def setColumnColors(self, *args, **kwargs): + self._treeView.setColumnColors(*args, **kwargs) + def appendItem(self, *args, **kwargs) : + self._treeView.appendItem(*args, **kwargs) + def addTopLevelItem(self, *args, **kwargs) : + self._treeView.addTopLevelItem(*args, **kwargs) + + + diff --git a/TermTk/TTkWidgets/treewidget.py b/TermTk/TTkWidgets/treewidget.py new file mode 100644 index 00000000..8dcf9351 --- /dev/null +++ b/TermTk/TTkWidgets/treewidget.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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.cfg import TTkCfg +from TermTk.TTkCore.constant import TTkK +from TermTk.TTkCore.log import TTkLog +from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal +from TermTk.TTkCore.color import TTkColor +from TermTk.TTkWidgets.widget import TTkWidget +from TermTk.TTkWidgets.checkbox import TTkCheckbox +from TermTk.TTkWidgets.tableview import TTkTableView +from TermTk.TTkLayouts.gridlayout import TTkGridLayout +from TermTk.TTkTypes.treewidgetitem import TTkTreeWidgetItem + +class _TTkDisplayedTreeItemControl(TTkCheckbox): + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , '_TTkDisplayedTreeItemControl' ) + self.setMinimumSize(1, 1) + + def paintEvent(self): + if self._checked: + self._canvas.drawText(pos=(0,0), text="▼") + else: + self._canvas.drawText(pos=(0,0), text="▶") + + +class _TTkDisplayedTreeItem(TTkWidget): + __slots__ = ('_depth', '_control', '_text', '_id', '_clicked', '_treeWidgetItem', '_isLeaf' ) + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + #Signals + self._clicked = pyTTkSignal(bool, _TTkDisplayedTreeItem, TTkTreeWidgetItem) + + self._name = kwargs.get('name' , '_TTkDisplayedTreeItem' ) + self._depth = kwargs.get('depth' , 0 ) + self._text = kwargs.get('text' , "" ) + self._id = kwargs.get('id' , 0 ) + self._treeWidgetItem = kwargs.get('treeWidgetItem', None) + self._isLeaf = len(self._treeWidgetItem.childs())==0 + if self._isLeaf: + self._control = None + else: + self._control = _TTkDisplayedTreeItemControl(parent=self, checked=self._treeWidgetItem.expand()) + self._control.setGeometry(self._depth, 0, 1, 1) + self._control.clicked.connect(self._controlClicked) + + @pyTTkSlot(bool) + def _controlClicked(self, status): + self._clicked.emit(status, self, self._treeWidgetItem) + pass + + def paintEvent(self): + if self._isLeaf: + self._canvas.drawText(pos=(self._depth, 0), text="⏺") + self._canvas.drawText(pos=(self._depth+2, 0), text=self._text) + + +class TTkTreeWidget(TTkTableView): + __slots__ = ( '_topLevelItems') + + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkTreeView' ) + self._topLevelItems = TTkTreeWidgetItem(None) + # if 'parent' in kwargs: kwargs.pop('parent') + + def _expand(self, item, depth): + item.setExpand(True) + toExpand = [] + index = self.indexOf(item.data())+1 + if index != 0: + for child in item.childs(): + self._addTreeWidgetItem(item=child, depth=depth, index=index) + index+=1 + if child.expand(): + toExpand.append(child) + for child in toExpand: + self._expand(item=child, depth=(depth+1)) + + + def _shrink(self, item): + item.setExpand(False) + index = self.indexOf(item.data()) + parent = item.parent() + if item == parent.childs()[-1]: + self.removeItemsFrom(index+1) + else: + nextItemIndex = parent.childs().index(item) + nextItem = parent.childs()[nextItemIndex+1] + indexTo = self.indexOf(nextItem.data()) + for id in reversed(range(index+1,indexTo)): + self.removeItemAt(id) + + + @pyTTkSlot(bool, _TTkDisplayedTreeItem, TTkTreeWidgetItem) + def _controlClicked(self, status, widget, item): + TTkLog.debug(f"{status} {widget._name}") + if status: # we need to expand the TtkTreeWidgetItem + self._expand(item=item, depth=(widget._depth+1)) + else: # we need to shrink the TtkTreeWidgetItem + self._shrink(item=item) + + + + def _addTreeWidgetItem(self, item, depth=0, index=-1): + if not isinstance(item, TTkTreeWidgetItem): + raise TypeError("TTkTreeWidgetItem is required in TTkTreeWidget.addTopLevelItem(item)") + if item.parent() is None: + self._topLevelItems.addChild(item) + displayedItems = [i for i in item.data()] + displayTreeItem = _TTkDisplayedTreeItem(text=displayedItems[0], id=0, depth=depth, treeWidgetItem=item) + displayTreeItem._clicked.connect(self._controlClicked) + displayedItems[0] = displayTreeItem + if index == -1: + self.appendItem(item=displayedItems, id=item.data()) + else: + self.insertItem(item=displayedItems, id=item.data(), index=index, ) + + def addTopLevelItem(self, item): + self._addTreeWidgetItem(item) + + def setHeaderLabels(self, labels): + columns = [-1]*len(labels) + self.setColumnSize(columns) + self.setHeader(labels) \ No newline at end of file diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index 22e0d775..54edaef0 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -93,6 +93,9 @@ class TTkWidget: self.update(repaint=True, updateLayout=True) + def __del__(self): + TTkLog.debug("DESTRUCTOR") + def addWidget(self, widget): widget._parent = self if self._layout is not None: @@ -178,7 +181,7 @@ class TTkWidget: lx,ly,lw,lh =layout.geometry() # opt of bounds if xlx+lw or ylh+ly: - return True + return False for item in reversed(layout.zSortedItems): # for item in layout.zSortedItems: if isinstance(item, TTkWidgetItem) and not item.isEmpty(): @@ -216,6 +219,10 @@ class TTkWidget: return False def mouseEvent(self, evt): + if self._layout is not None: + if TTkWidget._mouseEventLayoutHandle(evt, self._layout): + return True + # handle own events if evt.evt == lbt.MouseEvent.Move: if self.mouseMoveEvent(evt): @@ -245,8 +252,7 @@ class TTkWidget: #elif evt.type() == CuEvent.KeyRelease: # self.keyReleaseEvent(evt) # Trigger this event to the childs - if self._layout is not None: - return TTkWidget._mouseEventLayoutHandle(evt, self._layout) + return False def keyEvent(self, evt): pass @@ -371,6 +377,7 @@ class TTkWidget: @pyTTkSlot() def show(self): + if self._visible: return self._canvas.show() self._visible = True self.update(updateLayout=True, updateParent=True) @@ -386,6 +393,7 @@ class TTkWidget: @pyTTkSlot() def hide(self): + if not self._visible: return self._canvas.hide() self._visible = False self.update(repaint=False, updateParent=True) diff --git a/docs/TODO.md b/docs/TODO.md index 79ca13d6..f48c1475 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -8,7 +8,7 @@ - [ ] [UTF-8] Handle "Fullwidth" forms characters https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) -- [ ] Process child events before parent +- [x] Process child events before parent ## Terminal Helper - [ ] Events diff --git a/tests/test.ui.011.tree.py b/tests/test.ui.011.tree.py new file mode 100755 index 00000000..96b5ce0e --- /dev/null +++ b/tests/test.ui.011.tree.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 Eugenio Parodi +# +# 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. + +# Demo inspired from: +# https://stackoverflow.com/questions/41204234/python-pyqt5-qtreewidget-sub-item + +import os +import sys + +sys.path.append(os.path.join(sys.path[0],'..')) +import TermTk as ttk + +ttk.TTkLog.use_default_file_logging() + +fullscreen = False + +root = ttk.TTk() +if fullscreen: + rootTree1 = root + root.setLayout(ttk.TTkGridLayout()) +else: + rootTree1 = ttk.TTkWindow(parent=root,pos = (0,0), size=(150,50), title="Test Tree 1", layout=ttk.TTkGridLayout(), border=True) + +# tw = ttk.TTkTreeWidget(parent=rootTree1) +tw = ttk.TTkTree(parent=rootTree1) +tw.setHeaderLabels(["Column 1", "Column 2", "Column 3"]) +tw.setColumnSize((20,20,-1)) +tw.setColumnColors(( + ttk.TTkColor.RST, + ttk.TTkColor.fg('#00dddd', modifier=ttk.TTkColorGradient(increment=-4)), + ttk.TTkColor.fg('#cccc00', modifier=ttk.TTkColorGradient(increment=-2)) + )) +l1 = ttk.TTkTreeWidgetItem(["String A", "String B", "String C"]) +l2 = ttk.TTkTreeWidgetItem(["String AA", "String BB", "String CC"]) +l3 = ttk.TTkTreeWidgetItem(["String AAA", "String BBB", "String CCC"]) +l4 = ttk.TTkTreeWidgetItem(["String AAAA", "String BBBB", "String CCCC"]) +l5 = ttk.TTkTreeWidgetItem(["String AAAAA", "String BBBBB", "String CCCCC"]) +l2.addChild(l5) + + +for i in range(3): + l1_child = ttk.TTkTreeWidgetItem(["Child A" + str(i), "Child B" + str(i), "Child C" + str(i)]) + l1.addChild(l1_child) + +for j in range(2): + l2_child = ttk.TTkTreeWidgetItem(["Child AA" + str(j), "Child BB" + str(j), "Child CC" + str(j)]) + l2.addChild(l2_child) + +for j in range(2): + l3_child = ttk.TTkTreeWidgetItem(["Child AAA" + str(j), "Child BBB" + str(j), "Child CCC" + str(j)]) + l3.addChild(l3_child) + +for j in range(2): + l4_child = ttk.TTkTreeWidgetItem(["Child AAAA" + str(j), "Child BBBB" + str(j), "Child CCCC" + str(j)]) + l4.addChild(l4_child) + +for j in range(2): + l5_child = ttk.TTkTreeWidgetItem(["Child AAAAA" + str(j), "Child BBBBB" + str(j), "Child CCCCC" + str(j)]) + l5.addChild(l5_child) + + +tw.addTopLevelItem(l1) +tw.addTopLevelItem(l2) +tw.addTopLevelItem(l3) +tw.addTopLevelItem(l4) + +root.mainloop() \ No newline at end of file