# coding:utf-8 from PySide6.QtCore import Qt, QUrl, QEvent from PySide6.QtGui import QDesktopServices, QPainter, QPen, QColor from PySide6.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame from qfluentwidgets import (ScrollArea, PushButton, ToolButton, FluentIcon, isDarkTheme, IconWidget, Theme, ToolTipFilter) from ..common.config import cfg, FEEDBACK_URL, HELP_URL, EXAMPLE_URL from ..common.signal_bus import signalBus from ..common.style_sheet import StyleSheet class SeparatorWidget(QWidget): """ Seperator widget """ def __init__(self, parent=None): super().__init__(parent=parent) self.setFixedSize(6, 16) def paintEvent(self, e): painter = QPainter(self) pen = QPen(1) pen.setCosmetic(True) c = QColor(255, 255, 255, 21) if isDarkTheme() else QColor(0, 0, 0, 15) pen.setColor(c) painter.setPen(pen) x = self.width() // 2 painter.drawLine(x, 0, x, self.height()) class ToolBar(QWidget): """ Tool bar """ def __init__(self, title, subtitle, parent=None): super().__init__(parent=parent) self.titleLabel = QLabel(title, self) self.subtitleLabel = QLabel(subtitle, self) self.documentButton = PushButton( self.tr('Documentation'), self, FluentIcon.DOCUMENT) self.sourceButton = PushButton(self.tr('Source'), self, FluentIcon.GITHUB) self.themeButton = ToolButton(FluentIcon.CONSTRACT, self) self.separator = SeparatorWidget(self) self.supportButton = ToolButton(FluentIcon.HEART, self) self.feedbackButton = ToolButton(FluentIcon.FEEDBACK, self) self.vBoxLayout = QVBoxLayout(self) self.buttonLayout = QHBoxLayout() self.__initWidget() def __initWidget(self): self.setFixedHeight(138) self.vBoxLayout.setSpacing(0) self.vBoxLayout.setContentsMargins(36, 22, 36, 12) self.vBoxLayout.addWidget(self.titleLabel) self.vBoxLayout.addSpacing(4) self.vBoxLayout.addWidget(self.subtitleLabel) self.vBoxLayout.addSpacing(4) self.vBoxLayout.addLayout(self.buttonLayout, 1) self.vBoxLayout.setAlignment(Qt.AlignTop) self.buttonLayout.setSpacing(4) self.buttonLayout.setContentsMargins(0, 0, 0, 0) self.buttonLayout.addWidget(self.documentButton, 0, Qt.AlignLeft) self.buttonLayout.addWidget(self.sourceButton, 0, Qt.AlignLeft) self.buttonLayout.addStretch(1) self.buttonLayout.addWidget(self.themeButton, 0, Qt.AlignRight) self.buttonLayout.addWidget(self.separator, 0, Qt.AlignRight) self.buttonLayout.addWidget(self.supportButton, 0, Qt.AlignRight) self.buttonLayout.addWidget(self.feedbackButton, 0, Qt.AlignRight) self.buttonLayout.setAlignment(Qt.AlignVCenter | Qt.AlignLeft) self.themeButton.installEventFilter(ToolTipFilter(self.themeButton)) self.supportButton.installEventFilter(ToolTipFilter(self.supportButton)) self.feedbackButton.installEventFilter( ToolTipFilter(self.feedbackButton)) self.themeButton.setToolTip(self.tr('Toggle theme')) self.supportButton.setToolTip(self.tr('Support me')) self.feedbackButton.setToolTip(self.tr('Send feedback')) self.titleLabel.setObjectName('titleLabel') self.subtitleLabel.setObjectName('subtitleLabel') self.themeButton.clicked.connect(self.toggleTheme) self.supportButton.clicked.connect(signalBus.supportSignal) self.documentButton.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(HELP_URL))) self.sourceButton.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(EXAMPLE_URL))) self.feedbackButton.clicked.connect( lambda: QDesktopServices.openUrl(QUrl(FEEDBACK_URL))) def toggleTheme(self): theme = Theme.LIGHT if isDarkTheme() else Theme.DARK cfg.set(cfg.themeMode, theme) class ExampleCard(QWidget): """ Example card """ def __init__(self, title, widget: QWidget, sourcePath, stretch=0, parent=None): super().__init__(parent=parent) self.widget = widget self.stretch = stretch self.titleLabel = QLabel(title, self) self.card = QFrame(self) self.sourceWidget = QFrame(self.card) self.sourcePath = sourcePath self.sourcePathLabel = QLabel( self.tr('Source code'), self.sourceWidget) self.linkIcon = IconWidget(FluentIcon.LINK, self.sourceWidget) self.vBoxLayout = QVBoxLayout(self) self.cardLayout = QVBoxLayout(self.card) self.topLayout = QHBoxLayout() self.bottomLayout = QHBoxLayout(self.sourceWidget) self.__initWidget() def __initWidget(self): self.linkIcon.setFixedSize(16, 16) self.__initLayout() self.sourceWidget.setCursor(Qt.PointingHandCursor) self.sourceWidget.installEventFilter(self) self.titleLabel.setObjectName('titleLabel') self.card.setObjectName('card') self.sourcePathLabel.setObjectName('sourcePathLabel') self.sourceWidget.setObjectName('sourceWidget') def __initLayout(self): self.vBoxLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize) self.cardLayout.setSizeConstraint(QVBoxLayout.SetMinimumSize) self.topLayout.setSizeConstraint(QHBoxLayout.SetMinimumSize) self.vBoxLayout.setSpacing(12) self.vBoxLayout.setContentsMargins(0, 0, 0, 0) self.topLayout.setContentsMargins(12, 12, 12, 12) self.bottomLayout.setContentsMargins(18, 18, 18, 18) self.cardLayout.setContentsMargins(0, 0, 0, 0) self.vBoxLayout.addWidget(self.titleLabel, 0, Qt.AlignTop) self.vBoxLayout.addWidget(self.card, 0, Qt.AlignTop) self.vBoxLayout.setAlignment(Qt.AlignTop) self.cardLayout.setSpacing(0) self.cardLayout.setAlignment(Qt.AlignTop) self.cardLayout.addLayout(self.topLayout, 0) self.cardLayout.addWidget(self.sourceWidget, 0, Qt.AlignBottom) self.widget.setParent(self.card) self.topLayout.addWidget(self.widget) if self.stretch == 0: self.topLayout.addStretch(1) self.widget.show() self.bottomLayout.addWidget(self.sourcePathLabel, 0, Qt.AlignLeft) self.bottomLayout.addStretch(1) self.bottomLayout.addWidget(self.linkIcon, 0, Qt.AlignRight) self.bottomLayout.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) def eventFilter(self, obj, e): if obj is self.sourceWidget: if e.type() == QEvent.MouseButtonRelease: QDesktopServices.openUrl(QUrl(self.sourcePath)) return super().eventFilter(obj, e) class GalleryInterface(ScrollArea): """ Gallery interface """ def __init__(self, title: str, subtitle: str, parent=None): """ Parameters ---------- title: str The title of gallery subtitle: str The subtitle of gallery parent: QWidget parent widget """ super().__init__(parent=parent) self.view = QWidget(self) self.toolBar = ToolBar(title, subtitle, self) self.vBoxLayout = QVBoxLayout(self.view) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setViewportMargins(0, self.toolBar.height(), 0, 0) self.setWidget(self.view) self.setWidgetResizable(True) self.vBoxLayout.setSpacing(30) self.vBoxLayout.setAlignment(Qt.AlignTop) self.vBoxLayout.setContentsMargins(36, 20, 36, 36) self.view.setObjectName('view') StyleSheet.GALLERY_INTERFACE.apply(self) def addExampleCard(self, title, widget, sourcePath: str, stretch=0): card = ExampleCard(title, widget, sourcePath, stretch, self.view) self.vBoxLayout.addWidget(card, 0, Qt.AlignTop) return card def scrollToCard(self, index: int): """ scroll to example card """ w = self.vBoxLayout.itemAt(index).widget() self.verticalScrollBar().setValue(w.y()) def resizeEvent(self, e): super().resizeEvent(e) self.toolBar.resize(self.width(), self.toolBar.height())