# coding:utf-8 import sys from typing import Union from PyQt6.QtCore import QRect, QSize, Qt from PyQt6.QtGui import QColor, QIcon, QPainter, QPixmap from PyQt6.QtWidgets import QApplication, QHBoxLayout, QLabel, QVBoxLayout, QWidget from qfluentwidgets import MSFluentWindow, NavigationItemPosition, setTheme, Theme from qfluentwidgets.common.config import qconfig from qfluentwidgets.common.icon import FluentIconBase from qfluentwidgets.common.router import qrouter from qfluentwidgets.common.style_sheet import ( FluentStyleSheet, isDarkTheme, ) from qfluentwidgets.components.navigation import ( NavigationTreeWidget ) from qfluentwidgets.components.widgets.frameless_window import FramelessWindow from app.view.widgets.stacked_widget import StackedWidget class CustomFluentWindow(MSFluentWindow): """自定义的Fluent窗口,基于MSFluentWindow实现""" def __init__(self, parent=None): super().__init__(parent) # 背景相关设置 self._isMicaEnabled = False self._lightBackgroundColor = QColor(240, 244, 249) self._darkBackgroundColor = QColor(32, 32, 32) self._backgroundPixmap = None # 存储背景图片 self._backgroundOpacity = 1.0 # 背景图片不透明度 # 启用mica效果 self.setMicaEffectEnabled(True) # 连接主题变化信号 qconfig.themeChangedFinished.connect(self._onThemeChangedFinished) def addSubInterface( self, interface: QWidget, icon: Union[FluentIconBase, QIcon, str], text: str, selectedIcon: Union[FluentIconBase, QIcon, str] = None, position=NavigationItemPosition.TOP, parent=None, isTransparent=False, ) -> NavigationTreeWidget: """添加子界面 Parameters ---------- interface: QWidget 要添加的子界面 icon: FluentIconBase | QIcon | str 导航项的图标 selectedIcon: FluentIconBase | QIcon | str 选中状态下的图标 text: str 导航项的文本 position: NavigationItemPosition 导航项的位置 parent: QWidget 导航项的父项 isTransparent: bool 是否使用透明背景 """ if not interface.objectName(): raise ValueError("The object name of `interface` can't be empty string.") interface.setProperty("isStackedTransparent", isTransparent) # 使用MSFluentWindow的addSubInterface方法 if selectedIcon: item = super().addSubInterface(interface, icon, text, selectedIcon, position) else: item = super().addSubInterface(interface, icon, text, position=position) return item def removeInterface(self, interface, isDelete=False): """移除子界面 Parameters ---------- interface: QWidget 要移除的子界面 isDelete: bool 是否删除子界面 """ # 从stackedWidget中移除 self.stackedWidget.removeWidget(interface) interface.hide() # 从导航中移除 self.navigationInterface.removeWidget(interface.objectName()) if isDelete: interface.deleteLater() def setCustomBackgroundColor(self, light, dark): """设置自定义背景颜色 Parameters ---------- light, dark: QColor | Qt.GlobalColor | str 亮/暗主题模式下的背景颜色 """ self._lightBackgroundColor = QColor(light) self._darkBackgroundColor = QColor(dark) self._updateBackgroundColor() def setBackgroundImage(self, imagePath: str, opacity: float = 1.0): """设置背景图片 Parameters ---------- imagePath: str 背景图片路径 opacity: float 背景图片不透明度,范围0.0-1.0 """ # 如果启用了Mica效果,先禁用 if self._isMicaEnabled: self.setMicaEffectEnabled(False) self._backgroundPixmap = QPixmap(imagePath) if self._backgroundPixmap.isNull(): print(f"无法加载背景图片: {imagePath}") return self._backgroundOpacity = max(0.0, min(1.0, opacity)) # 确保在0-1范围内 self.update() # 触发重绘 def removeBackgroundImage(self): """移除背景图片""" self._backgroundPixmap = None self.update() def _normalBackgroundColor(self): if not self._isMicaEnabled: return ( self._darkBackgroundColor if isDarkTheme() else self._lightBackgroundColor ) return QColor(0, 0, 0, 0) def _onThemeChangedFinished(self): if self._isMicaEnabled: # MSFluentWindow已经处理了Mica效果,这里只需要确保背景颜色正确 self.setBackgroundColor(self._normalBackgroundColor()) def paintEvent(self, e): # 先调用父类的绘制方法 super().paintEvent(e) # 如果有背景图片,绘制背景图片 if self._backgroundPixmap and not self._backgroundPixmap.isNull(): painter = QPainter(self) # 设置不透明度 painter.setOpacity(self._backgroundOpacity) # 缩放图片以适应窗口大小 scaled_pixmap = self._backgroundPixmap.scaled( self.size(), Qt.AspectRatioMode.IgnoreAspectRatio, Qt.TransformationMode.SmoothTransformation ) painter.drawPixmap(0, 0, scaled_pixmap) def setMicaEffectEnabled(self, isEnabled: bool): """设置是否启用mica效果,仅在Win11上可用""" if sys.platform != "win32" or sys.getwindowsversion().build < 22000: self._isMicaEnabled = False return self._isMicaEnabled = isEnabled if isEnabled: # 启用Mica效果时移除背景图片 self.removeBackgroundImage() # MSFluentWindow自带Mica效果支持 self.windowEffect.setMicaEffect(self.winId(), isDarkTheme()) else: self.windowEffect.removeBackgroundEffect(self.winId()) self.setBackgroundColor(self._normalBackgroundColor()) def isMicaEffectEnabled(self): """获取是否启用了mica效果""" return self._isMicaEnabled def resizeEvent(self, e): super().resizeEvent(e) # 确保标题栏正确显示 if hasattr(self, 'titleBar'): self.titleBar.raise_()