initial fluent-widgets ui
This commit is contained in:
695
qfluentwidgets/components/navigation/navigation_widget.py
Normal file
695
qfluentwidgets/components/navigation/navigation_widget.py
Normal file
@ -0,0 +1,695 @@
|
||||
# coding:utf-8
|
||||
from typing import Union, List
|
||||
|
||||
from PySide6.QtCore import (Qt, Signal, QRect, QRectF, QPropertyAnimation, Property, QMargins,
|
||||
QEasingCurve, QPoint, QEvent)
|
||||
from PySide6.QtGui import QColor, QPainter, QPen, QIcon, QCursor, QFont, QBrush, QPixmap, QImage
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout
|
||||
from collections import deque
|
||||
|
||||
from ...common.config import isDarkTheme
|
||||
from ...common.style_sheet import themeColor
|
||||
from ...common.icon import drawIcon, toQIcon
|
||||
from ...common.icon import FluentIcon as FIF
|
||||
from ...common.color import autoFallbackThemeColor
|
||||
from ...common.font import setFont
|
||||
from ..widgets.scroll_area import ScrollArea
|
||||
from ..widgets.label import AvatarWidget
|
||||
from ..widgets.info_badge import InfoBadgeManager, InfoBadgePosition
|
||||
|
||||
|
||||
class NavigationWidget(QWidget):
|
||||
""" Navigation widget """
|
||||
|
||||
clicked = Signal(bool) # whether triggered by the user
|
||||
selectedChanged = Signal(bool)
|
||||
EXPAND_WIDTH = 312
|
||||
|
||||
def __init__(self, isSelectable: bool, parent=None):
|
||||
super().__init__(parent)
|
||||
self.isCompacted = True
|
||||
self.isSelected = False
|
||||
self.isPressed = False
|
||||
self.isEnter = False
|
||||
self.isSelectable = isSelectable
|
||||
self.treeParent = None
|
||||
self.nodeDepth = 0
|
||||
|
||||
# text color
|
||||
self.lightTextColor = QColor(0, 0, 0)
|
||||
self.darkTextColor = QColor(255, 255, 255)
|
||||
|
||||
self.setFixedSize(40, 36)
|
||||
|
||||
def enterEvent(self, e):
|
||||
self.isEnter = True
|
||||
self.update()
|
||||
|
||||
def leaveEvent(self, e):
|
||||
self.isEnter = False
|
||||
self.isPressed = False
|
||||
self.update()
|
||||
|
||||
def mousePressEvent(self, e):
|
||||
super().mousePressEvent(e)
|
||||
self.isPressed = True
|
||||
self.update()
|
||||
|
||||
def mouseReleaseEvent(self, e):
|
||||
super().mouseReleaseEvent(e)
|
||||
self.isPressed = False
|
||||
self.update()
|
||||
self.clicked.emit(True)
|
||||
|
||||
def click(self):
|
||||
self.clicked.emit(True)
|
||||
|
||||
def setCompacted(self, isCompacted: bool):
|
||||
""" set whether the widget is compacted """
|
||||
if isCompacted == self.isCompacted:
|
||||
return
|
||||
|
||||
self.isCompacted = isCompacted
|
||||
if isCompacted:
|
||||
self.setFixedSize(40, 36)
|
||||
else:
|
||||
self.setFixedSize(self.EXPAND_WIDTH, 36)
|
||||
|
||||
self.update()
|
||||
|
||||
def setSelected(self, isSelected: bool):
|
||||
""" set whether the button is selected
|
||||
|
||||
Parameters
|
||||
----------
|
||||
isSelected: bool
|
||||
whether the button is selected
|
||||
"""
|
||||
if not self.isSelectable:
|
||||
return
|
||||
|
||||
self.isSelected = isSelected
|
||||
self.update()
|
||||
self.selectedChanged.emit(isSelected)
|
||||
|
||||
def textColor(self):
|
||||
return self.darkTextColor if isDarkTheme() else self.lightTextColor
|
||||
|
||||
def setLightTextColor(self, color):
|
||||
""" set the text color in light theme mode """
|
||||
self.lightTextColor = QColor(color)
|
||||
self.update()
|
||||
|
||||
def setDarkTextColor(self, color):
|
||||
""" set the text color in dark theme mode """
|
||||
self.darkTextColor = QColor(color)
|
||||
self.update()
|
||||
|
||||
def setTextColor(self, light, dark):
|
||||
""" set the text color in light/dark theme mode """
|
||||
self.setLightTextColor(light)
|
||||
self.setDarkTextColor(dark)
|
||||
|
||||
|
||||
class NavigationPushButton(NavigationWidget):
|
||||
""" Navigation push button """
|
||||
|
||||
def __init__(self, icon: Union[str, QIcon, FIF], text: str, isSelectable: bool, parent=None):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
icon: str | QIcon | FluentIconBase
|
||||
the icon to be drawn
|
||||
|
||||
text: str
|
||||
the text of button
|
||||
"""
|
||||
super().__init__(isSelectable=isSelectable, parent=parent)
|
||||
|
||||
self._icon = icon
|
||||
self._text = text
|
||||
self.lightIndicatorColor = QColor()
|
||||
self.darkIndicatorColor = QColor()
|
||||
|
||||
setFont(self)
|
||||
|
||||
def text(self):
|
||||
return self._text
|
||||
|
||||
def setText(self, text: str):
|
||||
self._text = text
|
||||
self.update()
|
||||
|
||||
def icon(self):
|
||||
return toQIcon(self._icon)
|
||||
|
||||
def setIcon(self, icon: Union[str, QIcon, FIF]):
|
||||
self._icon = icon
|
||||
self.update()
|
||||
|
||||
def _margins(self):
|
||||
return QMargins(0, 0, 0, 0)
|
||||
|
||||
def _canDrawIndicator(self):
|
||||
return self.isSelected
|
||||
|
||||
def setIndicatorColor(self, light, dark):
|
||||
self.lightIndicatorColor = QColor(light)
|
||||
self.darkIndicatorColor = QColor(dark)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHints(QPainter.Antialiasing |
|
||||
QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform)
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
if self.isPressed:
|
||||
painter.setOpacity(0.7)
|
||||
if not self.isEnabled():
|
||||
painter.setOpacity(0.4)
|
||||
|
||||
# draw background
|
||||
c = 255 if isDarkTheme() else 0
|
||||
m = self._margins()
|
||||
pl, pr = m.left(), m.right()
|
||||
globalRect = QRect(self.mapToGlobal(QPoint()), self.size())
|
||||
|
||||
if self._canDrawIndicator():
|
||||
painter.setBrush(QColor(c, c, c, 6 if self.isEnter else 10))
|
||||
painter.drawRoundedRect(self.rect(), 5, 5)
|
||||
|
||||
# draw indicator
|
||||
painter.setBrush(autoFallbackThemeColor(self.lightIndicatorColor, self.darkIndicatorColor))
|
||||
painter.drawRoundedRect(pl, 10, 3, 16, 1.5, 1.5)
|
||||
elif self.isEnter and self.isEnabled() and globalRect.contains(QCursor.pos()):
|
||||
painter.setBrush(QColor(c, c, c, 10))
|
||||
painter.drawRoundedRect(self.rect(), 5, 5)
|
||||
|
||||
drawIcon(self._icon, painter, QRectF(11.5+pl, 10, 16, 16))
|
||||
|
||||
# draw text
|
||||
if self.isCompacted:
|
||||
return
|
||||
|
||||
painter.setFont(self.font())
|
||||
painter.setPen(self.textColor())
|
||||
|
||||
left = 44 + pl if not self.icon().isNull() else pl + 16
|
||||
painter.drawText(QRectF(left, 0, self.width()-13-left-pr, self.height()), Qt.AlignVCenter, self.text())
|
||||
|
||||
|
||||
class NavigationToolButton(NavigationPushButton):
|
||||
""" Navigation tool button """
|
||||
|
||||
def __init__(self, icon: Union[str, QIcon, FIF], parent=None):
|
||||
super().__init__(icon, '', False, parent)
|
||||
|
||||
def setCompacted(self, isCompacted: bool):
|
||||
self.setFixedSize(40, 36)
|
||||
|
||||
|
||||
class NavigationSeparator(NavigationWidget):
|
||||
""" Navigation Separator """
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(False, parent=parent)
|
||||
self.setCompacted(True)
|
||||
|
||||
def setCompacted(self, isCompacted: bool):
|
||||
if isCompacted:
|
||||
self.setFixedSize(48, 3)
|
||||
else:
|
||||
self.setFixedSize(self.EXPAND_WIDTH + 10, 3)
|
||||
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
painter = QPainter(self)
|
||||
c = 255 if isDarkTheme() else 0
|
||||
pen = QPen(QColor(c, c, c, 15))
|
||||
pen.setCosmetic(True)
|
||||
painter.setPen(pen)
|
||||
painter.drawLine(0, 1, self.width(), 1)
|
||||
|
||||
|
||||
class NavigationTreeItem(NavigationPushButton):
|
||||
""" Navigation tree item widget """
|
||||
|
||||
itemClicked = Signal(bool, bool) # triggerByUser, clickArrow
|
||||
|
||||
def __init__(self, icon: Union[str, QIcon, FIF], text: str, isSelectable: bool, parent=None):
|
||||
super().__init__(icon, text, isSelectable, parent)
|
||||
self._arrowAngle = 0
|
||||
self.rotateAni = QPropertyAnimation(self, b'arrowAngle', self)
|
||||
|
||||
def setExpanded(self, isExpanded: bool):
|
||||
self.rotateAni.stop()
|
||||
self.rotateAni.setEndValue(180 if isExpanded else 0)
|
||||
self.rotateAni.setDuration(150)
|
||||
self.rotateAni.start()
|
||||
|
||||
def mouseReleaseEvent(self, e):
|
||||
super().mouseReleaseEvent(e)
|
||||
clickArrow = QRectF(self.width()-30, 8, 20, 20).contains(e.pos())
|
||||
self.itemClicked.emit(True, clickArrow and not self.parent().isLeaf())
|
||||
self.update()
|
||||
|
||||
def _canDrawIndicator(self):
|
||||
p = self.parent() # type: NavigationTreeWidget
|
||||
if p.isLeaf() or p.isSelected:
|
||||
return p.isSelected
|
||||
|
||||
for child in p.treeChildren:
|
||||
if child.itemWidget._canDrawIndicator() and not child.isVisible():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _margins(self):
|
||||
p = self.parent() # type: NavigationTreeWidget
|
||||
return QMargins(p.nodeDepth*28, 0, 20*bool(p.treeChildren), 0)
|
||||
|
||||
def paintEvent(self, e):
|
||||
super().paintEvent(e)
|
||||
if self.isCompacted or not self.parent().treeChildren:
|
||||
return
|
||||
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHints(QPainter.Antialiasing)
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
if self.isPressed:
|
||||
painter.setOpacity(0.7)
|
||||
if not self.isEnabled():
|
||||
painter.setOpacity(0.4)
|
||||
|
||||
painter.translate(self.width() - 20, 18)
|
||||
painter.rotate(self.arrowAngle)
|
||||
FIF.ARROW_DOWN.render(painter, QRectF(-5, -5, 9.6, 9.6))
|
||||
|
||||
def getArrowAngle(self):
|
||||
return self._arrowAngle
|
||||
|
||||
def setArrowAngle(self, angle):
|
||||
self._arrowAngle = angle
|
||||
self.update()
|
||||
|
||||
arrowAngle = Property(float, getArrowAngle, setArrowAngle)
|
||||
|
||||
|
||||
class NavigationTreeWidgetBase(NavigationWidget):
|
||||
""" Navigation tree widget base class """
|
||||
|
||||
def addChild(self, child):
|
||||
""" add child
|
||||
|
||||
Parameters
|
||||
----------
|
||||
child: NavigationTreeWidgetBase
|
||||
child item
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def insertChild(self, index: int, child: NavigationWidget):
|
||||
""" insert child
|
||||
|
||||
Parameters
|
||||
----------
|
||||
child: NavigationTreeWidgetBase
|
||||
child item
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def removeChild(self, child: NavigationWidget):
|
||||
""" remove child
|
||||
|
||||
Parameters
|
||||
----------
|
||||
child: NavigationTreeWidgetBase
|
||||
child item
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def isRoot(self):
|
||||
""" is root node """
|
||||
return True
|
||||
|
||||
def isLeaf(self):
|
||||
""" is leaf node """
|
||||
return True
|
||||
|
||||
def setExpanded(self, isExpanded: bool):
|
||||
""" set the expanded status
|
||||
|
||||
Parameters
|
||||
----------
|
||||
isExpanded: bool
|
||||
whether to expand node
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def childItems(self) -> list:
|
||||
""" return child items """
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class NavigationTreeWidget(NavigationTreeWidgetBase):
|
||||
""" Navigation tree widget """
|
||||
|
||||
expanded = Signal()
|
||||
|
||||
def __init__(self, icon: Union[str, QIcon, FIF], text: str, isSelectable: bool, parent=None):
|
||||
super().__init__(isSelectable, parent)
|
||||
self.treeChildren = [] # type: List[NavigationTreeWidget]
|
||||
self.isExpanded = False
|
||||
self._icon = icon
|
||||
|
||||
self.itemWidget = NavigationTreeItem(icon, text, isSelectable, self)
|
||||
self.vBoxLayout = QVBoxLayout(self)
|
||||
self.expandAni = QPropertyAnimation(self, b'geometry', self)
|
||||
|
||||
self.__initWidget()
|
||||
|
||||
def __initWidget(self):
|
||||
self.vBoxLayout.setSpacing(4)
|
||||
self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.vBoxLayout.addWidget(self.itemWidget, 0, Qt.AlignTop)
|
||||
|
||||
self.itemWidget.itemClicked.connect(self._onClicked)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.expandAni.valueChanged.connect(lambda g: self.setFixedSize(g.size()))
|
||||
self.expandAni.valueChanged.connect(self.expanded)
|
||||
self.expandAni.finished.connect(self.parentWidget().layout().invalidate)
|
||||
|
||||
def addChild(self, child):
|
||||
self.insertChild(-1, child)
|
||||
|
||||
def text(self):
|
||||
return self.itemWidget.text()
|
||||
|
||||
def icon(self):
|
||||
return self.itemWidget.icon()
|
||||
|
||||
def setText(self, text):
|
||||
self.itemWidget.setText(text)
|
||||
|
||||
def setIcon(self, icon: Union[str, QIcon, FIF]):
|
||||
self.itemWidget.setIcon(icon)
|
||||
|
||||
def textColor(self):
|
||||
return self.itemWidget.textColor()
|
||||
|
||||
def setLightTextColor(self, color):
|
||||
""" set the text color in light theme mode """
|
||||
self.itemWidget.setLightTextColor(color)
|
||||
|
||||
def setDarkTextColor(self, color):
|
||||
""" set the text color in dark theme mode """
|
||||
self.itemWidget.setDarkTextColor(color)
|
||||
|
||||
def setTextColor(self, light, dark):
|
||||
""" set the text color in light/dark theme mode """
|
||||
self.lightTextColor = QColor(light)
|
||||
self.darkTextColor = QColor(dark)
|
||||
self.itemWidget.setTextColor(light, dark)
|
||||
|
||||
def setIndicatorColor(self, light, dark):
|
||||
""" set the indicator color in light/dark theme mode """
|
||||
self.itemWidget.setIndicatorColor(light, dark)
|
||||
|
||||
def setFont(self, font: QFont):
|
||||
super().setFont(font)
|
||||
self.itemWidget.setFont(font)
|
||||
|
||||
def clone(self):
|
||||
root = NavigationTreeWidget(self._icon, self.text(), self.isSelectable, self.parent())
|
||||
root.setSelected(self.isSelected)
|
||||
root.setFixedSize(self.size())
|
||||
root.setTextColor(self.lightTextColor, self.darkTextColor)
|
||||
root.setIndicatorColor(self.itemWidget.lightIndicatorColor, self.itemWidget.darkIndicatorColor)
|
||||
root.nodeDepth = self.nodeDepth
|
||||
|
||||
root.clicked.connect(self.clicked)
|
||||
self.selectedChanged.connect(root.setSelected)
|
||||
|
||||
for child in self.treeChildren:
|
||||
root.addChild(child.clone())
|
||||
|
||||
return root
|
||||
|
||||
def suitableWidth(self):
|
||||
m = self.itemWidget._margins()
|
||||
left = 57 + m.left() if not self.icon().isNull() else m.left() + 29
|
||||
tw = self.itemWidget.fontMetrics().boundingRect(self.text()).width()
|
||||
return left + tw + m.right()
|
||||
|
||||
def insertChild(self, index, child):
|
||||
if child in self.treeChildren:
|
||||
return
|
||||
|
||||
child.treeParent = self
|
||||
child.nodeDepth = self.nodeDepth + 1
|
||||
child.setVisible(self.isExpanded)
|
||||
child.expandAni.valueChanged.connect(lambda: self.setFixedSize(self.sizeHint()))
|
||||
child.expandAni.valueChanged.connect(self.expanded)
|
||||
|
||||
# connect height changed signal to parent recursively
|
||||
p = self.treeParent
|
||||
while p:
|
||||
child.expandAni.valueChanged.connect(lambda v, p=p: p.setFixedSize(p.sizeHint()))
|
||||
p = p.treeParent
|
||||
|
||||
if index < 0:
|
||||
index = len(self.treeChildren)
|
||||
|
||||
index += 1 # item widget should always be the first
|
||||
self.treeChildren.insert(index, child)
|
||||
self.vBoxLayout.insertWidget(index, child, 0, Qt.AlignTop)
|
||||
|
||||
# adjust height
|
||||
if self.isExpanded:
|
||||
self.setFixedHeight(self.height() + child.height() + self.vBoxLayout.spacing())
|
||||
p = self.treeParent
|
||||
while p:
|
||||
p.setFixedSize(p.sizeHint())
|
||||
p = p.treeParent
|
||||
|
||||
self.update()
|
||||
|
||||
def removeChild(self, child):
|
||||
self.treeChildren.remove(child)
|
||||
self.vBoxLayout.removeWidget(child)
|
||||
|
||||
def childItems(self) -> list:
|
||||
return self.treeChildren
|
||||
|
||||
def setExpanded(self, isExpanded: bool, ani=False):
|
||||
""" set the expanded status """
|
||||
if isExpanded == self.isExpanded:
|
||||
return
|
||||
|
||||
self.isExpanded = isExpanded
|
||||
self.itemWidget.setExpanded(isExpanded)
|
||||
|
||||
for child in self.treeChildren:
|
||||
child.setVisible(isExpanded)
|
||||
child.setFixedSize(child.sizeHint())
|
||||
|
||||
if ani:
|
||||
self.expandAni.stop()
|
||||
self.expandAni.setStartValue(self.geometry())
|
||||
self.expandAni.setEndValue(QRect(self.pos(), self.sizeHint()))
|
||||
self.expandAni.setDuration(120)
|
||||
self.expandAni.setEasingCurve(QEasingCurve.OutQuad)
|
||||
self.expandAni.start()
|
||||
else:
|
||||
self.setFixedSize(self.sizeHint())
|
||||
|
||||
def isRoot(self):
|
||||
return self.treeParent is None
|
||||
|
||||
def isLeaf(self):
|
||||
return len(self.treeChildren) == 0
|
||||
|
||||
def setSelected(self, isSelected: bool):
|
||||
super().setSelected(isSelected)
|
||||
self.itemWidget.setSelected(isSelected)
|
||||
|
||||
def mouseReleaseEvent(self, e):
|
||||
pass
|
||||
|
||||
def setCompacted(self, isCompacted: bool):
|
||||
super().setCompacted(isCompacted)
|
||||
self.itemWidget.setCompacted(isCompacted)
|
||||
|
||||
def _onClicked(self, triggerByUser, clickArrow):
|
||||
if not self.isCompacted:
|
||||
if self.isSelectable and not self.isSelected and not clickArrow:
|
||||
self.setExpanded(True, ani=True)
|
||||
else:
|
||||
self.setExpanded(not self.isExpanded, ani=True)
|
||||
|
||||
if not clickArrow or self.isCompacted:
|
||||
self.clicked.emit(triggerByUser)
|
||||
|
||||
|
||||
class NavigationAvatarWidget(NavigationWidget):
|
||||
""" Avatar widget """
|
||||
|
||||
def __init__(self, name: str, avatar: Union[str, QPixmap, QImage] = None, parent=None):
|
||||
super().__init__(isSelectable=False, parent=parent)
|
||||
self.name = name
|
||||
self.avatar = AvatarWidget(self)
|
||||
|
||||
self.avatar.setRadius(12)
|
||||
self.avatar.setText(name)
|
||||
self.avatar.move(8, 6)
|
||||
setFont(self)
|
||||
|
||||
if avatar:
|
||||
self.setAvatar(avatar)
|
||||
|
||||
def setName(self, name: str):
|
||||
self.name = name
|
||||
self.avatar.setText(name)
|
||||
self.update()
|
||||
|
||||
def setAvatar(self, avatar: Union[str, QPixmap, QImage]):
|
||||
self.avatar.setImage(avatar)
|
||||
self.avatar.setRadius(12)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHints(
|
||||
QPainter.SmoothPixmapTransform | QPainter.Antialiasing)
|
||||
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
if self.isPressed:
|
||||
painter.setOpacity(0.7)
|
||||
|
||||
# draw background
|
||||
if self.isEnter:
|
||||
c = 255 if isDarkTheme() else 0
|
||||
painter.setBrush(QColor(c, c, c, 10))
|
||||
painter.drawRoundedRect(self.rect(), 5, 5)
|
||||
|
||||
if not self.isCompacted:
|
||||
painter.setPen(self.textColor())
|
||||
painter.setFont(self.font())
|
||||
painter.drawText(QRect(44, 0, 255, 36), Qt.AlignVCenter, self.name)
|
||||
|
||||
|
||||
@InfoBadgeManager.register(InfoBadgePosition.NAVIGATION_ITEM)
|
||||
class NavigationItemInfoBadgeManager(InfoBadgeManager):
|
||||
""" Navigation item info badge manager """
|
||||
|
||||
def eventFilter(self, obj, e: QEvent):
|
||||
if obj is self.target:
|
||||
if e.type() == QEvent.Show:
|
||||
self.badge.show()
|
||||
|
||||
return super().eventFilter(obj, e)
|
||||
|
||||
def position(self):
|
||||
target = self.target
|
||||
self.badge.setVisible(target.isVisible())
|
||||
|
||||
if target.isCompacted:
|
||||
return target.geometry().topRight() - QPoint(self.badge.width() + 2, -2)
|
||||
|
||||
if isinstance(target, NavigationTreeWidget):
|
||||
dx = 10 if target.isLeaf() else 35
|
||||
x = target.geometry().right() - self.badge.width() - dx
|
||||
y = target.y() + 18 - self.badge.height() // 2
|
||||
else:
|
||||
x = target.geometry().right() - self.badge.width() - 10
|
||||
y = target.geometry().center().y() - self.badge.height() // 2
|
||||
|
||||
return QPoint(x, y)
|
||||
|
||||
|
||||
class NavigationFlyoutMenu(ScrollArea):
|
||||
""" Navigation flyout menu """
|
||||
|
||||
expanded = Signal()
|
||||
|
||||
def __init__(self, tree: NavigationTreeWidget, parent=None):
|
||||
super().__init__(parent)
|
||||
self.view = QWidget(self)
|
||||
|
||||
self.treeWidget = tree
|
||||
self.treeChildren = []
|
||||
|
||||
self.vBoxLayout = QVBoxLayout(self.view)
|
||||
|
||||
self.setWidget(self.view)
|
||||
self.setWidgetResizable(True)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.setStyleSheet("ScrollArea{border:none;background:transparent}")
|
||||
self.view.setStyleSheet("QWidget{border:none;background:transparent}")
|
||||
|
||||
self.vBoxLayout.setSpacing(5)
|
||||
self.vBoxLayout.setContentsMargins(5, 8, 5, 8)
|
||||
|
||||
# add nodes to menu
|
||||
for child in tree.treeChildren:
|
||||
node = child.clone()
|
||||
node.expanded.connect(self._adjustViewSize)
|
||||
|
||||
self.treeChildren.append(node)
|
||||
self.vBoxLayout.addWidget(node)
|
||||
|
||||
self._initNode(self)
|
||||
self._adjustViewSize(False)
|
||||
|
||||
def _initNode(self, root: NavigationTreeWidget):
|
||||
for c in root.treeChildren:
|
||||
c.nodeDepth -= 1
|
||||
c.setCompacted(False)
|
||||
|
||||
if c.isLeaf():
|
||||
c.clicked.connect(self.window().fadeOut)
|
||||
|
||||
self._initNode(c)
|
||||
|
||||
def _adjustViewSize(self, emit=True):
|
||||
w = self._suitableWidth()
|
||||
|
||||
# adjust the width of node
|
||||
for node in self.visibleTreeNodes():
|
||||
node.setFixedWidth(w - 10)
|
||||
node.itemWidget.setFixedWidth(w - 10)
|
||||
|
||||
self.view.setFixedSize(w, self.view.sizeHint().height())
|
||||
|
||||
h = min(self.window().parent().height() - 48, self.view.height())
|
||||
|
||||
self.setFixedSize(w, h)
|
||||
|
||||
if emit:
|
||||
self.expanded.emit()
|
||||
|
||||
def _suitableWidth(self):
|
||||
w = 0
|
||||
|
||||
for node in self.visibleTreeNodes():
|
||||
if not node.isHidden():
|
||||
w = max(w, node.suitableWidth() + 10)
|
||||
|
||||
window = self.window().parent() # type: QWidget
|
||||
return min(window.width() // 2 - 25, w) + 10
|
||||
|
||||
def visibleTreeNodes(self):
|
||||
nodes = []
|
||||
queue = deque()
|
||||
queue.extend(self.treeChildren)
|
||||
|
||||
while queue:
|
||||
node = queue.popleft() # type: NavigationTreeWidget
|
||||
nodes.append(node)
|
||||
queue.extend([i for i in node.treeChildren if not i.isHidden()])
|
||||
|
||||
return nodes
|
||||
Reference in New Issue
Block a user