You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

333 lines
11 KiB

# 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.
__all__ = ['TTkTreeWidgetItem']
from TermTk.TTkCore.cfg import TTkCfg
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.string import TTkString
from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal
from TermTk.TTkWidgets import TTkWidget
from TermTk.TTkAbstract.abstractitemmodel import TTkAbstractItemModel
class TTkTreeWidgetItem(TTkAbstractItemModel):
'''TTkTreeWidgetItem'''
__slots__ = ('_parent', '_data', '_widgets', '_height', '_alignment', '_children', '_expanded', '_selected', '_hidden',
'_childIndicatorPolicy', '_icon', '_defaultIcon',
'_sortColumn', '_sortOrder', '_hasWidgets', '_parentWidget',
# Signals
# 'refreshData'
'heightChanged'
)
def __init__(self, *args, **kwargs):
# Signals
# self.refreshData = pyTTkSignal(TTkTreeWidgetItem)
self.heightChanged = pyTTkSignal(int)
super().__init__(*args, **kwargs)
self._hasWidgets = False
self._children = []
self._parentWidget = None
self._height = 1
data = args[0] if len(args)>0 and type(args[0])==list else [TTkString()]
# self._data = [i if issubclass(type(i), TTkString) else TTkString(i) if isinstance(i,str) else TTkString() for i in data]
self._data, self._widgets = self._processDataInput(data)
self._alignment = [TTkK.LEFT_ALIGN]*len(self._data)
self._parent = kwargs.get('parent', None)
self._childIndicatorPolicy = kwargs.get('childIndicatorPolicy', TTkK.DontShowIndicatorWhenChildless)
self._defaultIcon = True
self._expanded = kwargs.get('expanded', False)
self._selected = kwargs.get('selected', False)
self._hidden = kwargs.get('hidden', False)
self._parent = kwargs.get("parent", None)
self._sortColumn = -1
self._sortOrder = TTkK.AscendingOrder
self._icon = ['']*len(self._data)
self._setDefaultIcon()
if 'icon' in kwargs:
self._icon[0] = kwargs['icon']
self._defaultIcon = False
def _processDataInputWidget(self, widget, index):
self._hasWidgets = True
widget.hide()
widget.sizeChanged.connect(self._widgetSizeChanged)
self._height = max(self._height,widget.height())
if self._parentWidget:
widget.setTreeItemParent(self._parentWidget)
if hasattr(widget, 'text'):
ret = widget.text()
if hasattr(widget,'textChanged'):
def _updateField(index):
def __updateFieldRet(text):
self._data[index] = text
return __updateFieldRet
widget.textChanged.connect(_updateField(index))
else:
ret = TTkString()
return ret
def _processDataInput(self, dataInput):
retData, retWidgets = [],[]
for index, di in enumerate(dataInput):
if issubclass(type(di), TTkString):
retData.append(di)
retWidgets.append(None)
elif isinstance(di,str):
retData.append(TTkString(di))
retWidgets.append(None)
elif issubclass(type(di), TTkWidget):
retData.append(self._processDataInputWidget(di, index))
retWidgets.append(di)
else:
retData.append(TTkString())
retWidgets.append(None)
self._height = max(self._height,len(retData[-1].split('\n')))
return retData, retWidgets
def _setDefaultIcon(self):
if not self._defaultIcon: return
self._icon[0] = TTkCfg.theme.tree[0]
if self._childIndicatorPolicy == TTkK.DontShowIndicatorWhenChildless and self._children or \
self._childIndicatorPolicy == TTkK.ShowIndicator:
if self._expanded:
self._icon[0] = TTkCfg.theme.tree[2]
else:
self._icon[0] = TTkCfg.theme.tree[1]
@pyTTkSlot(int, int)
def _widgetSizeChanged(self, _, h):
if h != self._height:
h = max(max([len(s.split("\n")) for s in self._data]), max(w.height() for w in self._widgets if w))
if h != self._height:
self._height = h
self.heightChanged.emit(h)
if self._parentWidget:
self._parentWidget._refreshCache()
def height(self):
return self._height
def _clearTreeItemParent(self):
widgets = []
if self._hasWidgets:
widgets += [w for w in self._widgets if w and w.parentWidget()]
# for widget in widgets:
# if pw := widget.parentWidget():
# pw.rootLayout().removeWidgets([w for w in self._widgets if w])
if self._parentWidget:
self._parentWidget.rootLayout().removeWidgets(widgets)
self._parentWidget = None
for c in self._children:
widgets += c._clearTreeItemParent()
return widgets
def _setTreeItemParent(self, parent):
self._parentWidget = parent
widgets = []
if self._hasWidgets:
widgets += [w for w in self._widgets if w]
# parent.layout().addWidgets(widgets)
for c in self._children:
widgets += c._setTreeItemParent(parent)
return widgets
def setTreeItemParent(self, parent):
if parent:
widgets = self._setTreeItemParent(parent)
parent.layout().addWidgets(widgets)
else:
# pw = self._parentWidget
widgets = self._clearTreeItemParent()
# pw.rootLayout().removeWidgets(widgets)
def hasWidgets(self):
return self._hasWidgets
def isHidden(self):
return self._hidden
def setHidden(self, hide):
if hide == self._hidden: return
self._hidden = hide
self.dataChanged.emit()
def childIndicatorPolicy(self):
return self._childIndicatorPolicy
def setChildIndicatorPolicy(self, policy):
self._childIndicatorPolicy = policy
self._setDefaultIcon()
def _addChild(self, child):
self._children.append(child)
child._parent = self
child._sortOrder = self._sortOrder
child._sortColumn = self._sortColumn
self._setDefaultIcon()
self._sort(children=False)
if self._parentWidget:
child.setTreeItemParent(self._parentWidget)
child.dataChanged.connect(self.emitDataChanged)
def addChild(self, child):
self._addChild(child)
self.dataChanged.emit()
def addChildren(self, children):
for child in children:
self._addChild(child)
self.dataChanged.emit()
def removeChild(self, child):
if child in self._children:
self.takeChild(self._children.index(child))
def takeChild(self, index):
if not (self._children and 0<= index < len(self._children)):
return None
child = self._children.pop(index)
child.dataChanged.disconnect(self.emitDataChanged)
child.setTreeItemParent(None)
self.dataChanged.emit()
return child
def takeChildren(self):
children = self._children
for child in children:
child.dataChanged.disconnect(self.emitDataChanged)
child.setTreeItemParent(None)
self._children = []
self.dataChanged.emit()
return children
def child(self, index):
if 0 <= index < len(self._children):
return self._children[index]
return None
def children(self):
return [x for x in self._children if not x.isHidden()]
def indexOfChild(self, child):
if child in self._children:
return self._children.index(child)
return None
def icon(self, col):
if col >= len(self._icon):
return ''
return self._icon[col]
def setIcon(self, col, icon):
if col==0:
self._defaultIcon = False
self._icon[col] = icon
self.dataChanged.emit()
def textAlignment(self, col):
if col >= len(self._alignment):
return TTkK.LEFT_ALIGN
return self._alignment[col]
def setTextAlignment(self, col, alignment):
self._alignment[col] = alignment
self.dataChanged.emit()
def data(self, col, role=None):
if col >= len(self._data):
return ''
return self._data[col]
def widget(self, col, role=None):
if col >= len(self._data):
return None
return self._widgets[col]
def sortData(self, col):
return self.data(col)
def _sort(self, children):
if self._sortColumn == -1: return
self._children = sorted(
self._children,
key = lambda x : x.sortData(self._sortColumn),
reverse = self._sortOrder == TTkK.DescendingOrder)
# Broadcast the sorting to the children
if children:
for c in self._children:
c.dataChanged.disconnect(self.emitDataChanged)
c.sortChildren(self._sortColumn, self._sortOrder)
c.dataChanged.connect(self.emitDataChanged)
def sortChildren(self, col, order):
self._sortColumn = col
self._sortOrder = order
if not self._children: return
self._sort(children=True)
self.dataChanged.emit()
@pyTTkSlot()
def emitDataChanged(self):
self.dataChanged.emit()
# def setDisabled(disabled):
# pass
def setExpanded(self, expand):
# hide all the widgets if this item is not expanded
if not expand:
def _recurseHide(item):
for c in item._children:
if c._hasWidgets:
for widget in [w for w in c._widgets if w]:
widget.hide()
if c._expanded:
_recurseHide(c)
_recurseHide(self)
self._expanded = expand
self._setDefaultIcon()
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 self._height + sum(c.size() for c in self.children())
else:
return self._height