提交 4349b7b3 编写于 作者: 之一Yo's avatar 之一Yo

添加卡片组件

上级 69962aa9
......@@ -7,14 +7,19 @@ assignees: ''
---
<!-- 作者还是一名学生党,有更加重要的事情等着自己完成,所以只会抽出一些周末时间来维护这个项目,响应 issue 的速度可能很慢。新组件将按照作者的计划进行添加,请不要提关于新组件需求的 issue 和 PR。
<!-- 作者还是一名学生党,学业为重,所以只会抽出一些周末时间来维护这个项目,响应 issue 的速度可能很慢,暂时不接受新组件的 PR。
提 Issue 前请先搜索历史 Issue,可能有相似的,如果没有,需要补充完整模板要求的环境信息和最小复现代码,并确保自己安装的包是最新版本的,这很重要,不符合规范的 Issue 会被直接关闭哦。无边框窗口的问题请移步 PyQt-Frameless-Window 仓库。同时请注意礼貌用词,语句之间夹杂的阴阳怪气的省略号或者流汗黄豆绝对达咩 -->
提 Issue 前请先搜索历史 Issue,可能有相似的,如果没有,需要补充完整模板要求的环境信息和最小复现代码,并确保自己安装的包是最新版本的,这很重要!!!请注意礼貌用词,语句之间夹杂的阴阳怪气的省略号或者流汗黄豆绝对达咩!!!
无边框窗口的问题请移步 PyQt-Frameless-Window 仓库。 -->
**Describe the bug**
A clear and concise description of what the bug is.
问题的简要描述。
**Environment**
环境信息
- OS: [e.g. Windows10]
- DPI scaling: [e.g. 125%]
- Python: [e.g. 3.8.6 64-bit]
......@@ -22,13 +27,16 @@ A clear and concise description of what the bug is.
- PyQt-Fluent-Widgets: [e.g 0.2.1]
**To Reproduce**
Steps to reproduce the behavior(you can use gif to demonstrate the problem):
复现问题的步骤,推荐使用 gif 进行演示。
Steps to reproduce the behavior(you can use gif to demonstrate the problem:)
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Code**
最小复现代码
```python
# Minimum code to reproduce the error
......
# coding:utf-8
import sys
from PyQt5.QtCore import Qt, QPoint
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from qfluentwidgets import (CardWidget, setTheme, Theme, IconWidget, BodyLabel, CaptionLabel, PushButton,
TransparentToolButton, FluentIcon, RoundMenu, Action)
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.sizeHint().width()) // 2 + 10
pos = self.moreButton.mapToGlobal(QPoint(x, self.moreButton.height()))
menu.exec(pos)
class Demo(QWidget):
def __init__(self):
super().__init__()
# self.setStyleSheet("Demo {background: rgb(39, 39, 39)}")
# setTheme(Theme.DARK)
self.resize(600, 600)
self.vBoxLayout = QVBoxLayout(self)
self.vBoxLayout.setSpacing(6)
self.vBoxLayout.setContentsMargins(30, 30, 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)
if __name__ == '__main__':
# enable dpi scale
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
app = QApplication(sys.argv)
w = Demo()
w.show()
app.exec_()
......@@ -206,7 +206,7 @@ class MainWindow(FramelessWindow):
def onSupport(self):
w = MessageBox(
'支持作者🥰',
'个人维护不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护的动力🚀',
'个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
self
)
w.yesButton.setText('来啦老弟')
......
......@@ -143,6 +143,9 @@ class Window(FramelessWindow):
self.stackWidget.currentChanged.connect(self.onCurrentInterfaceChanged)
self.stackWidget.setCurrentIndex(1)
# always expand
# self.navigationInterface.setCollapsible(False)
def initWindow(self):
self.resize(900, 700)
self.setWindowIcon(QIcon('resource/logo.png'))
......@@ -186,7 +189,7 @@ class Window(FramelessWindow):
def showMessageBox(self):
w = MessageBox(
'支持作者🥰',
'个人维护不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护的动力🚀',
'个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
self
)
w.yesButton.setText('来啦老弟')
......
......@@ -204,7 +204,7 @@ class Window(FramelessWindow):
def showMessageBox(self):
w = MessageBox(
'支持作者🥰',
'个人维护不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护的动力🚀',
'个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
self
)
w.yesButton.setText('来啦老弟')
......
......@@ -204,7 +204,7 @@ class Window(FramelessWindow):
def showMessageBox(self):
w = MessageBox(
'支持作者🥰',
'个人维护不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护的动力🚀',
'个人开发不易,如果这个项目帮助到了您,可以考虑请作者喝一瓶快乐水🥤。您的支持就是作者开发和维护项目的动力🚀',
self
)
w.yesButton.setText('来啦老弟')
......
......@@ -4,8 +4,7 @@ from PyQt5.QtDesigner import (QPyDesignerCustomWidgetPlugin, QDesignerFormWindow
QPyDesignerContainerExtension)
from qfluentwidgets import (ScrollArea, SmoothScrollArea, SingleDirectionScrollArea, OpacityAniStackedWidget,
PopUpAniStackedWidget)
from qframelesswindow import FramelessMainWindow, FramelessWindow
PopUpAniStackedWidget, CardWidget)
from plugin_base import PluginBase
......@@ -19,6 +18,19 @@ class ContainerPlugin(PluginBase):
return True
class CardWidgetPlugin(ContainerPlugin, QPyDesignerCustomWidgetPlugin):
""" Single direction scroll area plugin """
def createWidget(self, parent):
return CardWidget(parent)
def icon(self):
return super().icon("CommandBar")
def name(self):
return "CardWidget"
class ScrollAreaPluginBase(ContainerPlugin):
""" Scroll area plugin base """
......
......@@ -2,7 +2,7 @@
from PyQt5.QtCore import Qt
from PyQt5.QtDesigner import QPyDesignerCustomWidgetPlugin
from qfluentwidgets import NavigationInterface, NavigationPanel, Pivot
from qfluentwidgets import NavigationInterface, NavigationPanel, Pivot, SegmentedWidget, NavigationBar, FluentIcon
from plugin_base import PluginBase
......@@ -39,6 +39,21 @@ class NavigationPanelPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
return "NavigationPanel"
class NavigationBarPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
""" Navigation bar plugin """
def createWidget(self, parent):
bar = NavigationBar(parent)
bar.addItem('item', FluentIcon.HOME, 'Home')
return bar
def icon(self):
return super().icon("NavigationView")
def name(self):
return "NavigationBar"
class PivotPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
""" Navigation panel plugin """
......@@ -55,3 +70,21 @@ class PivotPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
def name(self):
return "Pivot"
class SegmentedWidgetPlugin(NavigationPlugin, QPyDesignerCustomWidgetPlugin):
""" Segmented widget plugin """
def createWidget(self, parent):
p = SegmentedWidget(parent)
for i in range(1, 4):
p.addItem(f'Item{i}', f'Item{i}', print)
p.setCurrentItem('Item1')
return p
def icon(self):
return super().icon("Pivot")
def name(self):
return "SegmentedWidget"
......@@ -12,7 +12,7 @@ Examples are available at https://github.com/zhiyiYo/PyQt-Fluent-Widgets/tree/ma
:license: GPLv3, see LICENSE for more details.
"""
__version__ = "0.9.6"
__version__ = "0.9.7"
from .components import *
from .common import *
......
......@@ -16,7 +16,7 @@ class NavigationInterface(QWidget):
displayModeChanged = pyqtSignal(NavigationDisplayMode)
def __init__(self, parent=None, showMenuButton=True, showReturnButton=False):
def __init__(self, parent=None, showMenuButton=True, showReturnButton=False, collapsible=True):
"""
Parameters
----------
......@@ -28,11 +28,15 @@ class NavigationInterface(QWidget):
showReturnButton: bool
whether to show return button
collapsible: bool
Is the navigation interface collapsible
"""
super().__init__(parent=parent)
self.panel = NavigationPanel(self)
self.panel.setMenuButtonVisible(showMenuButton)
self.panel.setMenuButtonVisible(showMenuButton and collapsible)
self.panel.setReturnButtonVisible(showReturnButton)
self.panel.setCollapsible(collapsible)
self.panel.installEventFilter(self)
self.panel.displayModeChanged.connect(self.displayModeChanged)
......@@ -216,6 +220,17 @@ class NavigationInterface(QWidget):
""" set the maximum width """
self.panel.setExpandWidth(width)
def setMenuButtonVisible(self, isVisible: bool):
""" set whether the menu button is visible """
self.panel.setMenuButtonVisible(isVisible)
def setReturnButtonVisible(self, isVisible: bool):
""" set whether the return button is visible """
self.panel.setReturnButtonVisible(isVisible)
def setCollapsible(self, collapsible: bool):
self.panel.setCollapsible(collapsible)
def widget(self, routeKey: str):
return self.panel.widget(routeKey)
......
......@@ -64,6 +64,7 @@ class NavigationPanel(QFrame):
self._parent = parent # type: QWidget
self._isMenuButtonVisible = True
self._isReturnButtonVisible = False
self._isCollapsible = True
self.scrollArea = SingleDirectionScrollArea(self)
self.scrollWidget = QWidget()
......@@ -380,6 +381,11 @@ class NavigationPanel(QFrame):
self._isReturnButtonVisible = isVisible
self.returnButton.setVisible(isVisible)
def setCollapsible(self, on: bool):
self._isCollapsible = on
if not on and self.displayMode != NavigationDisplayMode.EXPAND:
self.expand(False)
def setExpandWidth(self, width: int):
""" set the maximum width """
if width <= 42:
......@@ -388,7 +394,7 @@ class NavigationPanel(QFrame):
self.expandWidth = width
NavigationWidget.EXPAND_WIDTH = width - 10
def expand(self):
def expand(self, useAni=True):
""" expand navigation panel """
self._setWidgetCompacted(False)
self.expandAni.setProperty('expand', True)
......@@ -397,7 +403,7 @@ class NavigationPanel(QFrame):
# determine the display mode according to the width of window
# https://learn.microsoft.com/en-us/windows/apps/design/controls/navigationview#default
expandWidth = 1007 + self.expandWidth - 322
if self.window().width() > expandWidth and not self.isMinimalEnabled:
if (self.window().width() > expandWidth and not self.isMinimalEnabled) or not self._isCollapsible:
self.displayMode = NavigationDisplayMode.EXPAND
else:
self.setProperty('menu', True)
......@@ -410,12 +416,16 @@ class NavigationPanel(QFrame):
self.show()
self.displayModeChanged.emit(self.displayMode)
self.expandAni.setStartValue(
QRect(self.pos(), QSize(48, self.height())))
self.expandAni.setEndValue(
QRect(self.pos(), QSize(self.expandWidth, self.height())))
self.expandAni.start()
if useAni:
self.displayModeChanged.emit(self.displayMode)
self.expandAni.setStartValue(
QRect(self.pos(), QSize(48, self.height())))
self.expandAni.setEndValue(
QRect(self.pos(), QSize(self.expandWidth, self.height())))
self.expandAni.start()
else:
self.setFixedWidth(self.expandWidth)
self._onExpandAniFinished()
def collapse(self):
""" collapse navigation panel """
......@@ -477,7 +487,7 @@ class NavigationPanel(QFrame):
self.scrollArea.setFixedHeight(max(h, 36))
def eventFilter(self, obj, e: QEvent):
if obj is not self.window():
if obj is not self.window() or not self._isCollapsible:
return super().eventFilter(obj, e)
if e.type() == QEvent.MouseButtonRelease:
......
......@@ -4,6 +4,7 @@ from .button import (DropDownPushButton, DropDownToolButton, PrimaryPushButton,
PrimarySplitToolButton, PrimaryDropDownPushButton, PrimaryDropDownToolButton,
TogglePushButton, ToggleToolButton, TransparentPushButton, TransparentTogglePushButton,
TransparentToggleToolButton, TransparentDropDownPushButton, TransparentDropDownToolButton)
from .card_widget import CardWidget
from .check_box import CheckBox
from .combo_box import ComboBox, EditableComboBox
from .line_edit import LineEdit, TextEdit, PlainTextEdit, LineEditButton, SearchLineEdit
......
# coding:utf-8
from PyQt5.QtCore import Qt, pyqtSignal, QRectF
from PyQt5.QtGui import QPixmap, QPainter, QColor, QPainterPath
from PyQt5.QtWidgets import QWidget, QFrame
from ...common.style_sheet import isDarkTheme
class CardWidget(QFrame):
""" Card widget """
clicked = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent=parent)
self._isClickEnabled = False
self.isPressed = False
self.isHover = False
def mousePressEvent(self, e):
self.isPressed = True
self.update()
def mouseReleaseEvent(self, e):
self.isPressed = False
self.update()
self.clicked.emit()
def enterEvent(self, e):
self.isHover = True
self.update()
def leaveEvent(self, e):
self.isHover = False
self.update()
def setClickEnabled(self, isEnabled: bool):
self._isClickEnabled = isEnabled
self.update()
def isClickEnabled(self):
return self._isClickEnabled
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
w, h = self.width(), self.height()
r = 5
d = 2 * r
isDark = isDarkTheme()
# draw top border
path = QPainterPath()
# path.moveTo(1, h - r)
path.arcMoveTo(1, h - d - 1, d, d, 225)
path.arcTo(1, h - d - 1, d, d, 225, -45)
path.lineTo(1, r)
path.arcTo(1, 1, d, d, -180, -90)
path.lineTo(w - r, 1)
path.arcTo(w - d - 1, 1, d, d, 90, -90)
path.lineTo(w - 1, h - r)
path.arcTo(w - d - 1, h - d - 1, d, d, 0, -45)
topBorderColor = QColor(0, 0, 0, 20)
if isDark:
if self.isPressed:
topBorderColor = QColor(255, 255, 255, 18)
elif self.isHover:
topBorderColor = QColor(255, 255, 255, 13)
else:
topBorderColor = QColor(0, 0, 0, 15)
painter.strokePath(path, topBorderColor)
# draw bottom border
path = QPainterPath()
path.arcMoveTo(1, h - d - 1, d, d, 225)
path.arcTo(1, h - d - 1, d, d, 225, 45)
path.lineTo(w - r - 1, h - 1)
path.arcTo(w - d - 1, h - d - 1, d, d, 270, 45)
bottomBorderColor = topBorderColor
if not isDark and self.isHover and not self.isPressed:
bottomBorderColor = QColor(0, 0, 0, 27)
painter.strokePath(path, bottomBorderColor)
# draw background
painter.setPen(Qt.NoPen)
alpha = 170
if isDark:
if self.isPressed:
alpha = 8
elif self.isHover:
alpha = 21
else:
alpha = 13
elif self.isPressed or self.isHover:
alpha = 64
rect = self.rect().adjusted(1, 1, -1, -1)
painter.setBrush(QColor(255, 255, 255, alpha))
painter.drawRoundedRect(rect, r, r)
\ No newline at end of file
......@@ -6,7 +6,7 @@ with open('README.md', encoding='utf-8') as f:
setuptools.setup(
name="PyQt-Fluent-Widgets",
version="0.9.6",
version="0.9.7",
keywords="pyqt fluent widgets",
author="zhiyiYo",
author_email="shokokawaii@outlook.com",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册