Browse Source

386 feat ttktree add drag (#387)

pull/385/head^2
Pier CeccoPierangioliEugenio 11 months ago committed by GitHub
parent
commit
62f9cd42a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      libs/pyTermTk/TermTk/TTkCore/TTkTerm/inputmouse.py
  2. 18
      libs/pyTermTk/TermTk/TTkGui/drag.py
  3. 1
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tree.py
  4. 38
      libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/treewidget.py
  5. 90
      libs/pyTermTk/TermTk/TTkWidgets/kodetab.py
  6. 92
      libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py
  7. 13
      libs/pyTermTk/TermTk/TTkWidgets/widget.py
  8. 135
      tests/t.ui/test.ui.011.tree.04.dnd.01.py

7
libs/pyTermTk/TermTk/TTkCore/TTkTerm/inputmouse.py

@ -86,6 +86,13 @@ class TTkMouseEvent:
Right = TTkK.WHEEL_Right
__slots__ = ('x', 'y', 'key', 'evt', 'mod', 'tap', 'raw')
x: int
y: int
key: int
evt: int
mod: int
tap: int
raw: str
def __init__(self, x: int, y: int, key: int, evt: int, mod: int, tap: int, raw: str):
self.x = x
self.y = y

18
libs/pyTermTk/TermTk/TTkGui/drag.py

@ -100,6 +100,15 @@ class TTkDnD():
'''
return self._hotSpot
def clone(self):
'''
Clone this event
:return: the cloned event
:rtype: :py:class:`TTkDnD`
'''
return TTkDnD(data=self._data, hotspot=self._hotSpot)
class TTkDnDEvent(TTkDnD):
'''
Drag and Drop event class.
@ -123,6 +132,15 @@ class TTkDnDEvent(TTkDnD):
'''
return self._pos
def clone(self):
'''
Clone this event
:return: the cloned event
:rtype: :py:class:`TTkDnDEvent`
'''
return TTkDnDEvent(data=self._data, hotspot=self._hotSpot, pos=self._pos)
class TTkDrag(TTkDnD):
__slots__ = ('_pixmap', '_showPixmap')
def __init__(self, **kwargs) -> None:

1
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/tree.py

@ -41,6 +41,7 @@ class TTkTree(TTkAbstractScrollArea):
'setHeaderLabels',
'setColumnWidth', 'resizeColumnToContents',
'sortColumn', 'sortItems',
'dragDropMode', 'setDragDropMode',
# 'appendItem', 'setAlignment', 'setColumnColors', 'setColumnSize', 'setHeader',
'addTopLevelItem', 'addTopLevelItems', 'takeTopLevelItem', 'topLevelItem', 'indexOfTopLevelItem', 'selectedItems', 'clear'])
)

38
libs/pyTermTk/TermTk/TTkWidgets/TTkModelView/treewidget.py

@ -22,14 +22,19 @@
__all__ = ['TTkTreeWidget']
from typing import List
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.color import TTkColor
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
from TermTk.TTkGui.drag import TTkDrag, TTkDnDEvent
from TermTk.TTkWidgets.TTkModelView.treewidgetitem import TTkTreeWidgetItem
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
from TermTk.TTkAbstract.abstractitemmodel import TTkAbstractItemModel
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from dataclasses import dataclass
@ -151,6 +156,7 @@ class TTkTreeWidget(TTkAbstractScrollView):
'_header', '_columnsPos',
'_selectedId', '_selected', '_separatorSelected',
'_sortColumn', '_sortOrder', '_sortingEnabled',
'_dndMode',
# Signals
'itemChanged', 'itemClicked', 'itemDoubleClicked', 'itemExpanded', 'itemCollapsed', 'itemActivated'
)
@ -162,15 +168,23 @@ class TTkTreeWidget(TTkAbstractScrollView):
widgets: list
firstLine: bool
@dataclass(frozen=True)
class _DropTreeData:
widget: TTkAbstractScrollView
items: List[TTkAbstractItemModel]
def __init__(self, *,
header=None,
sortingEnabled=True,
dragDropMode:TTkK.DragDropMode=TTkK.DragDropMode.NoDragDrop,
**kwargs) -> None:
'''
:param header: define the header labels of each column, defaults to []
:type header: list[TTkString], optional
:param sortingEnabled: enable the column sorting, defaults to False
:type sortingEnabled: bool, optional
:param dragDropMode: This property holds the drag and drop event the view will act upon, defaults to :py:class:`TTkK.DragDropMode.NoDragDrop`.
:type dragDropMode: :py:class:`TTkK.DragDropMode`, optional
'''
# Signals
self.itemActivated = pyTTkSignal(TTkTreeWidgetItem, int)
@ -180,6 +194,7 @@ class TTkTreeWidget(TTkAbstractScrollView):
self.itemExpanded = pyTTkSignal(TTkTreeWidgetItem)
self.itemCollapsed = pyTTkSignal(TTkTreeWidgetItem)
self._dndMode = dragDropMode
self._selected = None
self._selectedId = None
self._separatorSelected = None
@ -270,6 +285,14 @@ class TTkTreeWidget(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
def dragDropMode(self):
'''dragDropMode'''
return self._dndMode
def setDragDropMode(self, dndMode):
'''setDragDropMode'''
self._dndMode = dndMode
def isSortingEnabled(self) -> bool:
'isSortingEnabled'
return self._sortingEnabled
@ -432,6 +455,21 @@ class TTkTreeWidget(TTkAbstractScrollView):
self.viewChanged.emit()
self.update()
return True
elif ( self._dndMode & TTkK.DragDropMode.AllowDrag and
evt.key == TTkMouseEvent.LeftButton and self._selected ):
drag = TTkDrag()
data = TTkTreeWidget._DropTreeData(widget=self,items=[self._selected])
text = self._selected.data(0)
if text.termWidth() > 30:
text = '['+text.substring(to=27)+'...]'
else:
text = '['+text+']'
pm = TTkCanvas(text.termWidth()+2,1)
pm.drawTTkString(pos=(0,0),text=text)
drag.setPixmap(pm)
drag.setData(data)
drag.exec()
return True
return False
def _alignWidgets(self) -> None:

90
libs/pyTermTk/TermTk/TTkWidgets/kodetab.py

@ -22,6 +22,8 @@
__all__ = ['TTkKodeTab']
from typing import Callable
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.helper import TTkHelper
from TermTk.TTkCore.log import TTkLog
@ -29,7 +31,7 @@ from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.color import TTkColor, TTkColorGradient
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.tabwidget import TTkTabWidget
from TermTk.TTkWidgets.tabwidget import TTkTabWidget, _TTkNewTabWidgetDragData, _TTkTabWidgetDragData
from TermTk.TTkWidgets.splitter import TTkSplitter
from TermTk.TTkWidgets.frame import TTkFrame
from TermTk.TTkLayouts.gridlayout import TTkGridLayout
@ -117,45 +119,78 @@ class _TTkKodeTab(TTkTabWidget):
def dropEvent(self, evt:TTkDnDEvent) -> bool:
self._frameOverlay = None
x,y = evt.x, evt.y
ret = True
data = evt.data()
tb = data.tabButton()
tw = data.tabWidget()
if issubclass(type(data),_TTkNewTabWidgetDragData):
tw = None
elif issubclass(type(data),_TTkTabWidgetDragData):
tw = data.tabWidget()
else:
return False
def _processDrop(widget, label, orientation, offset):
fwold = self._baseWidget._getFirstWidget()
splitter = self.parentWidget()
index = splitter.indexOf(self)
if splitter.orientation() != orientation:
splitter.replaceWidget(index, splitter := TTkSplitter(orientation=orientation))
splitter.addWidget(self)
index=offset
splitter.insertWidget(index+offset, kt:=_TTkKodeTab(baseWidget=self._baseWidget, border=self.border(), closable=self.tabsClosable()))
kt._dropEventProxy = self._dropEventProxy
kt.addTab(widget,label)
if fwold!=(fwnew := self._baseWidget._getFirstWidget()) and fwold._hasMenu():
fwnew._importMenu(fwold)
ret = True
if y<3:
ret = super().dropEvent(evt)
else:
elif issubclass(type(data),_TTkNewTabWidgetDragData):
tw = None
widget = data.widget()
tabData = data.data()
label = data.label()
closable = data.closable()
w,h = self.size()
h-=3
y-=3
if x<w//4:
_processDrop(widget, label, TTkK.HORIZONTAL, 0)
elif x>w*3//4:
_processDrop(widget, label, TTkK.HORIZONTAL, 1)
elif y<h//4:
_processDrop(widget, label, TTkK.VERTICAL, 0)
elif y>h*3//4:
_processDrop(widget, label, TTkK.VERTICAL, 1)
else:
ret = super().dropEvent(evt)
elif issubclass(type(data),_TTkTabWidgetDragData):
tb = data.tabButton()
tw = data.tabWidget()
w,h = self.size()
h-=3
y-=3
index = tw._tabBar._tabButtons.index(tb)
widget = tw._tabWidgets[index]
label = tb.text()
def _processDrop(index, orientation, offset):
fwold = self._baseWidget._getFirstWidget()
tw.removeTab(index)
splitter = self.parentWidget()
index = splitter.indexOf(self)
if splitter.orientation() != orientation:
splitter.replaceWidget(index, splitter := TTkSplitter(orientation=orientation))
splitter.addWidget(self)
index=offset
splitter.insertWidget(index+offset, kt:=_TTkKodeTab(baseWidget=self._baseWidget, border=self.border(), closable=self.tabsClosable()))
kt.addTab(widget,tb.text())
if fwold!=(fwnew := self._baseWidget._getFirstWidget()) and fwold._hasMenu():
fwnew._importMenu(fwold)
if x<w//4:
_processDrop(index, TTkK.HORIZONTAL, 0)
tw.removeTab(index)
_processDrop(widget, label, TTkK.HORIZONTAL, 0)
elif x>w*3//4:
_processDrop(index, TTkK.HORIZONTAL, 1)
tw.removeTab(index)
_processDrop(widget, label, TTkK.HORIZONTAL, 1)
elif y<h//4:
_processDrop(index, TTkK.VERTICAL, 0)
tw.removeTab(index)
_processDrop(widget, label, TTkK.VERTICAL, 0)
elif y>h*3//4:
_processDrop(index, TTkK.VERTICAL, 1)
tw.removeTab(index)
_processDrop(widget, label, TTkK.VERTICAL, 1)
else:
ret = super().dropEvent(evt)
# Remove the widget and/or all the cascade empty splitters
self._kodeTabClosed(tw)
if tw:
self._kodeTabClosed(tw)
self.update()
return ret
@ -163,7 +198,7 @@ class _TTkKodeTab(TTkTabWidget):
def _kodeTabClosed(self, widget=None):
# Remove the widget and/or all the cascade empty splitters
fwold = self._baseWidget._getFirstWidget()
widget = widget if type(widget) is _TTkKodeTab else self
widget = widget if issubclass(type(widget), _TTkKodeTab) else self
if not widget._tabWidgets:
if splitter := widget.parentWidget():
while splitter.count() == 1 and splitter != self._baseWidget:
@ -199,6 +234,7 @@ class TTkKodeTab(TTkSplitter):
kwargs.pop('visible',None)
# self.layout().addWidget(splitter := TTkSplitter())
self._lastKodeTabWidget = _TTkKodeTab(baseWidget=self, **kwargs)
self._lastKodeTabWidget._dropEventProxy = self._dropEventProxy
self.addWidget(self._lastKodeTabWidget)
def _getFirstWidget(self):
@ -207,6 +243,12 @@ class TTkKodeTab(TTkSplitter):
while type(item:=kt.widget(0)) != _TTkKodeTab: kt = item
return item if type(item)==_TTkKodeTab else None
def setDropEventProxy(self, proxy:Callable) -> None:
for widget in self.layout().iterWidgets(onlyVisible=False):
if issubclass(type(widget),_TTkKodeTab):
widget.setDropEventProxy(proxy)
return super().setDropEventProxy(proxy)
@pyTTkSlot(TTkWidget)
def setCurrentWidget(self, *args, **kwargs) -> None:
return self._lastKodeTabWidget.setCurrentWidget(*args, **kwargs)

92
libs/pyTermTk/TermTk/TTkWidgets/tabwidget.py

@ -65,7 +65,6 @@ _tabStyleFocussed = {
}
class _TTkTabWidgetDragData():
__slots__ = ('_tabButton', '_tabWidget')
def __init__(self, b, tw):
self._tabButton = b
@ -73,6 +72,18 @@ class _TTkTabWidgetDragData():
def tabButton(self): return self._tabButton
def tabWidget(self): return self._tabWidget
class _TTkNewTabWidgetDragData():
__slots__ = ('_label', '_widget', '_closable', '_data')
def __init__(self, label, widget:TTkWidget, data=None, closable:bool=False):
self._data = data
self._label = label
self._widget = widget
self._closable = closable
def data(self): return self._data
def label(self): return self._label
def widget(self): return self._widget
def closable(self): return self._closable
class _TTkTabBarDragData():
__slots__ = ('_tabButton','_tabBar')
def __init__(self, b, tb):
@ -666,37 +677,54 @@ class TTkTabWidget(TTkFrame):
def dropEvent(self, evt:TTkDnDEvent) -> bool:
data = evt.data()
x, y = evt.x, evt.y
if not issubclass(type (data),_TTkTabWidgetDragData):
return False
tb = data.tabButton()
tw = data.tabWidget()
index = tw._tabBar._tabButtons.index(tb)
widget = tw.widget(index)
data = tw.tabData(index)
if TTkHelper.isParent(self, tw):
return False
if y < 3:
tbx = self._tabBar.x()
newIndex = 0
for b in self._tabBar._tabButtons:
if tbx+b.x()+b.width()/2 < x:
newIndex += 1
if tw == self:
if index <= newIndex:
newIndex -= 1
tw.removeTab(index)
self.insertTab(newIndex, widget, tb.text(), data, tb._closable)
self.setCurrentIndex(newIndex)
#self._tabChanged(newIndex)
elif tw != self:
tw.removeTab(index)
newIndex = len(self._tabWidgets)
self.addTab(widget, tb.text(), data)
self.setCurrentIndex(newIndex)
self._tabChanged(newIndex)
TTkLog.debug(f"Drop -> pos={evt.pos()}")
return True
if issubclass(type(data),_TTkTabWidgetDragData):
tb = data.tabButton()
tw = data.tabWidget()
index = tw._tabBar._tabButtons.index(tb)
widget = tw.widget(index)
data = tw.tabData(index)
if TTkHelper.isParent(self, tw):
return False
if y < 3:
tbx = self._tabBar.x()
newIndex = 0
for b in self._tabBar._tabButtons:
if tbx+b.x()+b.width()/2 < x:
newIndex += 1
if tw == self:
if index <= newIndex:
newIndex -= 1
tw.removeTab(index)
self.insertTab(newIndex, widget, tb.text(), data, tb._closable)
self.setCurrentIndex(newIndex)
#self._tabChanged(newIndex)
elif tw != self:
tw.removeTab(index)
newIndex = len(self._tabWidgets)
self.addTab(widget, tb.text(), data)
self.setCurrentIndex(newIndex)
self._tabChanged(newIndex)
TTkLog.debug(f"Drop -> pos={evt.pos()}")
return True
elif issubclass(type(data),_TTkNewTabWidgetDragData):
w = data.widget()
d = data.data()
l = data.label()
c = data.closable()
if y < 3:
tbx = self._tabBar.x()
newIndex = 0
for b in self._tabBar._tabButtons:
if tbx+b.x()+b.width()/2 < x:
newIndex += 1
self.insertTab(newIndex, w, l, d, c)
self.setCurrentIndex(newIndex)
else:
self.addTab(w, l, d, c)
self.setCurrentIndex(len(self._tabBar._tabButtons)-1)
TTkLog.debug(f"Drop -> pos={evt.pos()}")
return True
return False
def addMenu(self, text, position=TTkK.LEFT, data=None) -> TTkMenuBarButton:
'''addMenu'''

13
libs/pyTermTk/TermTk/TTkWidgets/widget.py

@ -22,6 +22,8 @@
__all__ = ['TTkWidget']
from typing import Callable, Any, List
try:
from typing import Self
except:
@ -115,6 +117,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
'_enabled',
'_style', '_currentStyle',
'_toolTip',
'_dropEventProxy',
'_widgetCursor', '_widgetCursorEnabled', '_widgetCursorType',
#Signals
'focusChanged', 'sizeChanged', 'currentStyleChanged', 'closed')
@ -194,6 +197,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
self.closed = pyTTkSignal(TTkWidget)
# self.sizeChanged.connect(self.resizeEvent)
self._dropEventProxy = lambda x:x
self._widgetCursor = (0,0)
self._widgetCursorEnabled = False
self._widgetCursorType = TTkK.Cursor_Blinking_Bar
@ -267,6 +271,13 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
'''
self._name = name
def setDropEventProxy(self, proxy:Callable) -> None:
'''
.. warning::
This is an alpha Method to prototype the Drag and Drop prosy feature and may change in the future
'''
self._dropEventProxy = proxy
def widgetItem(self) -> TTkWidgetItem:
return self._widgetItem
@ -444,7 +455,7 @@ class TTkWidget(TMouseEvents,TKeyEvents, TDragEvents):
TTkHelper.dndEnter(self)
return True
if evt.evt == TTkK.Release:
if self.dropEvent(TTkHelper.dndGetDrag().getDropEvent(evt)):
if self.dropEvent(self._dropEventProxy(TTkHelper.dndGetDrag().getDropEvent(evt))):
return True
return ret

135
tests/t.ui/test.ui.011.tree.04.dnd.01.py

@ -0,0 +1,135 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Demo inspired from:
# https://stackoverflow.com/questions/41204234/python-pyqt5-qtreewidget-sub-item
import os
import sys
import argparse
sys.path.append(os.path.join(sys.path[0],'../..'))
import TermTk as ttk
class DropClass(ttk.TTkFrame):
def __init__(self, **kwargs):
super().__init__(**kwargs|{'title':'Drop Here'})
def dropEvent(self, evt):
ttk.TTkLog.debug(f"Dropped: {evt.data()}")
return super().dropEvent(evt)
def dragEnterEvent(self, evt):
ttk.TTkLog.debug(f"Drag Enter: {evt}")
return super().dragEnterEvent(evt)
def dragMoveEvent(self, evt):
ttk.TTkLog.debug(f"Drag Move: {evt}")
return super().dragMoveEvent(evt)
def dragLeaveEvent(self, evt):
ttk.TTkLog.debug(f"Drag Leave: {evt}")
return super().dragLeaveEvent(evt)
def dropEventProxy(evt:ttk.TTkDnDEvent):
data = evt.data()
ttk.TTkLog.debug(f"Proxy: {evt=} {data=}")
if issubclass(type(data), ttk.TTkTreeWidget._DropTreeData) and data.items:
item:ttk.TTkTreeWidgetItem = data.items[0]
newData = ttk.TTkWidgets.tabwidget._TTkNewTabWidgetDragData(
widget=ttk.TTkTestWidgetSizes(),
label=item.data(0),
data=None,
closable=True
)
newEvt = evt.clone()
newEvt.setData(newData)
return newEvt
return evt
ttk.TTkLog.use_default_file_logging()
parser = argparse.ArgumentParser()
parser.add_argument('-f', help='Full Screen', action='store_true')
args = parser.parse_args()
root = ttk.TTk()
DropClass(parent=root, pos=(0,0), size=(30,5))
logsWin = ttk.TTkWindow(parent=root, title='Logs', pos=(31,0), size=(100,30), layout=ttk.TTkGridLayout())
ttk.TTkLogViewer(parent=logsWin)
kodeWin = ttk.TTkWindow(parent=root,pos = (30,5), size=(80,40), title="KodeWin", layout=ttk.TTkGridLayout(), border=True)
kt = ttk.TTkKodeTab(parent=kodeWin)
kt.setDropEventProxy(dropEventProxy)
kt.addTab(ttk.TTkTestWidgetSizes(),"t 1")
kt.addTab(ttk.TTkTestWidgetSizes(),"t 2")
kt.addTab(ttk.TTkTestWidgetSizes(),"t 3")
kt.addTab(ttk.TTkTestWidgetSizes(),"t 4")
kt.addTab(ttk.TTkTestWidgetSizes(),"t 5")
fileTreeWin = ttk.TTkWindow(parent=root,pos = (3,12), size=(40,40), title="Test Tree 1", layout=ttk.TTkGridLayout(), border=True)
ft = ttk.TTkFileTree(parent=fileTreeWin, dragDropMode=ttk.TTkK.DragDropMode.AllowDrag)
treeWin = ttk.TTkWindow(parent=root,pos = (0,6), size=(40,40), title="Test Tree 1", layout=ttk.TTkGridLayout(), border=True)
tw = ttk.TTkTree(parent=treeWin, dragDropMode=ttk.TTkK.DragDropMode.AllowDrag)
tw.setHeaderLabels(["Column 1", "Column 2", "Column 3"])
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)
l1.setExpanded(True)
l3.setExpanded(True)
root.mainloop()
Loading…
Cancel
Save