Files

462 lines
17 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# coding:utf-8
import sys
from pathlib import Path
from PyQt5.QtCore import Qt, QPoint, QSize, QUrl, QRect
from PyQt5.QtGui import QIcon, QFont, QColor, QPainter
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QSizePolicy
from qfluentwidgets import (CardWidget, setTheme, Theme, IconWidget, BodyLabel, CaptionLabel, PushButton,
TransparentToolButton, FluentIcon, RoundMenu, Action, ElevatedCardWidget,
ImageLabel, isDarkTheme, FlowLayout, MSFluentTitleBar, SimpleCardWidget,
HeaderCardWidget, InfoBarIcon, HyperlinkLabel, HorizontalFlipView,
PrimaryPushButton, TitleLabel, PillPushButton, setFont, SingleDirectionScrollArea,
VerticalSeparator)
from qfluentwidgets.components.widgets.acrylic_label import AcrylicBrush
def isWin11():
return sys.platform == 'win32' and sys.getwindowsversion().build >= 22000
if isWin11():
from qframelesswindow import AcrylicWindow as Window
else:
from qframelesswindow import FramelessWindow as Window
class AppCard(CardWidget):
""" App card """
def __init__(self, icon, title, content, parent=None):
super().__init__(parent)
self.iconWidget = IconWidget(icon)
self.titleLabel = BodyLabel(title, self)
self.contentLabel = CaptionLabel(content, self)
self.openButton = PushButton('打开', self)
self.moreButton = TransparentToolButton(FluentIcon.MORE, self)
self.hBoxLayout = QHBoxLayout(self)
self.vBoxLayout = QVBoxLayout()
self.setFixedHeight(73)
self.iconWidget.setFixedSize(48, 48)
self.contentLabel.setTextColor("#606060", "#d2d2d2")
self.openButton.setFixedWidth(120)
self.hBoxLayout.setContentsMargins(20, 11, 11, 11)
self.hBoxLayout.setSpacing(15)
self.hBoxLayout.addWidget(self.iconWidget)
self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
self.vBoxLayout.setSpacing(0)
self.vBoxLayout.addWidget(self.titleLabel, 0, Qt.AlignVCenter)
self.vBoxLayout.addWidget(self.contentLabel, 0, Qt.AlignVCenter)
self.vBoxLayout.setAlignment(Qt.AlignVCenter)
self.hBoxLayout.addLayout(self.vBoxLayout)
self.hBoxLayout.addStretch(1)
self.hBoxLayout.addWidget(self.openButton, 0, Qt.AlignRight)
self.hBoxLayout.addWidget(self.moreButton, 0, Qt.AlignRight)
self.moreButton.setFixedSize(32, 32)
self.moreButton.clicked.connect(self.onMoreButtonClicked)
def onMoreButtonClicked(self):
menu = RoundMenu(parent=self)
menu.addAction(Action(FluentIcon.SHARE, '共享', self))
menu.addAction(Action(FluentIcon.CHAT, '写评论', self))
menu.addAction(Action(FluentIcon.PIN, '固定到任务栏', self))
x = (self.moreButton.width() - menu.width()) // 2 + 10
pos = self.moreButton.mapToGlobal(QPoint(x, self.moreButton.height()))
menu.exec(pos)
class EmojiCard(ElevatedCardWidget):
""" Emoji card """
def __init__(self, iconPath: str, parent=None):
super().__init__(parent)
self.iconWidget = ImageLabel(iconPath, self)
self.label = CaptionLabel(Path(iconPath).stem, self)
self.iconWidget.scaledToHeight(68)
self.vBoxLayout = QVBoxLayout(self)
self.vBoxLayout.setAlignment(Qt.AlignCenter)
self.vBoxLayout.addStretch(1)
self.vBoxLayout.addWidget(self.iconWidget, 0, Qt.AlignCenter)
self.vBoxLayout.addStretch(1)
self.vBoxLayout.addWidget(
self.label, 0, Qt.AlignHCenter | Qt.AlignBottom)
self.setFixedSize(168, 176)
class StatisticsWidget(QWidget):
""" Statistics widget """
def __init__(self, title: str, value: str, parent=None):
super().__init__(parent=parent)
self.titleLabel = CaptionLabel(title, self)
self.valueLabel = BodyLabel(value, self)
self.vBoxLayout = QVBoxLayout(self)
self.vBoxLayout.setContentsMargins(16, 0, 16, 0)
self.vBoxLayout.addWidget(self.valueLabel, 0, Qt.AlignTop)
self.vBoxLayout.addWidget(self.titleLabel, 0, Qt.AlignBottom)
setFont(self.valueLabel, 18, QFont.DemiBold)
self.titleLabel.setTextColor(QColor(96, 96, 96), QColor(206, 206, 206))
class AppInfoCard(SimpleCardWidget):
""" App information card """
def __init__(self, parent=None):
super().__init__(parent)
self.iconLabel = ImageLabel(":/qfluentwidgets/images/logo.png", self)
self.iconLabel.setBorderRadius(8, 8, 8, 8)
self.iconLabel.scaledToWidth(120)
self.nameLabel = TitleLabel('QFluentWidgets', self)
self.installButton = PrimaryPushButton('安装', self)
self.companyLabel = HyperlinkLabel(
QUrl('https://github.com/zhiyiYo/PyQt-Fluent-Widgets'), 'Shokokawaii Inc.', self)
self.installButton.setFixedWidth(160)
self.scoreWidget = StatisticsWidget('平均', '5.0', self)
self.separator = VerticalSeparator(self)
self.commentWidget = StatisticsWidget('评论数', '3K', self)
self.descriptionLabel = BodyLabel(
'PyQt-Fluent-Widgets 是一个基于 PyQt/PySide 的 Fluent Design 风格组件库,包含许多美观实用的组件,支持亮暗主题无缝切换和自定义主题色,帮助开发者快速实现美观优雅的现代化界面。', self)
self.descriptionLabel.setWordWrap(True)
self.tagButton = PillPushButton('组件库', self)
self.tagButton.setCheckable(False)
setFont(self.tagButton, 12)
self.tagButton.setFixedSize(80, 32)
self.shareButton = TransparentToolButton(FluentIcon.SHARE, self)
self.shareButton.setFixedSize(32, 32)
self.shareButton.setIconSize(QSize(14, 14))
self.hBoxLayout = QHBoxLayout(self)
self.vBoxLayout = QVBoxLayout()
self.topLayout = QHBoxLayout()
self.statisticsLayout = QHBoxLayout()
self.buttonLayout = QHBoxLayout()
self.initLayout()
def initLayout(self):
self.hBoxLayout.setSpacing(30)
self.hBoxLayout.setContentsMargins(34, 24, 24, 24)
self.hBoxLayout.addWidget(self.iconLabel)
self.hBoxLayout.addLayout(self.vBoxLayout)
self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
self.vBoxLayout.setSpacing(0)
# name label and install button
self.vBoxLayout.addLayout(self.topLayout)
self.topLayout.setContentsMargins(0, 0, 0, 0)
self.topLayout.addWidget(self.nameLabel)
self.topLayout.addWidget(self.installButton, 0, Qt.AlignRight)
# company label
self.vBoxLayout.addSpacing(3)
self.vBoxLayout.addWidget(self.companyLabel)
# statistics widgets
self.vBoxLayout.addSpacing(20)
self.vBoxLayout.addLayout(self.statisticsLayout)
self.statisticsLayout.setContentsMargins(0, 0, 0, 0)
self.statisticsLayout.setSpacing(10)
self.statisticsLayout.addWidget(self.scoreWidget)
self.statisticsLayout.addWidget(self.separator)
self.statisticsLayout.addWidget(self.commentWidget)
self.statisticsLayout.setAlignment(Qt.AlignLeft)
# description label
self.vBoxLayout.addSpacing(20)
self.vBoxLayout.addWidget(self.descriptionLabel)
# button
self.vBoxLayout.addSpacing(12)
self.buttonLayout.setContentsMargins(0, 0, 0, 0)
self.vBoxLayout.addLayout(self.buttonLayout)
self.buttonLayout.addWidget(self.tagButton, 0, Qt.AlignLeft)
self.buttonLayout.addWidget(self.shareButton, 0, Qt.AlignRight)
class GalleryCard(HeaderCardWidget):
""" Gallery card """
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle('屏幕截图')
self.flipView = HorizontalFlipView(self)
self.expandButton = TransparentToolButton(
FluentIcon.CHEVRON_RIGHT_MED, self)
self.expandButton.setFixedSize(32, 32)
self.expandButton.setIconSize(QSize(12, 12))
self.flipView.addImages([
'resource/shoko1.jpg', 'resource/shoko2.jpg',
'resource/shoko3.jpg', 'resource/shoko4.jpg',
])
self.flipView.setBorderRadius(8)
self.flipView.setSpacing(10)
self.headerLayout.addWidget(self.expandButton, 0, Qt.AlignRight)
self.viewLayout.addWidget(self.flipView)
class DescriptionCard(HeaderCardWidget):
""" Description card """
def __init__(self, parent=None):
super().__init__(parent)
self.descriptionLabel = BodyLabel(
'PyQt-Fluent-Widgets 是一个基于 PyQt/PySide 的 Fluent Design 风格组件库,包含许多美观实用的组件,支持亮暗主题无缝切换和自定义主题色,搭配所见即所得的 QtDesigner帮助开发者快速实现美观优雅的现代化界面。', self)
self.descriptionLabel.setWordWrap(True)
self.viewLayout.addWidget(self.descriptionLabel)
self.setTitle('描述')
class SystemRequirementCard(HeaderCardWidget):
""" System requirements card """
def __init__(self, parent=None):
super().__init__(parent)
self.setTitle('系统要求')
self.infoLabel = BodyLabel('此产品适用于你的设备。具有复选标记的项目符合开发人员的系统要求。', self)
self.successIcon = IconWidget(InfoBarIcon.SUCCESS, self)
self.detailButton = HyperlinkLabel('详细信息', self)
self.vBoxLayout = QVBoxLayout()
self.hBoxLayout = QHBoxLayout()
self.successIcon.setFixedSize(16, 16)
self.hBoxLayout.setSpacing(10)
self.vBoxLayout.setSpacing(16)
self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
self.hBoxLayout.addWidget(self.successIcon)
self.hBoxLayout.addWidget(self.infoLabel)
self.vBoxLayout.addLayout(self.hBoxLayout)
self.vBoxLayout.addWidget(self.detailButton)
self.viewLayout.addLayout(self.vBoxLayout)
class LightBox(QWidget):
""" Light box """
def __init__(self, parent=None):
super().__init__(parent=parent)
if isDarkTheme():
tintColor = QColor(32, 32, 32, 200)
luminosityColor = QColor(0, 0, 0, 0)
else:
tintColor = QColor(255, 255, 255, 160)
luminosityColor = QColor(255, 255, 255, 0)
self.acrylicBrush = AcrylicBrush(self, 30, tintColor, luminosityColor)
self.vBoxLayout = QVBoxLayout(self)
self.closeButton = TransparentToolButton(FluentIcon.CLOSE, self)
self.flipView = HorizontalFlipView(self)
self.nameLabel = BodyLabel('屏幕截图 1', self)
self.pageNumButton = PillPushButton('1 / 4', self)
self.pageNumButton.setCheckable(False)
self.pageNumButton.setFixedSize(80, 32)
setFont(self.nameLabel, 16, QFont.DemiBold)
self.closeButton.setFixedSize(32, 32)
self.closeButton.setIconSize(QSize(14, 14))
self.closeButton.clicked.connect(self.hide)
self.vBoxLayout.setContentsMargins(26, 28, 26, 28)
self.vBoxLayout.addWidget(self.closeButton, 0, Qt.AlignRight | Qt.AlignTop)
self.vBoxLayout.addWidget(self.flipView, 1)
self.vBoxLayout.addWidget(self.nameLabel, 0, Qt.AlignHCenter)
self.vBoxLayout.addSpacing(10)
self.vBoxLayout.addWidget(self.pageNumButton, 0, Qt.AlignHCenter)
self.flipView.addImages([
'resource/shoko1.jpg', 'resource/shoko2.jpg',
'resource/shoko3.jpg', 'resource/shoko4.jpg',
])
self.flipView.currentIndexChanged.connect(self.setCurrentIndex)
def showEvent(self, e):
rect = QRect(self.mapToGlobal(QPoint()), self.size())
self.acrylicBrush.grabImage(rect)
super().showEvent(e)
def setCurrentIndex(self, index: int):
self.nameLabel.setText(f'屏幕截图 {index + 1}')
self.pageNumButton.setText(f'{index + 1} / {self.flipView.count()}')
self.flipView.setCurrentIndex(index)
def paintEvent(self, e):
if self.acrylicBrush.isAvailable():
return self.acrylicBrush.paint()
painter = QPainter(self)
painter.setPen(Qt.NoPen)
if isDarkTheme():
painter.setBrush(QColor(32, 32, 32))
else:
painter.setBrush(QColor(255, 255, 255))
painter.drawRect(self.rect())
def resizeEvent(self, e):
w = self.width() - 52
self.flipView.setItemSize(QSize(w, w * 9 // 16))
class MicaWindow(Window):
def __init__(self):
super().__init__()
self.setTitleBar(MSFluentTitleBar(self))
if isWin11():
self.windowEffect.setMicaEffect(self.winId(), isDarkTheme())
class Demo1(MicaWindow):
def __init__(self):
super().__init__()
self.setWindowIcon(QIcon(':/qfluentwidgets/images/logo.png'))
self.setWindowTitle('Fluent Emoji gallery')
self.flowLayout = FlowLayout(self)
self.resize(580, 680)
self.flowLayout.setSpacing(6)
self.flowLayout.setContentsMargins(30, 60, 30, 30)
self.flowLayout.setAlignment(Qt.AlignVCenter)
for path in Path('./resource').glob('*.png'):
self.addCard(str(path))
def addCard(self, iconPath: str):
card = EmojiCard(iconPath, self)
self.flowLayout.addWidget(card)
class Demo2(MicaWindow):
def __init__(self):
super().__init__()
self.resize(600, 600)
self.vBoxLayout = QVBoxLayout(self)
self.vBoxLayout.setSpacing(6)
self.vBoxLayout.setContentsMargins(30, 60, 30, 30)
self.vBoxLayout.setAlignment(Qt.AlignTop)
suffix = ":/qfluentwidgets/images/controls"
self.addCard(f":/qfluentwidgets/images/logo.png",
"PyQt-Fluent-Widgets", 'Shokokawaii Inc.')
self.addCard(f"{suffix}/TitleBar.png",
"PyQt-Frameless-Window", 'Shokokawaii Inc.')
self.addCard(f"{suffix}/RatingControl.png",
"反馈中心", 'Microsoft Corporation')
self.addCard(f"{suffix}/Checkbox.png",
"Microsoft 使用技巧", 'Microsoft Corporation')
self.addCard(f"{suffix}/Pivot.png", "MSN 天气", 'Microsoft Corporation')
self.addCard(f"{suffix}/MediaPlayerElement.png",
"电影和电视", 'Microsoft Corporation')
self.addCard(f"{suffix}/PersonPicture.png",
"照片", 'Microsoft Corporation')
def addCard(self, icon, title, content):
card = AppCard(icon, title, content, self)
self.vBoxLayout.addWidget(card, alignment=Qt.AlignTop)
class Demo3(MicaWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('PyQt-Fluent-Widgets')
self.setWindowIcon(QIcon(':/qfluentwidgets/images/logo.png'))
self.hBoxLayout = QHBoxLayout(self)
self.scrollArea = SingleDirectionScrollArea(self)
self.view = QWidget(self)
self.vBoxLayout = QVBoxLayout(self.view)
self.appCard = AppInfoCard(self)
self.galleryCard = GalleryCard(self)
self.descriptionCard = DescriptionCard(self)
self.systemCard = SystemRequirementCard(self)
self.lightBox = LightBox(self)
self.lightBox.hide()
self.galleryCard.flipView.itemClicked.connect(self.showLightBox)
self.scrollArea.setWidget(self.view)
self.scrollArea.setWidgetResizable(True)
self.hBoxLayout.setContentsMargins(0, 48, 0, 0)
self.hBoxLayout.addWidget(self.scrollArea)
self.vBoxLayout.setSpacing(10)
self.vBoxLayout.setContentsMargins(30, 0, 30, 30)
self.vBoxLayout.addWidget(self.appCard, 0, Qt.AlignTop)
self.vBoxLayout.addWidget(self.galleryCard, 0, Qt.AlignTop)
self.vBoxLayout.addWidget(self.descriptionCard, 0, Qt.AlignTop)
self.vBoxLayout.addWidget(self.systemCard, 0, Qt.AlignTop)
self.resize(780, 800)
self.titleBar.raise_()
self.scrollArea.setStyleSheet(
"QScrollArea {border: none; background:transparent}")
self.view.setStyleSheet('QWidget {background:transparent}')
def showLightBox(self):
index = self.galleryCard.flipView.currentIndex()
self.lightBox.setCurrentIndex(index)
self.lightBox.show()
def resizeEvent(self, e):
super().resizeEvent(e)
self.lightBox.resize(self.width(), self.height() - 48)
self.lightBox.move(0, 48)
if __name__ == '__main__':
# enable dpi scale
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
# setTheme(Theme.DARK)
app = QApplication(sys.argv)
w1 = Demo1()
w1.show()
w2 = Demo2()
w2.show()
w3 = Demo3()
w3.show()
app.exec_()