用 QFluentWidgets 美化 PySide6 介面
PyQt-Fluent-Widgets / QFluentWidgets 是一套 Fluent Design 風格的 Qt 元件庫。官方文件預設範例多半寫 PyQt5,但同一套 API 也有 PySide6 專用套件:PySide6-Fluent-Widgets。import 名稱一律是 qfluentwidgets。
結論:可以,而且很適合拿來「美化」PySide6——多數情況只要把 QPushButton 換成 PushButton,再呼叫 setTheme(),外觀就會明顯現代化。
版本對照(很重要)
| 你 用的 Qt binding | 要安裝的套件 |
|---|---|
| PyQt5 | PyQt-Fluent-Widgets |
| PyQt6 | PyQt6-Fluent-Widgets |
| PySide2 | PySide2-Fluent-Widgets |
| PySide6 | PySide6-Fluent-Widgets |
注意:
- 不要同時安裝 上述多個套件,它們的 Python package 名稱都是
qfluentwidgets,會互相覆寫。 - 不要混用 PySide 與 PyQt(例如程式用 PySide6,卻裝了 PyQt 版 Fluent),容易直接崩潰。
- 文件站:pyqt-fluent-widgets.readthedocs.io;安裝說明亦可參考 qfluentwidgets.com/pages/install。
授權為 GPLv3(開源版)。商業閉源產品需自行評估授權,或考慮官方 Pro 方案。
安裝(PySide6)
# 建議獨立虛擬環境
pip install PySide6
# lite:一般元件足夠
pip install PySide6-Fluent-Widgets -i https://pypi.org/simple/
# full:含 AcrylicLabel 等材質效果
pip install "PySide6-Fluent-Widgets[full]" -i https://pypi.org/simple/
用 uv:
uv add PySide6 "PySide6-Fluent-Widgets"
若出現 ImportError: cannot import name 'XXX' from 'qfluentwidgets',多半是版本過舊,改用官方 PyPI 來源重裝最新版。
較新的 PySide6 在 Windows 11 上偶有 QStyle 衝突導致樣式異常;官方建議可暫時鎖在例如 6.7.2 再回報問題。
核心用法:換元件名即可
QFluentWidgets 很多元件是「帶 Fluent 樣式的 Qt 控件」。API 與原生控件相近,學習成本低:
| 原生 Qt | QFluentWidgets |
|---|---|
QPushButton | PushButton / PrimaryPushButton |
QLabel | BodyLabel / TitleLabel / SubtitleLabel |
QLineEdit | LineEdit / SearchLineEdit |
QCheckBox | CheckBox |
QComboBox | ComboBox |
QSlider | Slider |
QProgressBar | ProgressBar / ProgressRing |
另外還有原生沒有、但桌面 App 很常用的:InfoBar、Flyout、MessageBox、NavigationInterface、FluentWindow、SettingCard 等。
最小美化範例
import sys
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
from qfluentwidgets import (
BodyLabel,
PushButton,
SwitchButton,
Theme,
setTheme,
)
class MyWindow(QWidget):
def __init__(self) -> None:
super().__init__()
self.setWindowTitle("My First Fluent App")
self.resize(400, 220)
self.label = BodyLabel("歡迎使用 PySide6-Fluent-Widgets", self)
self.button = PushButton("點擊我", self)
self.switch = SwitchButton(self)
self.button.clicked.connect(self.on_button_clicked)
self.switch.checkedChanged.connect(self.on_switch_toggled)
layout = QVBoxLayout(self)
layout.setSpacing(20)
layout.setContentsMargins(30, 30, 30, 30)
layout.addWidget(self.label)
layout.addWidget(self.button)
layout.addWidget(self.switch)
layout.addStretch(1)
def on_button_clicked(self) -> None:
self.label.setText("按鈕被點擊了!")
def on_switch_toggled(self, is_checked: bool) -> None:
self.label.setText(f"開關狀態:{'開啟' if is_checked else '關閉'}")
if __name__ == "__main__":
app = QApplication(sys.argv)
setTheme(Theme.DARK) # LIGHT / DARK / AUTO
window = MyWindow()
window.show()
sys.exit(app.exec())
你會看到圓角按鈕、懸停效果與切換動畫——不用自己寫一長串 QSS。
主題與主題色
亮/暗模式
from qfluentwidgets import Theme, setTheme
setTheme(Theme.LIGHT) # 淺色
setTheme(Theme.DARK) # 深色
setTheme(Theme.AUTO) # 跟隨系統(偵測不到則用淺色)
主題變更時,qconfig 會發出 themeChanged 訊號。細節見官方 Theme。
主題色
from qfluentwidgets import setThemeColor
setThemeColor("#0065d5") # 也可傳 QColor / Qt.GlobalColor / 色名如 "red"
自訂頁面 QSS 跟隨主題
若自訂視窗也要跟亮暗切換,可繼承 StyleSheetBase,把 light/dark 兩套 qss 分開放:
from enum import Enum
from qfluentwidgets import StyleSheetBase, Theme, qconfig
class StyleSheet(StyleSheetBase, Enum):
MAIN_WINDOW = "main_window"
def path(self, theme: Theme = Theme.AUTO) -> str:
theme = qconfig.theme if theme == Theme.AUTO else theme
return f"app/resource/qss/{theme.value.lower()}/{self.value}.qss"
# StyleSheet.MAIN_WINDOW.apply(self)
小幅覆寫單一控件樣式可用 setCustomStyleSheet(widget, light_qss, dark_qss)。
FluentWindow:側邊導航多頁 App
多數工具型桌面 App 需要「左側導航 + 右側內容」。FluentWindow 已幫你組好骨架,用 addSubInterface() 掛頁面即可。
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
from qfluentwidgets import (
BodyLabel,
FluentIcon,
FluentWindow,
NavigationItemPosition,
Theme,
setTheme,
)
class HomePage(QWidget):
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)
self.setObjectName("home")
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignCenter)
layout.addWidget(BodyLabel("這是首頁", self))
class SettingsPage(QWidget):
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)
self.setObjectName("settings")
layout = QVBoxLayout(self)
layout.setAlignment(Qt.AlignCenter)
layout.addWidget(BodyLabel("這是設定頁", self))
class MainWindow(FluentWindow):
def __init__(self) -> None:
super().__init__()
self.setWindowTitle("多頁面應用")
self.resize(900, 700)
self.home_page = HomePage(self)
self.settings_page = SettingsPage(self)
self.addSubInterface(self.home_page, FluentIcon.HOME, "首頁")
self.addSubInterface(
self.settings_page,
FluentIcon.SETTING,
"設定",
position=NavigationItemPosition.BOTTOM,
)
if __name__ == "__main__":
app = QApplication(sys.argv)
setTheme(Theme.AUTO)
window = MainWindow()
window.show()
sys.exit(app.exec())
導航結構細節(NavigationInterface、routeKey、顯示模式 EXPAND / COMPACT / MENU / MINIMAL)見官方 Navigation。
常見美化場景速查
| 需求 | 建議元件/做法 |
|---|---|
| 主要 CTA 按鈕 | PrimaryPushButton |
| 一般操作按鈕 | PushButton / ToolButton |
| 成功/錯誤提示 | InfoBar.success() / InfoBar.error() |
| 設定頁 | SettingCard + SettingCardGroup |
| 搜尋框 | SearchLineEdit |
| 開關 | SwitchButton |
| 無邊框視窗 | 套件內建 frameless 相關元件(搭配 FluentWindow) |
| 圖示 | FluentIcon(內建大量 SVG 風格圖示) |
Gallery 分類(官方文件 Gallery):Basic input、Dialogs、Menus、Flow Layout、Navigation、Status & info、Material。
跑官方範例(需 clone 對應分支原始碼):
# PySide6 分支:https://github.com/zhiyiYo/PyQt-Fluent-Widgets/tree/PySide6
cd examples/gallery
python demo.py
從「醜的原生視窗」遷移的實務步驟
- 確認只 裝
PySide6+PySide6-Fluent-Widgets。 - 全域
setTheme(Theme.AUTO)或Theme.DARK。 - 把頁面上的原生控件改成
qfluentwidgets同名/對應控件。 - 主殼改成
FluentWindow,子頁用addSubInterface。 - 回饋訊息改用
InfoBar/MessageBox,少用系統QMessageBox(除非刻意要原生)。 - 需要品牌色時再
setThemeColor(...)。
多數按鈕、輸入框、標籤「只改 class 名稱」就能吃到樣式;真正要讀文件的通常是 FluentWindow、InfoBar、Pivot、NavigationInterface 這類組裝型元件。
常見問題
樣式沒生效?
確認 import 的是 qfluentwidgets.PushButton,不是 PySide6.QtWidgets.QPushButton。
和另一個 Fluent 套件衝突?
卸掉其他 *-Fluent-Widgets,只留 PySide6 版後重裝。
想用 Qt Designer?
官方文件有 Designer(Promote widget / Client 外掛)。做法是在 .ui 裡放基底控件,再 promote 成 qfluentwidgets 類別。
設定頁怎麼做?
見官方 Settings:qconfig + 各種 SettingCard。