diff --git a/TermTk/TTkAbstract/abstractitemmodel.py b/TermTk/TTkAbstract/abstractitemmodel.py index 550d84eb..402e7d5d 100644 --- a/TermTk/TTkAbstract/abstractitemmodel.py +++ b/TermTk/TTkAbstract/abstractitemmodel.py @@ -22,6 +22,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal + class TTkAbstractItemModel(): + __slots__ = ( + # Signals + 'dataChanged' + ) def __init__(self, *args, **kwargs): - pass \ No newline at end of file + self.dataChanged = pyTTkSignal() diff --git a/TermTk/TTkCore/helper.py b/TermTk/TTkCore/helper.py index c44ed3e3..e597b584 100644 --- a/TermTk/TTkCore/helper.py +++ b/TermTk/TTkCore/helper.py @@ -196,7 +196,7 @@ class TTkHelper: widget.paintEvent() # Compose all the canvas to the parents - # From the deepest childs to the bottom + # From the deepest children to the bottom pushToTerminal = False sortedUpdateWidget = [ (w, TTkHelper.widgetDepth(w)) for w in updateWidgets] sortedUpdateWidget = sorted(sortedUpdateWidget, key=lambda w: -w[1]) diff --git a/TermTk/TTkGui/theme.py b/TermTk/TTkGui/theme.py index a6aa24ab..648daaca 100644 --- a/TermTk/TTkGui/theme.py +++ b/TermTk/TTkGui/theme.py @@ -115,8 +115,6 @@ class TTkTheme(): └ ─ ┴ ┘ ''' - - buttonBox = ( ('┌','─','┐', '│',' ','│', @@ -136,6 +134,10 @@ class TTkTheme(): hscroll = ('◀','┄','▓','▶') vscroll = ('▲','┊','▓','▼') + tree = ('•','▶','▼',' ', + '│','╿') + + # 0 1 2 3 4 5 menuBar = ('├','─','┤','┄','┄','▶') @@ -268,3 +270,6 @@ class TTkTheme(): tabOffsetColorFocus = tabOffsetColor tabBorderColorFocus = TTkColor.fg("#ffff88") tabSelectColorFocus = TTkColor.fg("#ffff88")+TTkColor.bg("#000066")+TTkColor.BOLD + + treeHeaderColor = TTkColor.fg("#ffffff")+TTkColor.bg("#444444")+TTkColor.BOLD + treeSelectedColor = TTkColor.fg("#ffff88")+TTkColor.bg("#000066")+TTkColor.BOLD \ No newline at end of file diff --git a/TermTk/TTkTypes/__init__.py b/TermTk/TTkTypes/__init__.py index 976bfc8b..2c64069d 100644 --- a/TermTk/TTkTypes/__init__.py +++ b/TermTk/TTkTypes/__init__.py @@ -1,2 +1 @@ -from .viewitem import * -from .treewidgetitem import * +from .viewitem import * \ No newline at end of file diff --git a/TermTk/TTkWidgets/Fancy/__init__.py b/TermTk/TTkWidgets/Fancy/__init__.py new file mode 100644 index 00000000..85b8abe8 --- /dev/null +++ b/TermTk/TTkWidgets/Fancy/__init__.py @@ -0,0 +1,6 @@ +from .table import * +from .tableview import * +from .tree import * +from .treeview import * +from .treewidget import * +from .treewidgetitem import * diff --git a/TermTk/TTkWidgets/table.py b/TermTk/TTkWidgets/Fancy/table.py similarity index 91% rename from TermTk/TTkWidgets/table.py rename to TermTk/TTkWidgets/Fancy/table.py index 713fad50..0987fa18 100644 --- a/TermTk/TTkWidgets/table.py +++ b/TermTk/TTkWidgets/Fancy/table.py @@ -27,11 +27,11 @@ 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 +from TermTk.TTkWidgets.Fancy.tableview import TTkFancyTableView from TermTk.TTkLayouts.gridlayout import TTkGridLayout from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea -class TTkTable(TTkAbstractScrollArea): +class TTkFancyTable(TTkAbstractScrollArea): __slots__ = ( '_tableView', 'activated', # Forwarded Methods @@ -42,9 +42,9 @@ class TTkTable(TTkAbstractScrollArea): def __init__(self, *args, **kwargs): TTkAbstractScrollArea.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkTable' ) + self._name = kwargs.get('name' , 'TTkFancyTable' ) if 'parent' in kwargs: kwargs.pop('parent') - self._tableView = TTkTableView(*args, **kwargs) + self._tableView = TTkFancyTableView(*args, **kwargs) # Forward the signal self.activated = self._tableView.activated diff --git a/TermTk/TTkWidgets/tableview.py b/TermTk/TTkWidgets/Fancy/tableview.py similarity index 96% rename from TermTk/TTkWidgets/tableview.py rename to TermTk/TTkWidgets/Fancy/tableview.py index 63022be5..1c6e2930 100644 --- a/TermTk/TTkWidgets/tableview.py +++ b/TermTk/TTkWidgets/Fancy/tableview.py @@ -31,11 +31,11 @@ from TermTk.TTkWidgets.widget import TTkWidget from TermTk.TTkLayouts.gridlayout import TTkGridLayout from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView -class _TTkTableViewHeader(TTkWidget): +class _TTkFancyTableViewHeader(TTkWidget): __slots__ = ('_header', '_alignments', '_headerColor', '_columns') def __init__(self, *args, **kwargs): TTkWidget.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , '_TTkTableViewHeader' ) + self._name = kwargs.get('name' , '_TTkFancyTableViewHeader' ) self._columns = kwargs.get('columns' , [-1] ) self._header = [""]*len(self._columns) self._alignments = [TTkK.NONE]*len(self._columns) @@ -80,7 +80,7 @@ class _TTkTableViewHeader(TTkWidget): self._canvas.drawTableLine(pos=(0,0), items=self._header, sizes=sizes, colors=colors, alignments=self._alignments) -class _TTkTableView(TTkAbstractScrollView): +class _TTkFancyTableView(TTkAbstractScrollView): __slots__ = ( '_alignments', '_headerColor', '_columns', '_columnColors', @@ -94,7 +94,7 @@ class _TTkTableView(TTkAbstractScrollView): self._tableDataWidget = [] self._shownWidgets = [] TTkAbstractScrollView.__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , '_TTkTableView' ) + self._name = kwargs.get('name' , '_TTkFancyTableView' ) # define signals self.activated = pyTTkSignal(int) # Value self.doubleClicked = pyTTkSignal(int) # Value @@ -335,7 +335,7 @@ class _TTkTableView(TTkAbstractScrollView): 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) -class TTkTableView(TTkAbstractScrollView): +class TTkFancyTableView(TTkAbstractScrollView): __slots__ = ( '_header', '_tableView', '_showHeader', 'activated', # Forwarded Methods @@ -344,12 +344,12 @@ class TTkTableView(TTkAbstractScrollView): def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkTableView' ) + self._name = kwargs.get('name' , 'TTkFancyTableView' ) 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._tableView = _TTkFancyTableView(*args, **kwargs) + self._header = _TTkFancyTableViewHeader(*args, **kwargs) self.layout().addWidget(self._header,0,0) self.layout().addWidget(self._tableView,1,0) # Forward the tableSignals diff --git a/TermTk/TTkWidgets/Fancy/tree.py b/TermTk/TTkWidgets/Fancy/tree.py new file mode 100644 index 00000000..1e51b9b0 --- /dev/null +++ b/TermTk/TTkWidgets/Fancy/tree.py @@ -0,0 +1,61 @@ +#!/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.Fancy.treewidget import TTkFancyTreeWidget +from TermTk.TTkLayouts.gridlayout import TTkGridLayout +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea + +class TTkFancyTree(TTkAbstractScrollArea): + __slots__ = ( + '_treeView', 'activated', + # Forwarded Methods + 'setAlignment', 'setHeader', 'setHeaderLabels', 'setColumnSize', 'setColumnColors', 'appendItem', 'addTopLevelItem' ) + + def __init__(self, *args, **kwargs): + TTkAbstractScrollArea.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkFancyTree' ) + if 'parent' in kwargs: kwargs.pop('parent') + self._treeView = TTkFancyTreeWidget(*args, **kwargs) + # Forward the signal + self.activated = self._treeView.activated + + self.setFocusPolicy(TTkK.ClickFocus) + self.setViewport(self._treeView) + + # Forwarded Methods + self.setAlignment = self._treeView.setAlignment + self.setHeader = self._treeView.setHeader + self.setHeaderLabels = self._treeView.setHeaderLabels + self.setColumnSize = self._treeView.setColumnSize + self.setColumnColors = self._treeView.setColumnColors + self.appendItem = self._treeView.appendItem + self.addTopLevelItem = self._treeView.addTopLevelItem + + + diff --git a/TermTk/TTkWidgets/treeview.py b/TermTk/TTkWidgets/Fancy/treeview.py similarity index 90% rename from TermTk/TTkWidgets/treeview.py rename to TermTk/TTkWidgets/Fancy/treeview.py index fc08dd5b..3a5d296e 100644 --- a/TermTk/TTkWidgets/treeview.py +++ b/TermTk/TTkWidgets/Fancy/treeview.py @@ -27,13 +27,13 @@ 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 +from TermTk.TTkWidgets.Fancy.tableview import TTkFancyTableView -class TTkTreeView(TTkTableView): +class TTkFancyTreeView(TTkFancyTableView): __slots__ = ( '_header', '_treeView', '_showHeader', 'activated') def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) - self._name = kwargs.get('name' , 'TTkTreeView' ) + self._name = kwargs.get('name' , 'TTkFancyTreeView' ) # if 'parent' in kwargs: kwargs.pop('parent') diff --git a/TermTk/TTkWidgets/Fancy/treewidget.py b/TermTk/TTkWidgets/Fancy/treewidget.py new file mode 100644 index 00000000..80cd4959 --- /dev/null +++ b/TermTk/TTkWidgets/Fancy/treewidget.py @@ -0,0 +1,157 @@ +#!/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.TTkLayouts.gridlayout import TTkGridLayout +from TermTk.TTkWidgets.Fancy.tableview import TTkFancyTableView +from TermTk.TTkWidgets.Fancy.treewidgetitem import TTkFancyTreeWidgetItem + +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, TTkFancyTreeWidgetItem) + + 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 = self._treeWidgetItem.childIndicatorPolicy() == TTkK.DontShowIndicator + self._isLeaf |= self._treeWidgetItem.childIndicatorPolicy() == TTkK.DontShowIndicatorWhenChildless and not self._treeWidgetItem.children() + 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) + + 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 TTkFancyTreeWidget(TTkFancyTableView): + __slots__ = ( '_topLevelItems') + + def __init__(self, *args, **kwargs): + super().__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkFancyTreeView' ) + self._topLevelItems = TTkFancyTreeWidgetItem(None) + self.doubleClicked.connect(self._doubleClickItem) + # if 'parent' in kwargs: kwargs.pop('parent') + + def _expand(self, item, depth): + item.setExpand(True) + item.refresh() + toExpand = [] + index = self.indexOf(item.data())+1 + if index != 0: + for child in item.children(): + 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) + item.refresh() + index = self.indexOf(item.data()) + parent = item.parent() + if item == parent.children()[-1]: + self.removeItemsFrom(index+1) + else: + nextItemIndex = parent.children().index(item) + nextItem = parent.children()[nextItemIndex+1] + indexTo = self.indexOf(nextItem.data()) + for id in reversed(range(index+1,indexTo)): + self.removeItemAt(id) + + @pyTTkSlot(int) + def _doubleClickItem(self, index): + if not (item := self.itemAt(index)): return + if item[0]._isLeaf: return + if not item[0]._treeWidgetItem.expand(): # we need to expand the TTkFancyTreeWidgetItem + self._expand(item=item[0]._treeWidgetItem, depth=item[0]._depth+1) + else: # we need to shrink the TTkFancyTreeWidgetItem + self._shrink(item=item[0]._treeWidgetItem) + + + @pyTTkSlot(bool, _TTkDisplayedTreeItem, TTkFancyTreeWidgetItem) + def _controlClicked(self, status, widget, item): + TTkLog.debug(f"{status} {widget._name}") + if status: # we need to expand the TTkFancyTreeWidgetItem + self._expand(item=item, depth=(widget._depth+1)) + else: # we need to shrink the TTkFancyTreeWidgetItem + self._shrink(item=item) + + def _addTreeWidgetItem(self, item, depth=0, index=-1): + if not isinstance(item, TTkFancyTreeWidgetItem): + raise TypeError("TTkFancyTreeWidgetItem is required in TTkFancyTreeWidget.addTopLevelItem(item)") + if item.parent() is None: + self._topLevelItems.addChild(item) + displayedItems = item.data().copy() + 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) diff --git a/TermTk/TTkTypes/treewidgetitem.py b/TermTk/TTkWidgets/Fancy/treewidgetitem.py similarity index 87% rename from TermTk/TTkTypes/treewidgetitem.py rename to TermTk/TTkWidgets/Fancy/treewidgetitem.py index 18af1571..0b2c7cd8 100644 --- a/TermTk/TTkTypes/treewidgetitem.py +++ b/TermTk/TTkWidgets/Fancy/treewidgetitem.py @@ -27,15 +27,15 @@ from TermTk.TTkCore.constant import TTkK from TermTk.TTkCore.log import TTkLog from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal -class TTkTreeWidgetItem(): - __slots__ = ('_parent', '_data', '_childs', '_expand', '_childIndicatorPolicy', +class TTkFancyTreeWidgetItem(): + __slots__ = ('_parent', '_data', '_children', '_expand', '_childIndicatorPolicy', # Signals 'refreshData') def __init__(self, *args, **kwargs): # Signals - self.refreshData = pyTTkSignal(TTkTreeWidgetItem) + self.refreshData = pyTTkSignal(TTkFancyTreeWidgetItem) self._data = args[0] - self._childs = [] + self._children = [] self._childIndicatorPolicy = kwargs.get('childIndicatorPolicy', TTkK.DontShowIndicatorWhenChildless) self._expand = False self._parent = kwargs.get("parent", None) @@ -56,7 +56,7 @@ class TTkTreeWidgetItem(): return self._expand def addChild(self, item): - self._childs.append(item) + self._children.append(item) item._parent = self def data(self): @@ -65,5 +65,5 @@ class TTkTreeWidgetItem(): def parent(self): return self._parent - def childs(self): - return self._childs \ No newline at end of file + def children(self): + return self._children \ No newline at end of file diff --git a/TermTk/TTkWidgets/TTkPickers/__init__.py b/TermTk/TTkWidgets/TTkPickers/__init__.py index d06eed9f..4fad8737 100644 --- a/TermTk/TTkWidgets/TTkPickers/__init__.py +++ b/TermTk/TTkWidgets/TTkPickers/__init__.py @@ -1 +1,2 @@ -from .colorpicker import * \ No newline at end of file +from .colorpicker import * +from .filepicker import * \ No newline at end of file diff --git a/TermTk/TTkWidgets/TTkPickers/filepicker.py b/TermTk/TTkWidgets/TTkPickers/filepicker.py new file mode 100644 index 00000000..bc8801f9 --- /dev/null +++ b/TermTk/TTkWidgets/TTkPickers/filepicker.py @@ -0,0 +1,43 @@ +#!/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 os import walk + +from TermTk.TTkWidgets.window import TTkWindow +from TermTk.TTkWidgets.Fancy.treewidgetitem import TTkFancyTreeWidgetItem + +class _FileWidgetItem(TTkFancyTreeWidgetItem): + def __init__(self, *args, **kwargs): + TTkFancyTreeWidgetItem.__init__(self, *args, **kwargs) + +class TTkFileDialogPicker(TTkWindow): + def __init__(self, *args, **kwargs): + TTkWindow.__init__(self, *args, **kwargs) + self._name = kwargs.get('name' , 'TTkFileDialogPicker' ) + +''' +for (dirpath, dirnames, filenames) in walk('/tmp'): + print(f"{dirpath} {dirnames} {filenames}") + break +''' \ No newline at end of file diff --git a/TermTk/TTkWidgets/__init__.py b/TermTk/TTkWidgets/__init__.py index 473fc6a1..48f7e891 100644 --- a/TermTk/TTkWidgets/__init__.py +++ b/TermTk/TTkWidgets/__init__.py @@ -1,29 +1,28 @@ -from .widget import * -from .spacer import * -from .frame import * +from .widget import * +from .spacer import * +from .frame import * from .resizableframe import * -from .splitter import * -from .label import * -from .button import * -from .checkbox import * -from .radiobutton import * -from .combobox import * -from .lineedit import * -from .texedit import * -from .scrollbar import * -from .scrollarea import * -from .window import * -from .tabwidget import * -from .list_ import * -from .listwidget import * -from .table import * -from .tableview import * -from .tree import * -from .treeview import * -from .treewidget import * -from .graph import * -from .menubar import * -from .TTkPickers import * -from .spinbox import * -from .image import TTkImage -from .about import TTkAbout +from .splitter import * +from .label import * +from .button import * +from .checkbox import * +from .radiobutton import * +from .combobox import * +from .lineedit import * +from .texedit import * +from .scrollbar import * +from .scrollarea import * +from .window import * +from .tabwidget import * +from .list_ import * +from .listwidget import * +from .graph import * +from .menubar import * +from .TTkPickers import * +from .spinbox import * +from .image import TTkImage +from .about import TTkAbout +from .tree import TTkTree +from .treewidget import TTkTreeWidget +from .treewidgetitem import TTkTreeWidgetItem +from .Fancy import * diff --git a/TermTk/TTkWidgets/splitter.py b/TermTk/TTkWidgets/splitter.py index b5182b28..6c20f0cc 100644 --- a/TermTk/TTkWidgets/splitter.py +++ b/TermTk/TTkWidgets/splitter.py @@ -32,7 +32,7 @@ class TTkSplitter(TTkFrame): __slots__ = ( '_splitterInitialized', '_orientation', '_separators', '_separatorsRef', '_sizeRef', '_initSizes', - '_separatorSelected', '_mouseDelta') + '_separatorSelected') def __init__(self, *args, **kwargs): self._splitterInitialized = False # self._splitterInitialized = True @@ -194,11 +194,9 @@ class TTkSplitter(TTkFrame): def mousePressEvent(self, evt): self._separatorSelected = None - self._mouseDelta = (evt.x, evt.y) x,y = evt.x, evt.y # TTkLog.debug(f"{self._separators} {evt}") - for i in range(len(self._separators)): - val = self._separators[i] + for i, val in enumerate(self._separators): if self._orientation == TTkK.HORIZONTAL: if x == val: self._separatorSelected = i diff --git a/TermTk/TTkWidgets/tree.py b/TermTk/TTkWidgets/tree.py index f427b37c..b0c39704 100644 --- a/TermTk/TTkWidgets/tree.py +++ b/TermTk/TTkWidgets/tree.py @@ -22,13 +22,9 @@ # 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): @@ -43,18 +39,18 @@ class TTkTree(TTkAbstractScrollArea): if 'parent' in kwargs: kwargs.pop('parent') self._treeView = TTkTreeWidget(*args, **kwargs) # Forward the signal - self.activated = self._treeView.activated + # self.activated = self._treeView.activated self.setFocusPolicy(TTkK.ClickFocus) self.setViewport(self._treeView) # Forwarded Methods - self.setAlignment = self._treeView.setAlignment - self.setHeader = self._treeView.setHeader + #self.setAlignment = self._treeView.setAlignment + #self.setHeader = self._treeView.setHeader self.setHeaderLabels = self._treeView.setHeaderLabels - self.setColumnSize = self._treeView.setColumnSize - self.setColumnColors = self._treeView.setColumnColors - self.appendItem = self._treeView.appendItem + #self.setColumnSize = self._treeView.setColumnSize + #self.setColumnColors = self._treeView.setColumnColors + #self.appendItem = self._treeView.appendItem self.addTopLevelItem = self._treeView.addTopLevelItem diff --git a/TermTk/TTkWidgets/treewidget.py b/TermTk/TTkWidgets/treewidget.py index c2529cfb..218f24bd 100644 --- a/TermTk/TTkWidgets/treewidget.py +++ b/TermTk/TTkWidgets/treewidget.py @@ -23,135 +23,205 @@ # SOFTWARE. from TermTk.TTkCore.cfg import TTkCfg +from TermTk.TTkCore.color import TTkColor 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 = self._treeWidgetItem.childIndicatorPolicy() == TTkK.DontShowIndicator - self._isLeaf |= self._treeWidgetItem.childIndicatorPolicy() == TTkK.DontShowIndicatorWhenChildless and not self._treeWidgetItem.childs() - 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) - - 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') +from TermTk.TTkWidgets.treewidgetitem import TTkTreeWidgetItem +from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollView +from TermTk.TTkCore.signal import pyTTkSlot + +from dataclasses import dataclass + +class TTkTreeWidget(TTkAbstractScrollView): + __slots__ = ( '_items', '_header', '_columnsPos', '_cache', + '_selectedId', '_selected', '_separatorSelected', '_mouseDelta', + '_headerColor', '_selectedColor') + @dataclass(frozen=True) + class _Cache: + item: TTkTreeWidgetItem + level: int + data: list def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkTreeView' ) - self._topLevelItems = TTkTreeWidgetItem(None) - self.doubleClicked.connect(self._doubleClickItem) - # if 'parent' in kwargs: kwargs.pop('parent') - - def _expand(self, item, depth): - item.setExpand(True) - item.refresh() - 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) - item.refresh() - 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(int) - def _doubleClickItem(self, index): - if not (item := self.itemAt(index)): return - if item[0]._isLeaf: return - if not item[0]._treeWidgetItem.expand(): # we need to expand the TtkTreeWidgetItem - self._expand(item=item[0]._treeWidgetItem, depth=item[0]._depth+1) - else: # we need to shrink the TtkTreeWidgetItem - self._shrink(item=item[0]._treeWidgetItem) - - - @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 = item.data().copy() - 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, ) + self._selected = None + self._selectedId = None + self._separatorSelected = None + self._items = [] + self._header = kwargs.get('header',[]) + self._columnsPos = [] + self._cache = [] + self._headerColor = kwargs.get('headerColor',TTkCfg.theme.treeHeaderColor) + self._selectedColor = kwargs.get('selectedColor',TTkCfg.theme.treeSelectedColor) + self.setMinimumHeight(1) + self.setFocusPolicy(TTkK.ClickFocus) + + # Overridden function + def viewFullAreaSize(self) -> (int, int): + w = self._columnsPos[-1] if self._columnsPos else 0 + h = 1+sum([c.size() for c in self._items]) + # TTkLog.debug(f"{w=} {h=}") + return w,h + + # Overridden function + def viewDisplayedSize(self) -> (int, int): + # TTkLog.debug(f"{self.size()=}") + return self.size() def addTopLevelItem(self, item): - self._addTreeWidgetItem(item) + item.dataChanged.connect(self._refreshCache) + self._items.append(item) + self._refreshCache() + self.viewChanged.emit() + self.update() def setHeaderLabels(self, labels): - columns = [-1]*len(labels) - self.setColumnSize(columns) - self.setHeader(labels) + self._header = labels + # Set 20 as default column size + self._columnsPos = [20+x*20 for x in range(len(labels))] + self.viewChanged.emit() + self.update() + + def mouseDoubleClickEvent(self, evt): + _,y = evt.x, evt.y + _, oy = self.getViewOffsets() + y -= 1-oy + if 0 <= y < len(self._cache): + item = self._cache[y].item + item.setExpanded(not item.isExpanded()) + if self._selected: + self._selected.setSelected(False) + self._selectedId = y + self._selected = item + self._selected.setSelected(True) + + self.update() + return True + + def focusOutEvent(self): + self._separatorSelected = None + + def mousePressEvent(self, evt): + x,y = evt.x, evt.y + ox, oy = self.getViewOffsets() + + x += ox + + self._separatorSelected = None + self._mouseDelta = (evt.x, evt.y) + + # Handle Header Events + if y == 0: + for i, c in enumerate(self._columnsPos): + if x == c: + self._separatorSelected = i + self.update() + return True + # Handle Tree/Table Events + y += oy-1 + if 0 <= y < len(self._cache): + item = self._cache[y].item + level = self._cache[y].level + if level*2 <= x < level*2+3: + item.setExpanded(not item.isExpanded()) + else: + if self._selected: + self._selected.setSelected(False) + self._selectedId = y + self._selected = item + self._selected.setSelected(True) + self.update() + return True + + def mouseDragEvent(self, evt): + ''' + :: + + columnPos (Selected = 2) + 0 1 2 3 4 + ----|-------|--------|----------|---| + Mouse (Drag) Pos + ^ + I consider at least 4 char (3+1) as spacing + Min Selected Pos = (Selected+1) * 4 + + ''' + if self._separatorSelected is not None: + x,y = evt.x, evt.y + ox, oy = self.getViewOffsets() + y += oy + x += ox + ss = self._separatorSelected + pos = max((ss+1)*4, x) + self._columnsPos[ss] = pos + # Align the previous Separators if pushed + for i in range(ss): + self._columnsPos[i] = min(self._columnsPos[i], pos-(ss-i)*4) + # Align the next Separators if pushed + for i in range(ss, len(self._columnsPos)): + self._columnsPos[i] = max(self._columnsPos[i], pos+(i-ss)*4) + self.update() + self.viewChanged.emit() + return True + return False + + @pyTTkSlot() + def _refreshCache(self): + ''' I save a representation fo the displayed tree in a cache array + to avoid eccessve recursion over the items and + identify quickly the nth displayed line to improve the interaction + + _cache is an array of TTkTreeWidget._Cache: + [ item, level, data=[txtCol1, txtCol2, txtCol3, ... ]] + ''' + self._cache = [] + def _addToCache(_child, _level): + tt = TTkCfg.theme.tree + _data = [] + for _il in range(len(self._header)): + _data.append(_child.data(_il)) + if not _child.children(): + _data[0] = f"{' '*_level} {tt[0]} {_data[0]}" + elif _child.isExpanded(): + _data[0] = f"{' '*_level} {tt[2]} {_data[0]}" + else: + _data[0] = f"{' '*_level} {tt[1]} {_data[0]}" + self._cache.append(TTkTreeWidget._Cache( + item = _child, + level = _level, + data = _data)) + if _child.isExpanded(): + for _c in _child.children(): + _addToCache(_c, _level+1) + for c in self._items: + _addToCache(c,0) + self.update() + self.viewChanged.emit() + + def paintEvent(self): + x,y = self.getViewOffsets() + w,h = self.size() + tt = TTkCfg.theme.tree + + # Draw header first: + for i,l in enumerate(self._header): + hx = 0 if i==0 else self._columnsPos[i-1]+1 + hx1 = self._columnsPos[i] + self._canvas.drawText(pos=(hx-x,0), text=l, width=hx1-hx, color=self._headerColor) + # Draw header separators + for sx in self._columnsPos: + self._canvas.drawChar(pos=(sx-x,0), char=tt[5], color=self._headerColor) + for sy in range(1,h): + self._canvas.drawChar(pos=(sx-x,sy), char=tt[4]) + + # Draw cache + for i, c in enumerate(self._cache): + if i-y<0 : continue + item = c.item + level = c.level + color = self._selectedColor if item.isSelected() else TTkColor.RST + for il in range(len(self._header)): + lx = 0 if il==0 else self._columnsPos[il-1]+1 + lx1 = self._columnsPos[il] + self._canvas.drawText(pos=(lx-x,i-y+1), text=c.data[il], width=lx1-lx, color=color) diff --git a/TermTk/TTkWidgets/treewidgetitem.py b/TermTk/TTkWidgets/treewidgetitem.py new file mode 100644 index 00000000..357b3a99 --- /dev/null +++ b/TermTk/TTkWidgets/treewidgetitem.py @@ -0,0 +1,100 @@ +#!/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.TTkAbstract.abstractitemmodel import TTkAbstractItemModel + + +class TTkTreeWidgetItem(TTkAbstractItemModel): + __slots__ = ('_parent', '_data', '_children', '_expanded', '_selected', + '_childIndicatorPolicy', + # Signals + 'refreshData') + + def __init__(self, *args, **kwargs): + # Signals + self.refreshData = pyTTkSignal(TTkTreeWidgetItem) + super().__init__(self, *args, **kwargs) + self._children = [] + self._data = args[0] if len(args)>0 and type(args[0])==list else None + self._parent = kwargs.get('parent', None) + self._childIndicatorPolicy = kwargs.get('childIndicatorPolicy', TTkK.DontShowIndicatorWhenChildless) + self._expanded = False + self._selected = False + self._parent = kwargs.get("parent", None) + + def addChild(self, child): + self._children.append(child) + child._parent = self + child.dataChanged.connect(self.emitDataChanged) + self.dataChanged.emit() + + def addChildren(self, children): + for child in children: + self.addChild(child) + + def child(self, index): + if 0 <= index < len(self._children): + return self._children[index] + return None + + def children(self): + return self._children + + def data(self, column, role=None): + if column >= len(self._data): + return None + return self._data[column] + + @pyTTkSlot() + def emitDataChanged(self): + self.dataChanged.emit() + + # def setDisabled(disabled): + # pass + + def setExpanded(self, expand): + self._expanded = expand + self.emitDataChanged() + + def setSelected(self, select): + self._selected = select + + # def isDisabled(): + # pass + + def isExpanded(self): + return self._expanded + + def isSelected(self): + return self._selected + + def size(self): + if self._expanded: + return 1 + sum([c.size() for c in self._children]) + else: + return 1 diff --git a/TermTk/TTkWidgets/widget.py b/TermTk/TTkWidgets/widget.py index 2e846b20..0f005d08 100644 --- a/TermTk/TTkWidgets/widget.py +++ b/TermTk/TTkWidgets/widget.py @@ -48,7 +48,7 @@ class TTkWidget(TMouseEvents,TKeyEvents): │ (x,y)┌─────────────────────────┐ │ │ │ padt (Top Padding) │ │ │ │ ┌───────────────┐ │ height │ - │ │padl│ Layout/childs │padr│ │ + │ │padl│ Layout/child │padr│ │ │ │ └───────────────┘ │ │ │ │ padb (Bottom Pad.) │ │ │ └─────────────────────────┘ │ diff --git a/demo/demo.py b/demo/demo.py index 3eb048b7..9199cb36 100755 --- a/demo/demo.py +++ b/demo/demo.py @@ -32,9 +32,7 @@ import TermTk as ttk from showcase.layout_basic import demoLayout from showcase.layout_nested import demoLayoutNested from showcase.layout_span import demoLayoutSpan -from showcase.table import demoTable from showcase.tab import demoTab -from showcase.tree import demoTree from showcase.graph import demoGraph from showcase.splitter import demoSplitter from showcase.windows import demoWindows @@ -43,6 +41,9 @@ from showcase.scrollarea import demoScrollArea from showcase.list import demoList from showcase.menubar import demoMenuBar from showcase.colorpicker import demoColorPicker +from showcase.tree import demoTree +from showcase.fancytable import demoFancyTable +from showcase.fancytree import demoFancyTree words = ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit,", "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."] def getWord(): @@ -78,9 +79,10 @@ def demoShowcase(root=None, border=True): tabWidgets = ttk.TTkTabWidget(parent=mainFrame, border=False, visible=False) tabWidgets.addTab(demoFormWidgets(), " Form Test ") tabWidgets.addTab(demoList(), " List Test ") - tabWidgets.addTab(demoTable(), " Table Test ") - tabWidgets.addTab(demoTree(), " Tree Test ") + tabWidgets.addTab(demoTree(), " Tree Test") tabWidgets.addTab(demoTab(), " Tab Test ") + tabWidgets.addTab(demoFancyTable(), " Old Table ") + tabWidgets.addTab(demoFancyTree(), " Old Tree ") listMenu.addItem(f"Pickers") tabPickers = ttk.TTkTabWidget(parent=mainFrame, border=False, visible=False) diff --git a/demo/gittk.py b/demo/gittk.py index 4ed584f2..1ca04cea 100755 --- a/demo/gittk.py +++ b/demo/gittk.py @@ -59,7 +59,7 @@ else: gittk = ttk.TTkWindow(parent=root,pos = (1,1), size=(100,40), title="gittk", border=True, layout=ttk.TTkGridLayout()) gittkVsplitter = ttk.TTkSplitter(parent=gittk, orientation=ttk.TTkK.VERTICAL) -tableCommit = ttk.TTkTable(parent=gittkVsplitter, selectColor=ttk.TTkColor.bg('#882200')) +tableCommit = ttk.TTkFancyTable(parent=gittkVsplitter, selectColor=ttk.TTkColor.bg('#882200')) gittkHsplitter = ttk.TTkSplitter(parent=gittkVsplitter, orientation=ttk.TTkK.HORIZONTAL) diffText = ttk.TTkTextEdit(parent=gittkHsplitter) gittkHsplitter.addWidget(ttk.TTkTestWidgetSizes(border=True, title="Details"),20) diff --git a/demo/showcase/table.py b/demo/showcase/fancytable.py similarity index 96% rename from demo/showcase/table.py rename to demo/showcase/fancytable.py index 91585c04..a3a52195 100755 --- a/demo/showcase/table.py +++ b/demo/showcase/fancytable.py @@ -36,13 +36,13 @@ def getSentence(a,b): table_ii = 1000 -def demoTable(root=None): +def demoFancyTable(root=None): frame = ttk.TTkFrame(parent=root, border=False, layout=ttk.TTkVBoxLayout()) top = ttk.TTkFrame(parent=frame, border=False, layout=ttk.TTkHBoxLayout()) btn1 = ttk.TTkButton(parent=top, maxHeight=3, border=True, text='Add') btn2 = ttk.TTkButton(parent=top, maxHeight=3, border=True, text='Add Many') - table1 = ttk.TTkTable(parent=frame, selectColor=ttk.TTkColor.bg('#882200')) + table1 = ttk.TTkFancyTable(parent=frame, selectColor=ttk.TTkColor.bg('#882200')) table1.setColumnSize((5,10,-1,10,20)) table1.setAlignment(( @@ -88,7 +88,7 @@ def main(): root = ttk.TTk() win_table = ttk.TTkWindow(parent=root,pos = (3,3), size=(150,40), title="Test Table 1", layout=ttk.TTkHBoxLayout(), border=True) - demoTable(win_table) + demoFancyTable(win_table) root.mainloop() if __name__ == "__main__": diff --git a/demo/showcase/fancytree.py b/demo/showcase/fancytree.py new file mode 100755 index 00000000..1517e3f5 --- /dev/null +++ b/demo/showcase/fancytree.py @@ -0,0 +1,119 @@ +#!/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 +import random +import argparse + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + +words = ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit,", "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."] +def getWord(): + return random.choice(words) +def getSentence(a,b): + return " ".join([getWord() for i in range(0,random.randint(a,b))]) + +def demoFancyTree(root=None): + # tw = ttk.TTkFancyTreeWidget(parent=rootTree1) + tw = ttk.TTkFancyTree(parent=root) + tw.setHeaderLabels(["Column 1", "Column 2", "Column 3"]) + tw.setColumnSize((-1,20,20)) + 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.TTkFancyTreeWidgetItem(["String A", "String B", "String C"]) + l2 = ttk.TTkFancyTreeWidgetItem(["String AA", "String BB", "String CC"]) + l3 = ttk.TTkFancyTreeWidgetItem(["String AAA", "String BBB", "String CCC"]) + l4 = ttk.TTkFancyTreeWidgetItem(["String AAAA", "String BBBB", "String CCCC"]) + l5 = ttk.TTkFancyTreeWidgetItem(["String AAAAA", "String BBBBB", "String CCCCC"]) + + l2.addChild(l5) + + for i in range(3): + l1_child = ttk.TTkFancyTreeWidgetItem(["Child A" + str(i), "Child B" + str(i), "Child C" + str(i)]) + l1.addChild(l1_child) + + for j in range(2): + l2_child = ttk.TTkFancyTreeWidgetItem(["Child AA" + str(j), "Child BB" + str(j), "Child CC" + str(j)]) + l2.addChild(l2_child) + + for j in range(2): + l3_child = ttk.TTkFancyTreeWidgetItem(["Child AAA" + str(j), "Child BBB" + str(j), "Child CCC" + str(j)]) + l3.addChild(l3_child) + + for j in range(2): + l4_child = ttk.TTkFancyTreeWidgetItem(["Child AAAA" + str(j), "Child BBBB" + str(j), "Child CCCC" + str(j)]) + l4.addChild(l4_child) + + for j in range(2): + l5_child = ttk.TTkFancyTreeWidgetItem(["Child AAAAA" + str(j), "Child BBBBB" + str(j), "Child CCCCC" + str(j)]) + l5.addChild(l5_child) + + l6 = ttk.TTkFancyTreeWidgetItem(["RND", "RND", "RND"], childIndicatorPolicy=ttk.TTkK.ShowIndicator) + + def updateChildren(item): + if item.children(): return + for _ in range(0,random.randint(3,8)): + child = ttk.TTkFancyTreeWidgetItem([getWord(),getWord(),getWord()]) + if random.randint(0,10)>5: + child.setChildIndicatorPolicy(ttk.TTkK.ShowIndicator) + child.refreshData.connect(updateChildren) + item.addChild(child) + + + l6.refreshData.connect(updateChildren) + + tw.addTopLevelItem(l1) + tw.addTopLevelItem(l2) + tw.addTopLevelItem(l3) + tw.addTopLevelItem(l4) + tw.addTopLevelItem(l6) + + return tw + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen', action='store_true') + args = parser.parse_args() + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + if args.f: + rootTree1 = root + root.setLayout(ttk.TTkGridLayout()) + else: + rootTree1 = ttk.TTkWindow(parent=root,pos = (0,0), size=(70,40), title="Test Tree 1", layout=ttk.TTkGridLayout(), border=True) + demoFancyTree(rootTree1) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/demo/showcase/tree.py b/demo/showcase/tree.py index afd98bdb..af57feea 100755 --- a/demo/showcase/tree.py +++ b/demo/showcase/tree.py @@ -40,15 +40,9 @@ def getSentence(a,b): return " ".join([getWord() for i in range(0,random.randint(a,b))]) def demoTree(root=None): - # tw = ttk.TTkTreeWidget(parent=rootTree1) tw = ttk.TTkTree(parent=root) 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"]) @@ -79,17 +73,17 @@ def demoTree(root=None): l6 = ttk.TTkTreeWidgetItem(["RND", "RND", "RND"], childIndicatorPolicy=ttk.TTkK.ShowIndicator) - def updateChilds(item): - if item.childs(): return + def updateChildren(item): + if item.children(): return for _ in range(0,random.randint(3,8)): child = ttk.TTkTreeWidgetItem([getWord(),getWord(),getWord()]) if random.randint(0,10)>5: child.setChildIndicatorPolicy(ttk.TTkK.ShowIndicator) - child.refreshData.connect(updateChilds) + child.refreshData.connect(updateChildren) item.addChild(child) - l6.refreshData.connect(updateChilds) + l6.refreshData.connect(updateChildren) tw.addTopLevelItem(l1) tw.addTopLevelItem(l2) diff --git a/docs/MDNotes/TODO.md b/docs/MDNotes/TODO.md index 2cee2620..c4081684 100644 --- a/docs/MDNotes/TODO.md +++ b/docs/MDNotes/TODO.md @@ -93,10 +93,16 @@ - [ ] Basic Implementation - [ ] Events (Signal/Slots) - [x] Themes - #### Table Widget + #### Fancy Table Widget - [x] Basic Implementation - [ ] Events (Signal/Slots) - [ ] Themes + + #### Tree Widget + - [x] Basic Implementation + - [ ] Implement cache/pagination for big data + - [ ] Events (Signal/Slots) + - [ ] Themes #### Window Widget - [x] Basic Implementation - [ ] Events (Signal/Slots) diff --git a/tests/test.ui.008.table.py b/tests/test.ui.008.fancy.table.py similarity index 96% rename from tests/test.ui.008.table.py rename to tests/test.ui.008.fancy.table.py index 1677d550..3b1dfb74 100755 --- a/tests/test.ui.008.table.py +++ b/tests/test.ui.008.fancy.table.py @@ -42,13 +42,13 @@ btn1 = ttk.TTkButton(parent=root, pos=(0,0), size=(5,3), border=True, text='Add' btn2 = ttk.TTkButton(parent=root, pos=(5,0), size=(10,3), border=True, text='Add Many') win_table1 = ttk.TTkWindow(parent=root,pos = (3,3), size=(150,40), title="Test Table 1", layout=ttk.TTkHBoxLayout(), border=True) -table1 = ttk.TTkTable(parent=win_table1, selectColor=ttk.TTkColor.bg('#882200')) +table1 = ttk.TTkFancyTable(parent=win_table1, selectColor=ttk.TTkColor.bg('#882200')) win_table2 = ttk.TTkWindow(parent=root,pos = (15,5), size=(100,30), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) -table2 = ttk.TTkTable(parent=win_table2) +table2 = ttk.TTkFancyTable(parent=win_table2) win_table3 = ttk.TTkWindow(parent=root,pos = (15,5), size=(130,40), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) -table3 = ttk.TTkTable(parent=win_table3, showHeader=False) +table3 = ttk.TTkFancyTable(parent=win_table3, showHeader=False) table1.setColumnSize((5,10,-1,10,20)) table1.setAlignment(( diff --git a/tests/test.ui.011.fancy.tree.py b/tests/test.ui.011.fancy.tree.py new file mode 100755 index 00000000..11e8cd29 --- /dev/null +++ b/tests/test.ui.011.fancy.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.TTkFancyTreeWidget(parent=rootTree1) +tw = ttk.TTkFancyTree(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.TTkFancyTreeWidgetItem(["String A", "String B", "String C"]) +l2 = ttk.TTkFancyTreeWidgetItem(["String AA", "String BB", "String CC"]) +l3 = ttk.TTkFancyTreeWidgetItem(["String AAA", "String BBB", "String CCC"]) +l4 = ttk.TTkFancyTreeWidgetItem(["String AAAA", "String BBBB", "String CCCC"]) +l5 = ttk.TTkFancyTreeWidgetItem(["String AAAAA", "String BBBBB", "String CCCCC"]) +l2.addChild(l5) + + +for i in range(3): + l1_child = ttk.TTkFancyTreeWidgetItem(["Child A" + str(i), "Child B" + str(i), "Child C" + str(i)]) + l1.addChild(l1_child) + +for j in range(2): + l2_child = ttk.TTkFancyTreeWidgetItem(["Child AA" + str(j), "Child BB" + str(j), "Child CC" + str(j)]) + l2.addChild(l2_child) + +for j in range(2): + l3_child = ttk.TTkFancyTreeWidgetItem(["Child AAA" + str(j), "Child BBB" + str(j), "Child CCC" + str(j)]) + l3.addChild(l3_child) + +for j in range(2): + l4_child = ttk.TTkFancyTreeWidgetItem(["Child AAAA" + str(j), "Child BBBB" + str(j), "Child CCCC" + str(j)]) + l4.addChild(l4_child) + +for j in range(2): + l5_child = ttk.TTkFancyTreeWidgetItem(["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 diff --git a/tests/test.ui.011.tree.py b/tests/test.ui.011.tree.py index 96b5ce0e..338cc4ed 100755 --- a/tests/test.ui.011.tree.py +++ b/tests/test.ui.011.tree.py @@ -27,30 +27,27 @@ import os import sys +import argparse sys.path.append(os.path.join(sys.path[0],'..')) import TermTk as ttk ttk.TTkLog.use_default_file_logging() -fullscreen = False +parser = argparse.ArgumentParser() +parser.add_argument('-f', help='Full Screen', action='store_true') +args = parser.parse_args() root = ttk.TTk() -if fullscreen: +if args.f: 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) + rootTree1 = ttk.TTkWindow(parent=root,pos = (0,0), size=(150,40), 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"]) @@ -84,5 +81,7 @@ tw.addTopLevelItem(l1) tw.addTopLevelItem(l2) tw.addTopLevelItem(l3) tw.addTopLevelItem(l4) +l1.setExpanded(True) +l3.setExpanded(True) root.mainloop() \ No newline at end of file