Browse Source

Merge pull request #187 from ceccopierangiolieugenio/184-TTkList-DragDrop

DragDrop feature in the list widget
pull/186/head
Ceccopierangiolieugenio 2 years ago committed by GitHub
parent
commit
34242550a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      TermTk/TTkCore/canvas.py
  2. 12
      TermTk/TTkCore/constant.py
  3. 46
      TermTk/TTkUiTools/properties/list_.py
  4. 4
      TermTk/TTkWidgets/kodetab.py
  5. 15
      TermTk/TTkWidgets/list_.py
  6. 178
      TermTk/TTkWidgets/listwidget.py
  7. 78
      demo/showcase/list.py
  8. 25
      tests/test.ui.014.list.03.py
  9. 112
      tests/test.ui.014.list.04.py
  10. 2
      ttkDesigner/app/propertyeditor.py
  11. 4
      ttkDesigner/app/superobj/__init__.py
  12. 5
      ttkDesigner/app/superobj/superwidget.py
  13. 44
      ttkDesigner/app/superobj/superwidgetabstractscrollarea.py
  14. 29
      ttkDesigner/app/superobj/superwidgetlist.py
  15. 3
      ttkDesigner/app/widgetbox.py

1
TermTk/TTkCore/canvas.py

@ -37,7 +37,6 @@ class TTkCanvas:
'''
__slots__ = (
'_width', '_height', '_newWidth', '_newHeight',
'_theme',
'_data', '_colors',
'_bufferedData', '_bufferedColors',
'_visible', '_transparent', '_doubleBuffer')

12
TermTk/TTkCore/constant.py

@ -128,6 +128,18 @@ class TTkConstant:
# InsertAlphabetically = 0x06
# '''The string is inserted in the alphabetic order in the combobox.'''
class DragDropMode(int):
'''Specifies the Drag and Drop mode allowed by this widget'''
NoDragDrop = 0x00
'''No Drag and Drop is allowed'''
AllowDrag = 0x01
'''Drag allowed'''
AllowDrop = 0x02
'''Drop allowed'''
NoDragDrop = DragDropMode.NoDragDrop
AllowDrag = DragDropMode.AllowDrag
AllowDrop = DragDropMode.AllowDrop
class ChildIndicatorPolicy(int):
ShowIndicator = 0x00 #The controls for expanding and collapsing will be shown for this item even if there are no children.
DontShowIndicator = 0x01 #The controls for expanding and collapsing will never be shown even if there are children. If the node is forced open the user will not be able to expand or collapse the item.

46
TermTk/TTkUiTools/properties/list_.py

@ -22,4 +22,48 @@
__all__ = ['TTkListProperties']
TTkListProperties = {'properties' : {},'signals' : {},'slots' : {}}
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkWidgets.list_ import TTkList
from TermTk.TTkWidgets.listwidget import TTkListWidget, TTkAbstractListItem
TTkListProperties = {
'properties' : {
'Selection Mode' : {
'init': {'name':'selectionMode', 'type':'singleflag',
'flags':{
'Single Seelction' : TTkK.SingleSelection,
'Multi Selection' : TTkK.MultiSelection,
}},
'get': {'cb':lambda w: w.selectionMode(), 'type':'singleflag',
'flags':{
'Single Seelction' : TTkK.SingleSelection,
'Multi Selection' : TTkK.MultiSelection,
}},
'set': {'cb':lambda w,v: w.setSelectionMode(v), 'type':'singleflag',
'flags':{
'Single Seelction' : TTkK.SingleSelection,
'Multi Selection' : TTkK.MultiSelection,
}}},
'DnD Mode' : {
'init': {'name':'dragDropMode', 'type':'multiflags',
'flags':{
'Allow Drag' : TTkK.DragDropMode.AllowDrag,
'Allow Drop' : TTkK.DragDropMode.AllowDrop,
}},
'get': {'cb':lambda w: w.dragDropMode(), 'type':'multiflags',
'flags':{
'Allow Drag' : TTkK.DragDropMode.AllowDrag,
'Allow Drop' : TTkK.DragDropMode.AllowDrop,
}},
'set': {'cb':lambda w,v: w.setDragDropMode(v), 'type':'multiflags',
'flags':{
'Allow Drag' : TTkK.DragDropMode.AllowDrag,
'Allow Drop' : TTkK.DragDropMode.AllowDrop,
}}},
},
'signals' : {
'itemClicked(TTkAbstractListItem)' : {'name': 'itemClicked', 'type' : TTkAbstractListItem},
'textClicked(str)' : {'name': 'textClicked', 'type' : str},
},
'slots' : {}}

4
TermTk/TTkWidgets/kodetab.py

@ -79,11 +79,11 @@ class _TTkKodeTab(TTkTabWidget):
kt._tabBarTopLayout.update()
def dragEnterEvent(self, evt) -> bool:
TTkLog.debug(f"leave")
TTkLog.debug(f"Drag Enter")
return True
def dragLeaveEvent(self, evt) -> bool:
TTkLog.debug(f"leave")
TTkLog.debug(f"Drag Leave")
self._frameOverlay = None
self.update()
return True

15
TermTk/TTkWidgets/list_.py

@ -30,9 +30,12 @@ class TTkList(TTkAbstractScrollArea):
__slots__ = (
'_listView', 'itemClicked', 'textClicked',
# Forwarded Methods
'items', 'addItem', 'addItemAt', 'indexOf', 'itemAt',
'moveItem', 'removeAt', 'removeItem',
'setSelectionMode', 'selectedItems', 'selectedLabels',
'items',
'dragDropMode', 'setDragDropMode',
'addItem', 'addItemAt', 'addItems', 'addItemsAt',
'indexOf', 'itemAt', 'moveItem',
'removeAt', 'removeItem', 'removeItems',
'selectionMode', 'setSelectionMode', 'selectedItems', 'selectedLabels',
'setCurrentRow', 'setCurrentItem', )
def __init__(self, *args, **kwargs):
@ -51,11 +54,17 @@ class TTkList(TTkAbstractScrollArea):
self.moveItem = self._listView.moveItem
self.removeAt = self._listView.removeAt
self.removeItem = self._listView.removeItem
self.removeItems = self._listView.removeItems
self.addItem = self._listView.addItem
self.addItems = self._listView.addItems
self.addItemAt = self._listView.addItemAt
self.addItemsAt = self._listView.addItemsAt
self.selectionMode = self._listView.selectionMode
self.setSelectionMode = self._listView.setSelectionMode
self.selectedItems = self._listView.selectedItems
self.selectedLabels = self._listView.selectedLabels
self.setCurrentRow = self._listView.setCurrentRow
self.setCurrentItem = self._listView.setCurrentItem
self.dragDropMode = self._listView.dragDropMode
self.setDragDropMode = self._listView.setDragDropMode

178
TermTk/TTkWidgets/listwidget.py

@ -22,14 +22,17 @@
__all__ = ['TTkAbstractListItem', 'TTkListWidget']
from dataclasses import dataclass
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.TTkCore.canvas import TTkCanvas
from TermTk.TTkCore.string import TTkString
from TermTk.TTkGui.drag import TTkDrag
from TermTk.TTkWidgets.widget import TTkWidget
from TermTk.TTkWidgets.label import TTkLabel
from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView
class TTkAbstractListItem(TTkWidget):
@ -37,9 +40,9 @@ class TTkAbstractListItem(TTkWidget):
classStyle = TTkWidget.classStyle | {
'default': {'color': TTkColor.RST},
'highlighted': {'color': TTkColor.fg('#00FF00')+TTkColor.bg('#0055FF')+TTkColor.UNDERLINE},
'hover': {'color': TTkColor.fg('#00FF00')+TTkColor.bg('#0088FF')},
'selected': {'color': TTkColor.fg('#00FF00')+TTkColor.bg('#0055FF')},
'highlighted': {'color': TTkColor.bg('#008855')+TTkColor.UNDERLINE},
'hover': {'color': TTkColor.bg('#0088FF')},
'selected': {'color': TTkColor.bg('#0055FF')},
'clicked': {'color': TTkColor.fg('#FFFF00')},
'disabled': {'color': TTkColor.fg('#888888')},
}
@ -92,29 +95,40 @@ class TTkAbstractListItem(TTkWidget):
self.update()
def paintEvent(self, canvas):
style = self.currentStyle()
if style == self.classStyle['hover']:
pass
elif self._highlighted:
style = self.style()['highlighted']
elif self._selected:
style = self.style()['selected']
color = (style:=self.currentStyle())['color']
if self._highlighted:
color = color+self.style()['highlighted']['color']
if self._selected:
color = color+self.style()['selected']['color']
if style==self.style()['hover']:
color = color+self.style()['hover']['color']
w = self.width()
canvas.drawTTkString(pos=(0,0), width=w, color=style['color'] ,text=self._text)
canvas.drawTTkString(pos=(0,0), width=w, color=color ,text=self._text)
class TTkListWidget(TTkAbstractScrollView):
@dataclass(frozen=True)
class _DropListData:
widget: TTkAbstractScrollView
items: list
'''TTkListWidget'''
__slots__ = ('itemClicked', 'textClicked', '_selectedItems', '_selectionMode', '_highlighted', '_items')
__slots__ = ('itemClicked', 'textClicked',
'_selectedItems', '_selectionMode',
'_highlighted', '_items',
'_dragPos', '_dndMode')
def __init__(self, *args, **kwargs):
# Default Class Specific Values
self._selectionMode = kwargs.get("selectionMode", TTkK.SingleSelection)
self._selectedItems = []
self._items = []
self._highlighted = None
self._dragPos = None
self._dndMode = kwargs.get("dragDropMode",
TTkK.DragDropMode.AllowDrag | TTkK.DragDropMode.AllowDrop )
# Signals
self.itemClicked = pyTTkSignal(TTkWidget)
self.itemClicked = pyTTkSignal(TTkAbstractListItem)
self.textClicked = pyTTkSignal(str)
# Init Super
TTkAbstractScrollView.__init__(self, *args, **kwargs)
@ -149,6 +163,18 @@ class TTkListWidget(TTkAbstractScrollView):
self.itemClicked.emit(label)
self.textClicked.emit(label.text())
def dragDropMode(self):
'''dragDropMode'''
return self._dndMode
def setDragDropMode(self, dndMode):
'''setDragDropMode'''
self._dndMode = dndMode
def selectionMode(self):
'''selectionMode'''
return self._selectionMode
def setSelectionMode(self, mode):
'''setSelectionMode'''
self._selectionMode = mode
@ -186,6 +212,10 @@ class TTkListWidget(TTkAbstractScrollView):
'''addItem'''
self.addItemAt(item, len(self._items), data)
def addItems(self, items):
'''addItems'''
self.addItemAt(items, len(self._items))
def _placeItems(self):
minw = self.width()
for item in self._items:
@ -193,16 +223,24 @@ class TTkListWidget(TTkAbstractScrollView):
for y,item in enumerate(self._items):
item.setGeometry(0,y,minw,1)
self.viewChanged.emit()
self.update()
def addItemAt(self, item, pos, data=None):
'''addItemAt'''
if isinstance(item, str) or isinstance(item, TTkString):
#label = TTkAbstractListItem(text=item, width=max(len(item),self.width()))
label = TTkAbstractListItem(text=item, data=data)
return self.addItemAt(label,pos)
item.listItemClicked.connect(self._labelSelectedHandler)
self._items.insert(pos,item)
self.layout().addWidget(item)
item = TTkAbstractListItem(text=item, data=data)
return self.addItemsAt([item],pos)
def addItemsAt(self, items, pos):
'''addItemsAt'''
for item in items:
if not issubclass(type(item),TTkAbstractListItem):
TTkLog.error(f"{item=} is not an TTkAbstractListItem")
return
for item in items:
item.listItemClicked.connect(self._labelSelectedHandler)
self._items[pos:pos] = items
self.layout().addWidgets(items)
self._placeItems()
def indexOf(self, item):
@ -226,15 +264,20 @@ class TTkListWidget(TTkAbstractScrollView):
def removeItem(self, item):
'''removeItem'''
item.listItemClicked.disconnect(self._labelSelectedHandler)
item._setSelected(False)
item._setHighlighted(False)
self.layout().removeWidget(item)
self._items.remove(item)
if item in self._selectedItems:
self._selectedItems.remove(item)
if item == self._highlighted:
self._highlighted = None
self.removeItems([item])
def removeItems(self, items):
'''removeItems'''
self.layout().removeWidgets(items)
for item in items.copy():
item.listItemClicked.disconnect(self._labelSelectedHandler)
item._setSelected(False)
item._setHighlighted(False)
self._items.remove(item)
if item in self._selectedItems:
self._selectedItems.remove(item)
if item == self._highlighted:
self._highlighted = None
self._placeItems()
def removeAt(self, pos):
@ -261,6 +304,67 @@ class TTkListWidget(TTkAbstractScrollView):
elif index <= offy:
self.viewMoveTo(offx, index)
def mouseDragEvent(self, evt) -> bool:
if not(self._dndMode & TTkK.DragDropMode.AllowDrag):
return False
if not (items:=self._selectedItems.copy()):
return True
drag = TTkDrag()
data =TTkListWidget._DropListData(widget=self,items=items)
h = min(3,ih:=len(items)) + 2 + (1 if ih>3 else 0)
w = min(20,iw:=max([it.text().termWidth() for it in items[:3]])) + 2
pm = TTkCanvas(width=w,height=h)
for y,it in enumerate(items[:3],1):
txt = it.text()
if txt.termWidth() < 20:
pm.drawText(pos=(1,y), text=it.text())
else:
pm.drawText(pos=(1,y), text=it.text(), width=17)
pm.drawText(pos=(18,y), text='...')
if ih>3:
pm.drawText(pos=(1,4), text='...')
pm.drawBox(pos=(0,0),size=(w,h))
drag.setPixmap(pm)
drag.setData(data)
drag.exec()
return True
def dragEnterEvent(self, evt):
if not(self._dndMode & TTkK.DragDropMode.AllowDrop):
return False
if issubclass(type(evt.data()),TTkListWidget._DropListData):
return self.dragMoveEvent(evt)
return False
def dragMoveEvent(self, evt):
offx,offy = self.getViewOffsets()
y=min(evt.y+offy,len(self._items))
self._dragPos = (offx+evt.x, y)
self.update()
return True
def dragLeaveEvent(self, evt):
self._dragPos = None
self.update()
return True
def dropEvent(self, evt) -> bool:
if not(self._dndMode & TTkK.DragDropMode.AllowDrop):
return False
self._dragPos = None
if not issubclass(type(evt.data()) ,TTkListWidget._DropListData):
return False
offx,offy = self.getViewOffsets()
wid = evt.data().widget
items = evt.data().items
if wid and items:
wid.removeItems(items)
for it in items:
it.setCurrentStyle(it.style()['default'])
self.addItemsAt(items,offy+evt.y)
return True
return False
def keyEvent(self, evt):
if not self._highlighted: return False
if ( evt.type == TTkK.Character and evt.key==" " ) or \
@ -309,3 +413,19 @@ class TTkListWidget(TTkAbstractScrollView):
def focusOutEvent(self):
if self._highlighted:
self._highlighted._setHighlighted(False)
self._dragPos = None
# Stupid hack to paint on top of the child widgets
def paintChildCanvas(self):
super().paintChildCanvas()
if self._dragPos:
canvas = self.getCanvas()
x,y = self._dragPos
offx,offy = self.getViewOffsets()
p1 = (0,y-offy-1)
p2 = (0,y-offy)
canvas.drawText(pos=p1,text="╙─╼", color=TTkColor.fg("#FFFF00")+TTkColor.bg("#008855"))
canvas.drawText(pos=p2,text="╓─╼", color=TTkColor.fg("#FFFF00")+TTkColor.bg("#008855"))

78
demo/showcase/list.py

@ -33,23 +33,43 @@ from showcase._showcasehelper import getUtfWord
def demoList(root= None):
# Define the main Layout
splitter = ttk.TTkSplitter(parent=root, orientation=ttk.TTkK.HORIZONTAL)
frame2 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout())
frame1 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout())
frame3 = ttk.TTkFrame(parent=splitter, border=0, layout=ttk.TTkVBoxLayout())
retFrame = ttk.TTkFrame(parent=root, border=False, layout=(rootLayout:=ttk.TTkGridLayout()))
# Multi Selection List
ttk.TTkLabel(parent=frame1, text="[ MultiSelect ]",maxHeight=2)
listWidgetMulti = ttk.TTkList(parent=frame1, maxWidth=40, minWidth=10, selectionMode=ttk.TTkK.MultiSelection)
# Define the main Layout
win1 = ttk.TTkWindow(title="Single List", layout=ttk.TTkVBoxLayout())
win2 = ttk.TTkWindow(title="Multi List", layout=ttk.TTkVBoxLayout())
win3 = ttk.TTkWindow(title="Log", layout=ttk.TTkVBoxLayout())
win4 = ttk.TTkWindow(title="Oly Drag Allowed", layout=ttk.TTkVBoxLayout())
win5 = ttk.TTkWindow(title="Oly Drop Allowed", layout=ttk.TTkVBoxLayout())
layout1 = ttk.TTkLayout()
# Place the widgets in the root layout
rootLayout.addWidget(win1,0,0)
rootLayout.addWidget(win2,0,1)
rootLayout.addWidget(win3,0,2,1,3)
rootLayout.addItem(layout1,1,0,1,3)
rootLayout.addWidget(win4,1,3)
rootLayout.addWidget(win5,1,4)
# Single Selection List
ttk.TTkLabel(parent=frame2, text="[ SingleSelect ]",maxHeight=2)
listWidgetSingle = ttk.TTkList(parent=frame2, maxWidth=40, minWidth=10)
listWidgetSingle = ttk.TTkList(parent=win1, maxWidth=40, minWidth=10)
# Multi Selection List
listWidgetMulti = ttk.TTkList(parent=win2, maxWidth=40, minWidth=10, selectionMode=ttk.TTkK.MultiSelection)
# Multi Selection List - Drag Allowed
listWidgetDrag = ttk.TTkList(parent=win4, maxWidth=40, minWidth=10, dragDropMode=ttk.TTkK.DragDropMode.AllowDrag)
listWidgetDrop = ttk.TTkList(parent=win5, maxWidth=40, minWidth=10, dragDropMode=ttk.TTkK.DragDropMode.AllowDrop)
# Log Viewer
label1 = ttk.TTkLabel(parent=frame3, text="[ list1 ]",maxHeight=2)
label2 = ttk.TTkLabel(parent=frame3, text="[ list2 ]",maxHeight=2)
ttk.TTkLogViewer(parent=frame3)#, border=True)
label1 = ttk.TTkLabel(pos=(10,0), text="[ list1 ]",maxHeight=2)
label2 = ttk.TTkLabel(pos=(10,1), text="[ list2 ]",maxHeight=2)
ttk.TTkLogViewer(parent=win3)
btn_mv1 = ttk.TTkButton(pos=(0,0), text=" >> ")
btn_mv2 = ttk.TTkButton(pos=(0,1), text=" << ")
btn_del = ttk.TTkButton(pos=(0,2), text="Delete")
layout1.addWidgets([label1,label2,btn_mv1,btn_mv2,btn_del])
@ttk.pyTTkSlot(str)
def _listCallback1(label):
@ -61,16 +81,42 @@ def demoList(root= None):
ttk.TTkLog.info(f'Clicked label2: "{label}" - selected: {[str(s) for s in listWidgetMulti.selectedLabels()]}')
label2.setText(f'[ list2 ] clicked "{label}" - {[str(s) for s in listWidgetMulti.selectedLabels()]}')
@ttk.pyTTkSlot()
def _moveToRight2():
for i in listWidgetSingle.selectedItems().copy():
listWidgetSingle.removeItem(i)
listWidgetMulti.addItemAt(i,0)
@ttk.pyTTkSlot()
def _moveToLeft1():
for i in listWidgetMulti.selectedItems().copy():
listWidgetMulti.removeItem(i)
listWidgetSingle.addItemAt(i,0)
@ttk.pyTTkSlot()
def _delSelected():
items = listWidgetMulti.selectedItems()
listWidgetMulti.removeItems(items)
items = listWidgetSingle.selectedItems()
listWidgetSingle.removeItems(items)
btn_mv1.clicked.connect(_moveToRight2)
btn_mv2.clicked.connect(_moveToLeft1)
btn_del.clicked.connect(_delSelected)
# Connect the signals to the 2 slots defines
listWidgetSingle.textClicked.connect(_listCallback1)
listWidgetMulti.textClicked.connect(_listCallback2)
# populate the lists with random entries
for i in range(100):
listWidgetSingle.addItem(f"{i}) {getUtfWord()} {getUtfWord()}")
listWidgetMulti.addItem(f"{getUtfWord()} {getUtfWord()}")
for i in range(50):
listWidgetSingle.addItem(f"S-{i}) {getUtfWord()} {getUtfWord()}")
listWidgetMulti.addItem( f"M-{i}){getUtfWord()} {getUtfWord()}")
listWidgetDrag.addItem( f"D-{i}){getUtfWord()} {getUtfWord()}")
return splitter
return retFrame
def main():
parser = argparse.ArgumentParser()

25
tests/test.ui.014.list.03.py

@ -2,7 +2,7 @@
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
# Copyright (c) 2023 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
@ -27,9 +27,16 @@ import sys, os, argparse, math, random
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."]
zc1 = chr(0x07a6) # Zero width chars oަ
zc2 = chr(0x20D7) # Zero width chars o
zc3 = chr(0x065f) # Zero width chars oٟ
utfwords = [
f"--Zero{zc1}{zc2}{zc3}-1-", f"--Zero-2{zc1}{zc2}{zc3}-", f"--Ze{zc1}{zc2}{zc3}ro-3-", f"{zc1}{zc2}{zc3}--Zero-4-",
"d😮l😱r", "sit", "am😎t,", "c😱nsectetur", "t😜mpor", "inci😜di😜dunt", "u😜t", "l😜abore", "et", "d😜olore", "m😜a😜gna", "ali😜qua😜.", "Ut", "enim", "😜a😜d😜", "minim", "veniam,", "😜q😜uis", "😜nostrud", "exer😜c😜i😜tation", "ullamco", "labo😜ris", "n😜isi", "ut", "aliq😞ip", "e😜x😜", "ea", "comm😞do", "cons😿quat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "cul🙻a", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."]
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)
return random.choice(utfwords)
# return random.choice(words)
parser = argparse.ArgumentParser()
parser.add_argument('-t', help='Track Mouse', action='store_true')
@ -82,10 +89,10 @@ def _moveToLeft1():
@ttk.pyTTkSlot()
def _delSelected():
for i in listWidgetMulti.selectedItems().copy():
listWidgetMulti.removeItem(i)
for i in listWidgetSingle.selectedItems().copy():
listWidgetSingle.removeItem(i)
items = listWidgetMulti.selectedItems()
listWidgetMulti.removeItems(items)
items = listWidgetSingle.selectedItems()
listWidgetSingle.removeItems(items)
btn_mv1.clicked.connect(_moveToRight2)
@ -99,7 +106,7 @@ listWidgetMulti.textClicked.connect(_listCallback2)
# populate the lists with random entries
for i in range(10):
listWidgetSingle.addItem(f"{i}) {getWord()} {getWord()}")
listWidgetMulti.addItem(f"{getWord()} {getWord()}")
listWidgetSingle.addItem(f"S-{i}) {getWord()} {getWord()}")
listWidgetMulti.addItem(f"M-{i}) {getWord()} {getWord()}")
root.mainloop()

112
tests/test.ui.014.list.04.py

@ -0,0 +1,112 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 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.
import sys, os, argparse, math, random
sys.path.append(os.path.join(sys.path[0],'..'))
import TermTk as ttk
zc1 = chr(0x07a6) # Zero width chars oަ
zc2 = chr(0x20D7) # Zero width chars o
zc3 = chr(0x065f) # Zero width chars oٟ
utfwords = [
f"--Zero{zc1}{zc2}{zc3}-1-", f"--Zero-2{zc1}{zc2}{zc3}-", f"--Ze{zc1}{zc2}{zc3}ro-3-", f"{zc1}{zc2}{zc3}--Zero-4-",
"d😮l😱r", "sit", "am😎t,", "c😱nsectetur", "t😜mpor", "inci😜di😜dunt", "u😜t", "l😜abore", "et", "d😜olore", "m😜a😜gna", "ali😜qua😜.", "Ut", "enim", "😜a😜d😜", "minim", "veniam,", "😜q😜uis", "😜nostrud", "exer😜c😜i😜tation", "ullamco", "labo😜ris", "n😜isi", "ut", "aliq😞ip", "e😜x😜", "ea", "comm😞do", "cons😿quat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "cul🙻a", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."]
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(utfwords)
# return random.choice(words)
parser = argparse.ArgumentParser()
parser.add_argument('-t', help='Track Mouse', action='store_true')
args = parser.parse_args()
mouseTrack = args.t
root = ttk.TTk(title="pyTermTk List Demo", mouseTrack=mouseTrack)
# Define the main Layout
frame1 = ttk.TTkWindow(parent=root, pos=( 0, 0), size=(30,30), title="Single List", border=0, layout=ttk.TTkVBoxLayout())
frame2 = ttk.TTkWindow(parent=root, pos=(30, 0), size=(30,30), title="Multi List", border=0, layout=ttk.TTkVBoxLayout())
frame3 = ttk.TTkWindow(parent=root, pos=(60, 0), size=(80,30), title="Log", border=0, layout=ttk.TTkVBoxLayout())
# Single Selection List
listWidgetSingle = ttk.TTkList(parent=frame1, maxWidth=40, minWidth=10)
# Multi Selection List
listWidgetMulti = ttk.TTkList(parent=frame2, maxWidth=40, minWidth=10, selectionMode=ttk.TTkK.MultiSelection)
# Log Viewer
label1 = ttk.TTkLabel(parent=root, pos=(10,30), text="[ list1 ]",maxHeight=2)
label2 = ttk.TTkLabel(parent=root, pos=(10,31), text="[ list2 ]",maxHeight=2)
ttk.TTkLogViewer(parent=frame3)#, border=True)
btn_mv1 = ttk.TTkButton(parent=root, pos=(0,30), text=" >> ")
btn_mv2 = ttk.TTkButton(parent=root, pos=(0,31), text=" << ")
btn_del = ttk.TTkButton(parent=root, pos=(0,32), text="Delete")
@ttk.pyTTkSlot(str)
def _listCallback1(label):
ttk.TTkLog.info(f'Clicked label1: "{label}"')
label1.setText(f'[ list1 ] clicked "{label}" - Selected: {[str(s) for s in listWidgetSingle.selectedLabels()]}')
@ttk.pyTTkSlot(str)
def _listCallback2(label):
ttk.TTkLog.info(f'Clicked label2: "{label}" - selected: {[str(s) for s in listWidgetMulti.selectedLabels()]}')
label2.setText(f'[ list2 ] clicked "{label}" - {[str(s) for s in listWidgetMulti.selectedLabels()]}')
@ttk.pyTTkSlot()
def _moveToRight2():
for i in listWidgetSingle.selectedItems().copy():
listWidgetSingle.removeItem(i)
listWidgetMulti.addItemAt(i,0)
@ttk.pyTTkSlot()
def _moveToLeft1():
for i in listWidgetMulti.selectedItems().copy():
listWidgetMulti.removeItem(i)
listWidgetSingle.addItemAt(i,0)
@ttk.pyTTkSlot()
def _delSelected():
items = listWidgetMulti.selectedItems()
listWidgetMulti.removeItems(items)
items = listWidgetSingle.selectedItems()
listWidgetSingle.removeItems(items)
btn_mv1.clicked.connect(_moveToRight2)
btn_mv2.clicked.connect(_moveToLeft1)
btn_del.clicked.connect(_delSelected)
# Connect the signals to the 2 slots defines
listWidgetSingle.textClicked.connect(_listCallback1)
listWidgetMulti.textClicked.connect(_listCallback2)
# populate the lists with random entries
for i in range(10):
listWidgetSingle.addItem(f"S-{i}) {getWord()} {getWord()}")
listWidgetMulti.addItem(f"M-{i}) {getWord()} {getWord()}")
root.mainloop()

2
ttkDesigner/app/propertyeditor.py

@ -183,7 +183,7 @@ class PropertyEditor(ttk.TTkGridLayout):
# Color Fields
def _processTTkColor(name, prop):
getval = prop['get']['cb'](domw)
value = ttk.TTkWidget(layout=ttk.TTkHBoxLayout(), height=1)
value = ttk.TTkContainer(layout=ttk.TTkHBoxLayout(), height=1)
value.layout().addWidget(_cb := ttk.TTkColorButtonPicker(color=getval, height=1))
value.layout().addWidget(_rc := ttk.TTkButton(text=ttk.TTkString('x',ttk.TTkColor.fg('#FFAA00')),maxWidth=3))
_cb.colorSelected.connect(_bound(prop['set']['cb'],domw,lambda v:v))

4
ttkDesigner/app/superobj/__init__.py

@ -25,10 +25,12 @@ from .supercontrol import SuperControlWidget
from .superwidget import SuperWidget
from .superwidgetcontainer import SuperWidgetContainer
from .superwidgettextedit import SuperWidgetTextEdit
from .superwidgetabstractscrollarea import SuperWidgetAbstractScrollArea
# from .superwidgettextedit import SuperWidgetTextEdit
from .superwidgetradiobutton import SuperWidgetRadioButton
from .superwidgetframe import SuperWidgetFrame
from .superwidgetsplitter import SuperWidgetSplitter
# from .superwidgetlist import SuperWidgetList
from .superwidgetmenubutton import SuperWidgetMenuButton
from .superlayout import SuperLayout

5
ttkDesigner/app/superobj/superwidget.py

@ -99,14 +99,17 @@ class SuperWidget(ttk.TTkContainer):
def swFromWidget(wid:object, *args, **kwargs):
swClass = so.SuperWidget
for c, sc in {
ttk.TTkTextEdit: so.SuperWidgetTextEdit,
# ttk.TTkTextEdit: so.SuperWidgetTextEdit,
ttk.TTkRadioButton: so.SuperWidgetRadioButton,
# ttk.TTkResizableFrame: so.SuperWidgetFrame,
# ttk.TTkWindow: so.SuperWidgetFrame,
ttk.TTkSplitter: so.SuperWidgetSplitter,
# ttk.TTkList: so.SuperWidgetList,
ttk.TTkMenuButton: so.SuperWidgetMenuButton,
ttk.TTkFrame: so.SuperWidgetFrame,
ttk.TTkAbstractScrollArea: so.SuperWidgetAbstractScrollArea,
ttk.TTkContainer: so.SuperWidgetContainer,
ttk.TTkWidget: so.SuperWidget,
}.items():
if c in type(wid).mro():
swClass = sc

44
ttkDesigner/app/superobj/superwidgetabstractscrollarea.py

@ -0,0 +1,44 @@
# MIT License
#
# Copyright (c) 2023 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.
import TermTk as ttk
import ttkDesigner.app.superobj as so
from .superobj import SuperObject
class SuperWidgetAbstractScrollArea(so.SuperWidgetContainer):
@staticmethod
def _swFromWidget(wid, swClass, *args, **kwargs):
return swClass(wid=wid, *args, **kwargs)
def getSuperProperties(self):
additions, exceptions, exclude = super().getSuperProperties()
exclude += ['Layout','Padding']
return additions, exceptions, exclude
def dumpDict(self):
wid = self._wid
ret = {
'class' : wid.__class__.__name__,
'params' : SuperObject.dumpParams(wid,exclude=['Layout','Padding']),
}
return ret

29
ttkDesigner/app/superobj/superwidgetlist.py

@ -0,0 +1,29 @@
# MIT License
#
# Copyright (c) 2023 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.
import TermTk as ttk
import ttkDesigner.app.superobj as so
from .superobj import SuperObject
class SuperWidgetList(so.SuperWidgetAbstractScrollArea):
pass

3
ttkDesigner/app/widgetbox.py

@ -56,7 +56,8 @@ dWidgets = {
},
'Widgets':{
"Label" : { "class":ttk.TTkLabel, "params":{'size':(20,1), 'text':'Label'}},
"List" : { "class":ttk.TTkListWidget, "params":{'size':(20,1)}, "disabled": True},
"List" : { "class":ttk.TTkList, "params":{'size':(20,5)}},
# "List Widget" : { "class":ttk.TTkListWidget, "params":{'size':(20,5)}},
"Scroll Area" : { "class":ttk.TTkScrollArea, "params":{'size':(20,5)}, "disabled": True},
"Spacer" : { "class":ttk.TTkSpacer, "params":{'size':(10,5)}},
"Tab Widget" : { "class":ttk.TTkTabWidget, "params":{'size':(20,3)}, "disabled": True},

Loading…
Cancel
Save