initial fluent-widgets ui

This commit is contained in:
2025-08-14 18:45:16 +08:00
parent 746e83ab23
commit 4c66886257
1198 changed files with 805339 additions and 0 deletions

View File

@ -0,0 +1,6 @@
from .acrylic_menu import AcrylicMenu, AcrylicLineEditMenu, AcrylicCheckableMenu, AcrylicCheckableSystemTrayMenu, AcrylicSystemTrayMenu
from .acrylic_line_edit import AcrylicLineEditBase, AcrylicLineEdit, AcrylicSearchLineEdit
from .acrylic_combo_box import AcrylicComboBox, AcrylicComboBoxSettingCard, AcrylicEditableComboBox
from .acrylic_widget import AcrylicWidget, AcrylicBrush
from .acrylic_flyout import AcrylicFlyoutView, AcrylicFlyoutViewBase, AcrylicFlyout
from .acrylic_tool_tip import AcrylicToolTip, AcrylicToolTipFilter

View File

@ -0,0 +1,96 @@
# coding:utf-8
from PySide6.QtCore import Qt, QPoint
from PySide6.QtGui import QAction
from .acrylic_menu import AcrylicMenuBase, AcrylicMenuActionListWidget
from .acrylic_line_edit import AcrylicLineEditBase
from ..widgets.combo_box import ComboBoxMenu, ComboBox, EditableComboBox
from ..widgets.menu import MenuAnimationType, RoundMenu, IndicatorMenuItemDelegate
from ..settings import SettingCard
from ...common.config import OptionsConfigItem, qconfig
class AcrylicComboMenuActionListWidget(AcrylicMenuActionListWidget):
def _topMargin(self):
return 2
class AcrylicComboBoxMenu(AcrylicMenuBase, RoundMenu):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setUpMenu(AcrylicComboMenuActionListWidget(self))
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.view.setItemDelegate(IndicatorMenuItemDelegate())
self.view.setObjectName('comboListWidget')
self.setItemHeight(33)
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
return super().exec(pos, ani, aniType)
class AcrylicComboBox(ComboBox):
""" Acrylic combo box """
def _createComboMenu(self):
return AcrylicComboBoxMenu(self)
class AcrylicEditableComboBox(AcrylicLineEditBase, EditableComboBox):
""" Acrylic combo box """
def _createComboMenu(self):
return AcrylicComboBoxMenu(self)
class AcrylicComboBoxSettingCard(SettingCard):
""" Setting card with a combo box """
def __init__(self, configItem: OptionsConfigItem, icon, title, content=None, texts=None, parent=None):
"""
Parameters
----------
configItem: OptionsConfigItem
configuration item operated by the card
icon: str | QIcon | FluentIconBase
the icon to be drawn
title: str
the title of card
content: str
the content of card
texts: List[str]
the text of items
parent: QWidget
parent widget
"""
super().__init__(icon, title, content, parent)
self.configItem = configItem
self.comboBox = AcrylicComboBox(self)
self.hBoxLayout.addWidget(self.comboBox, 0, Qt.AlignRight)
self.hBoxLayout.addSpacing(16)
self.optionToText = {o: t for o, t in zip(configItem.options, texts)}
for text, option in zip(texts, configItem.options):
self.comboBox.addItem(text, userData=option)
self.comboBox.setCurrentText(self.optionToText[qconfig.get(configItem)])
self.comboBox.currentIndexChanged.connect(self._onCurrentIndexChanged)
configItem.valueChanged.connect(self.setValue)
def _onCurrentIndexChanged(self, index: int):
qconfig.set(self.configItem, self.comboBox.itemData(index))
def setValue(self, value):
if value not in self.optionToText:
return
self.comboBox.setCurrentText(self.optionToText[value])
qconfig.set(self.configItem, value)

View File

@ -0,0 +1,105 @@
# coding:utf-8
from typing import Union
from PySide6.QtCore import QPoint, Qt, QRect, QRectF
from PySide6.QtGui import QPixmap, QPainter, QColor, QPainterPath, QIcon, QImage
from PySide6.QtWidgets import QWidget
from ...common.style_sheet import isDarkTheme
from ...common.icon import FluentIconBase
from ..widgets.flyout import FlyoutAnimationType, FlyoutViewBase, FlyoutView, Flyout, FlyoutAnimationManager
from .acrylic_widget import AcrylicWidget
class AcrylicFlyoutViewBase(AcrylicWidget, FlyoutViewBase):
""" Acrylic flyout view base """
def acrylicClipPath(self):
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect().adjusted(1, 1, -1, -1)), 8, 8)
return path
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
self._drawAcrylic(painter)
# draw border
painter.setBrush(Qt.NoBrush)
painter.setPen(self.borderColor())
rect = QRectF(self.rect()).adjusted(1, 1, -1, -1)
painter.drawRoundedRect(rect, 8, 8)
class AcrylicFlyoutView(AcrylicWidget, FlyoutView):
""" Acrylic flyout view """
def acrylicClipPath(self):
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect().adjusted(1, 1, -1, -1)), 8, 8)
return path
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing)
self._drawAcrylic(painter)
# draw border
painter.setBrush(Qt.NoBrush)
painter.setPen(self.borderColor())
rect = self.rect().adjusted(1, 1, -1, -1)
painter.drawRoundedRect(rect, 8, 8)
class AcrylicFlyout(Flyout):
""" Acrylic flyout """
@classmethod
def create(cls, title: str, content: str, icon: Union[FluentIconBase, QIcon, str] = None,
image: Union[str, QPixmap, QImage] = None, isClosable=False, target: Union[QWidget, QPoint] = None,
parent=None, aniType=FlyoutAnimationType.PULL_UP, isDeleteOnClose=True):
""" create and show a flyout using the default view
Parameters
----------
title: str
the title of teaching tip
content: str
the content of teaching tip
icon: InfoBarIcon | FluentIconBase | QIcon | str
the icon of teaching tip
image: str | QPixmap | QImage
the image of teaching tip
isClosable: bool
whether to show the close button
target: QWidget | QPoint
the target widget or position to show flyout
parent: QWidget
parent window
aniType: FlyoutAnimationType
flyout animation type
isDeleteOnClose: bool
whether delete flyout automatically when flyout is closed
"""
view = AcrylicFlyoutView(title, content, icon, image, isClosable)
w = cls.make(view, target, parent, aniType, isDeleteOnClose)
view.closed.connect(w.close)
return w
def exec(self, pos: QPoint, aniType=FlyoutAnimationType.PULL_UP):
""" show calendar view """
self.aniManager = FlyoutAnimationManager.make(aniType, self)
if isinstance(self.view, AcrylicWidget):
pos = self.aniManager._adjustPosition(pos)
self.view.acrylicBrush.grabImage(QRect(pos, self.layout().sizeHint()))
self.show()
self.aniManager.exec(pos)

View File

@ -0,0 +1,27 @@
# coding:utf-8
from .acrylic_menu import AcrylicCompleterMenu, AcrylicLineEditMenu
from ..widgets.line_edit import LineEdit, SearchLineEdit
class AcrylicLineEditBase:
""" Acrylic line edit base """
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
def setCompleter(self, completer):
super().setCompleter(completer)
self.setCompleterMenu(AcrylicCompleterMenu(self))
def contextMenuEvent(self, e):
menu = AcrylicLineEditMenu(self)
menu.exec(e.globalPos())
class AcrylicLineEdit(AcrylicLineEditBase, LineEdit):
""" Acrylic line edit """
class AcrylicSearchLineEdit(AcrylicLineEditBase, SearchLineEdit):
""" Acrylic search line edit """

View File

@ -0,0 +1,204 @@
# coding:utf-8
from typing import List
from PySide6.QtCore import Qt, QRect, QRectF, QSize
from PySide6.QtGui import QPainter, QColor, QPainterPath, QAction
from PySide6.QtWidgets import QLineEdit, QListWidgetItem, QListWidget
from ..widgets.menu import (RoundMenu, MenuAnimationType, MenuAnimationManager, MenuActionListWidget,
IndicatorMenuItemDelegate, LineEditMenu, MenuIndicatorType, CheckableMenu)
from ..widgets.line_edit import CompleterMenu, LineEdit
from ..widgets.acrylic_label import AcrylicBrush
from ...common.style_sheet import isDarkTheme
class AcrylicMenuActionListWidget(MenuActionListWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.acrylicBrush = AcrylicBrush(self.viewport(), 35)
self.setViewportMargins(0, 0, 0, 0)
self.setProperty("transparent", True)
super().addItem(self.createPlaceholderItem(self._topMargin()))
super().addItem(self.createPlaceholderItem(self._bottomMargin()))
def _updateAcrylicColor(self):
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, 50)
self.acrylicBrush.tintColor = tintColor
self.acrylicBrush.luminosityColor = luminosityColor
def _topMargin(self):
return 6
def _bottomMargin(self):
return 6
def setItemHeight(self, height: int):
""" set the height of item """
if height == self._itemHeight:
return
for i in range(1, self.count() - 1):
item = self.item(i)
if not self.itemWidget(item):
item.setSizeHint(QSize(item.sizeHint().width(), height))
self._itemHeight = height
self.adjustSize()
def addItem(self, item):
return super().insertItem(self.count() - 1, item)
def createPlaceholderItem(self, height=2):
item = QListWidgetItem()
item.setSizeHint(QSize(1, height))
item.setFlags(Qt.ItemFlag.NoItemFlags)
return item
def clipPath(self):
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect()).adjusted(0, 0, -2.5, -2.5), 8, 8)
return path
def paintEvent(self, e) -> None:
painter = QPainter(self.viewport())
painter.setRenderHints(QPainter.Antialiasing |
QPainter.SmoothPixmapTransform)
self.acrylicBrush.clipPath = self.clipPath()
self._updateAcrylicColor()
self.acrylicBrush.paint()
super().paintEvent(e)
class AcrylicMenuBase:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def setUpMenu(self, view):
self.hBoxLayout.removeWidget(self.view)
self.view.deleteLater()
self.view = view
self.hBoxLayout.addWidget(self.view)
self.setShadowEffect()
self.view.itemClicked.connect(self._onItemClicked)
self.view.itemEntered.connect(self._onItemEntered)
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
p = MenuAnimationManager.make(self, aniType)._endPosition(pos)
self.view.acrylicBrush.grabImage(QRect(p, self.layout().sizeHint()))
super().exec(pos, ani, aniType)
class AcrylicMenu(AcrylicMenuBase, RoundMenu):
""" Acrylic menu """
def __init__(self, title="", parent=None):
super().__init__(title, parent)
self.setUpMenu(AcrylicMenuActionListWidget(self))
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
return super().exec(pos, ani, aniType)
class AcrylicCompleterMenuActionListWidget(AcrylicMenuActionListWidget):
def clipPath(self):
path = QPainterPath()
path.setFillRule(Qt.FillRule.WindingFill)
path.addRoundedRect(QRectF(self.rect()).adjusted(1, 1, -2.5, -2.5), 8, 8)
if self.property("dropDown"):
path.addRect(1, 1, 11, 11)
path.addRect(self.width() - 12, 1, 11, 11)
else:
path.addRect(1, self.height() - 11, 11, 11)
path.addRect(self.width() - 12, self.height() - 11, 11, 11)
return path
class AcrylicCompleterMenu(AcrylicMenuBase, CompleterMenu):
""" Acrylic completer menu """
def __init__(self, lineEdit: LineEdit):
super().__init__(lineEdit)
self.setUpMenu(AcrylicCompleterMenuActionListWidget(self))
self.view.setObjectName('completerListWidget')
self.view.setItemDelegate(IndicatorMenuItemDelegate())
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setItemHeight(33)
def _onItemClicked(self, item):
self._hideMenu(False)
self._onCompletionItemSelected(item.text(), self.view.row(item)-1)
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
return super().exec(pos, ani, aniType)
def setItems(self, items):
""" set completion items """
self.view.clear()
self.items = items
QListWidget.addItem(self.view, self.view.createPlaceholderItem(self.view._topMargin()))
self.view.addItems(items)
for i in range(1, self.view.count()):
item = self.view.item(i)
item.setSizeHint(QSize(1, self.itemHeight))
QListWidget.addItem(self.view, self.view.createPlaceholderItem(self.view._bottomMargin()))
class AcrylicLineEditMenu(AcrylicMenuBase, LineEditMenu):
""" Acrylic line edit menu """
def __init__(self, parent: QLineEdit):
super().__init__(parent)
self.setUpMenu(AcrylicMenuActionListWidget(self))
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
return super().exec(pos, ani, aniType)
class AcrylicCheckableMenu(AcrylicMenuBase, CheckableMenu):
""" Checkable menu """
def __init__(self, title="", parent=None, indicatorType=MenuIndicatorType.CHECK):
super().__init__(title, parent, indicatorType)
self.setUpMenu(AcrylicMenuActionListWidget(self))
self.view.setObjectName('checkableListWidget')
def exec(self, pos, ani=True, aniType=MenuAnimationType.DROP_DOWN):
return super().exec(pos, ani, aniType)
class AcrylicSystemTrayMenu(AcrylicMenu):
""" System tray menu """
def showEvent(self, e):
super().showEvent(e)
self.adjustPosition()
self.view.acrylicBrush.grabImage(QRect(self.pos(), self.layout().sizeHint()))
class AcrylicCheckableSystemTrayMenu(AcrylicCheckableMenu):
""" Checkable system tray menu """
def showEvent(self, e):
super().showEvent(e)
self.adjustPosition()

View File

@ -0,0 +1,39 @@
# coding: utf-8
from PySide6.QtCore import QRect, QRectF
from PySide6.QtGui import QPainterPath
from PySide6.QtWidgets import QApplication, QFrame
from .acrylic_widget import AcrylicWidget
from ..widgets.tool_tip import ToolTip, ToolTipFilter
class AcrylicToolTipContainer(AcrylicWidget, QFrame):
""" Acrylic tool tip container """
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setProperty("transparent", True)
def acrylicClipPath(self):
path = QPainterPath()
path.addRoundedRect(QRectF(self.rect().adjusted(1, 1, -1, -1)), 3, 3)
return path
class AcrylicToolTip(ToolTip):
""" Acrylic tool tip """
def _createContainer(self):
return AcrylicToolTipContainer(self)
def showEvent(self, e):
pos = self.pos() + self.container.pos()
self.container.acrylicBrush.grabImage(QRect(pos, self.container.size()))
return super().showEvent(e)
class AcrylicToolTipFilter(ToolTipFilter):
""" Acrylic tool tip filter """
def _createToolTip(self):
return AcrylicToolTip(self.parent().toolTip(), self.parent().window())

View File

@ -0,0 +1,42 @@
# coding:utf-8
from PySide6.QtGui import QPainterPath, QPainter, QColor
from ..widgets.acrylic_label import AcrylicBrush
from ...common.style_sheet import isDarkTheme
class AcrylicWidget:
""" Acrylic widget """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.acrylicBrush = AcrylicBrush(self, 30)
def _updateAcrylicColor(self):
if isDarkTheme():
tintColor = QColor(32, 32, 32, 200)
luminosityColor = QColor(0, 0, 0, 0)
else:
tintColor = QColor(255, 255, 255, 180)
luminosityColor = QColor(255, 255, 255, 0)
self.acrylicBrush.tintColor = tintColor
self.acrylicBrush.luminosityColor = luminosityColor
def acrylicClipPath(self):
return QPainterPath()
def _drawAcrylic(self, painter: QPainter):
path = self.acrylicClipPath()
if not path.isEmpty():
self.acrylicBrush.clipPath = self.acrylicClipPath()
self._updateAcrylicColor()
self.acrylicBrush.paint()
def paintEvent(self, e):
painter = QPainter(self)
painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
self._drawAcrylic(painter)
super().paintEvent(e)