# coding:utf-8 from PyQt6.QtCore import Qt, pyqtSignal, QUrl, QEvent from PyQt6.QtGui import QDesktopServices, QPainter, QPen, QColor from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QFrame from qfluentwidgets import (ScrollArea, PushButton, ToolButton, FluentIcon, isDarkTheme, IconWidget, Theme, ToolTipFilter, TitleLabel, CaptionLabel, StrongBodyLabel, BodyLabel, toggleTheme) from ..common.config import cfg, HELP_URL from ..common.icon import Icon from ..common.style_sheet import StyleSheet from ..common.signal_bus import signalBus 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 = TitleLabel(title, self) self.subtitleLabel = CaptionLabel(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.themeButton.clicked.connect(lambda: toggleTheme(True)) 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))) 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 = StrongBodyLabel(title, self) self.card = QFrame(self) self.sourceWidget = QFrame(self.card) self.sourcePath = sourcePath self.sourcePathLabel = BodyLabel( 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.card.setObjectName('card') 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())