文章目錄
- 1. 引言
- 2. PySide6 簡介與安裝
- 2.1 什么是PySide6
- 2.2 PySide6 vs. PyQt6
- 2.3 安裝PySide6
- 2.4 開發環境配置建議
- 3. Qt 設計原理
- 3.1 Qt對象模型
- 3.2 信號與槽機制
- 3.3 Qt坐標系統
- 3.4 Qt樣式表(QSS)
- 4. 創建第一個應用
- 4.1 基本應用結構
- 4.2 主窗口與應用生命周期
- 4.3 使用面向對象方法重構
- 4.4 簡單交互示例
- 5. 常用控件詳解
- 5.1 按鈕與輸入控件
- 5.1.1 QPushButton (按鈕)
- 5.1.2 QLabel (標簽)
- 5.1.3 QLineEdit (單行文本框)
- 5.1.4 QTextEdit (多行文本編輯器)
- 5.1.5 QCheckBox (復選框)
- 5.1.6 QRadioButton (單選按鈕)
- 5.1.7 QComboBox (下拉列表)
- 5.1.8 QSlider (滑塊)
- 5.1.9 QSpinBox (數字輸入)
- 5.2 容器控件
- 5.2.1 QGroupBox (分組框)
- 5.2.2 QTabWidget (選項卡)
- 5.2.3 QScrollArea (滾動區域)
- 5.2.4 QStackedWidget (堆疊部件)
- 5.3 對話框
- 5.3.1 QMessageBox (消息框)
- 5.3.2 QFileDialog (文件對話框)
- 5.3.3 QInputDialog (輸入對話框)
- 5.3.4 QColorDialog (顏色對話框)
- 5.3.5 QFontDialog (字體對話框)
- 5.4 菜單與工具欄
- 5.4.1 QMenuBar 和 QMenu (菜單欄和菜單)
- 5.4.2 QToolBar (工具欄)
- 5.4.3 QStatusBar (狀態欄)
- 6. 布局管理系統
- 6.1 QVBoxLayout (垂直布局)
- 6.2 QHBoxLayout (水平布局)
- 6.3 QGridLayout (網格布局)
- 6.4 QFormLayout (表單布局)
- 6.5 嵌套布局
- 6.6 高級布局技巧
- 6.6.1 布局與控件的尺寸策略
- 6.6.2 間隔器的使用
- 6.6.3 邊距和間距
- 6.6.4 對齊方式
- 7. 信號與槽機制
- 7.1 基礎概念
- 7.2 信號種類與發射
- 7.2.1 內置信號
- 7.2.2 自定義信號
- 7.3 槽的種類與連接
- 7.3.1 函數槽
- 7.3.2 方法槽
- 7.3.3 Lambda表達式
- 7.3.4 部分函數
- 7.4 信號連接管理
- 7.4.1 斷開連接
- 7.4.2 阻塞信號
- 7.4.3 連接類型
- 7.5 實例:自定義信號與槽
- 8. 樣式與主題定制
- 8.1 基本樣式屬性
- 8.2 Qt樣式表(QSS)
- 8.3 常用樣式屬性
- 8.3.1 背景與前景
- 8.3.2 邊框與輪廓
- 8.3.3 文本格式
- 8.3.4 尺寸與布局
- 8.4 狀態相關樣式
- 8.5 自定義應用主題
- 8.6 動態切換主題
- 9. Qt Designer 可視化設計
- 9.1 Qt Designer 基礎
- 9.2 創建界面
- 9.3 加載.ui文件
- 9.3.1 使用QUiLoader
- 9.3.2 使用uic編譯
- 9.4 多界面管理
- 9.5 表單繼承
- 9.6 資源文件管理
- 9.6.1 創建資源文件
- 9.6.2 編譯資源文件
- 9.6.3 使用資源
- 10. 多線程與并發處理
- 10.1 QThread 基本用法
- 10.2 QRunnable 和線程池
- 11. 數據持久化
- 11.1 QSettings
- 11.2 SQLite數據庫集成
- 12. 高級圖形與動畫
- 12.1 自定義繪圖
- 12.2 動畫效果
- 13. 國際化與本地化
- 13.1 使用QTranslator
- 13.2 使用tr()進行文本標記
- 14. 實戰項目開發
- 14.1 應用架構設計
- 14.2 項目質量保證
- 15. 打包與發布
- 15.1 使用PyInstaller打包
- 15.2 使用cx_Freeze打包
- 16. 總結
1. 引言
在Python應用程序開發領域,圖形用戶界面(GUI)是提升用戶體驗的關鍵因素。PySide6作為Qt框架的Python綁定,為開發者提供了強大而靈活的GUI開發工具。本文將全面介紹PySide6的核心概念、組件和最佳實踐,幫助讀者快速掌握這一現代GUI開發技術。
無論你是GUI開發新手,還是想從其他GUI框架遷移到PySide6,本指南都將為你提供系統化的學習路徑,幫助你構建專業、美觀且功能強大的Python桌面應用。
2. PySide6 簡介與安裝
2.1 什么是PySide6
PySide6是Qt for Python項目的一部分,它提供了對Qt 6.0的官方Python綁定。Qt是一個跨平臺的C++應用程序開發框架,以其豐富的UI組件、優秀的性能和跨平臺能力而聞名。PySide6繼承了Qt的這些優點,同時結合了Python的簡潔性和開發效率。
PySide6的主要特點包括:
- 跨平臺兼容性:支持Windows、macOS、Linux等多種操作系統
- 豐富的UI組件庫:提供200多種現成的UI控件
- 信號與槽機制:獨特的事件處理方式
- 強大的圖形能力:支持2D/3D圖形渲染
- 內置的國際化支持:輕松實現多語言界面
- Qt Designer集成:可視化界面設計工具
2.2 PySide6 vs. PyQt6
PySide6和PyQt6都是Qt的Python綁定,功能幾乎相同,主要區別在于許可證:
- PySide6:使用更為寬松的LGPL許可證
- PyQt6:使用GPL許可證,商業應用可能需要購買商業許可
代碼方面,兩者差異很小,通常只需修改導入語句就能在兩者間切換。
2.3 安裝PySide6
使用pip安裝PySide6非常簡單:
pip install pyside6
驗證安裝是否成功:
import PySide6.QtCore
print(PySide6.__version__)
print(PySide6.QtCore.__version__)
對于Anaconda用戶,也可以使用conda安裝:
conda install -c conda-forge pyside6
2.4 開發環境配置建議
推薦的PySide6開發環境:
- Python:3.6+(推薦3.8+)
- IDE:
- PyCharm(專業版提供更好的Qt支持)
- Visual Studio Code(配合Python和Qt相關插件)
- 調試工具:Qt Designer(界面設計)和Qt Creator(完整IDE)
- 版本控制:Git(特別是處理.ui文件時)
3. Qt 設計原理
3.1 Qt對象模型
Qt對象模型是理解PySide6的基礎,它基于以下幾個核心概念:
- QObject:幾乎所有Qt類的基類,提供信號與槽機制的基礎
- 元對象系統:提供運行時類型信息、動態屬性系統等功能
- 屬性系統:允許在對象上動態添加屬性
- 事件系統:處理用戶交互和系統事件
例如,一個基本的QObject派生類:
from PySide6.QtCore import QObject, Signal, Slotclass MyObject(QObject):# 定義信號valueChanged = Signal(int)def __init__(self):super().__init__()self._value = 0# 定義槽@Slot(int)def setValue(self, value):if self._value != value:self._value = valueself.valueChanged.emit(value)
3.2 信號與槽機制
信號與槽是Qt最獨特的特性之一,它提供了一種類型安全的回調機制:
- 信號(Signal):對象發出的通知,表示某些事件已經發生
- 槽(Slot):響應信號的函數或方法
信號與槽的連接:
from PySide6.QtWidgets import QApplication, QPushButtonapp = QApplication([])
button = QPushButton("Click me")# 連接按鈕的clicked信號到自定義函數
def onButtonClicked():print("Button clicked!")button.clicked.connect(onButtonClicked)
button.show()
app.exec()
3.3 Qt坐標系統
Qt使用自己的坐標系統,原點(0,0)通常位于組件的左上角:
- 物理坐標:實際設備上的像素位置
- 邏輯坐標:考慮DPI縮放的虛擬坐標
- 設備獨立像素:跨平臺一致性的關鍵
3.4 Qt樣式表(QSS)
Qt樣式表類似于CSS,用于自定義UI外觀:
button = QPushButton("Styled Button")
button.setStyleSheet("""QPushButton {background-color: #4CAF50;border: none;color: white;padding: 8px 16px;border-radius: 4px;font-size: 14px;}QPushButton:hover {background-color: #45a049;}QPushButton:pressed {background-color: #3d8b40;}
""")
4. 創建第一個應用
4.1 基本應用結構
所有PySide6應用都遵循類似的基本結構:
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout# 1. 創建應用實例
app = QApplication(sys.argv)# 2. 創建主窗口
window = QWidget()
window.setWindowTitle("My First PySide6 App")
window.setGeometry(100, 100, 400, 200) # x, y, width, height# 3. 創建布局和組件
layout = QVBoxLayout()
label = QLabel("Hello, PySide6!")
layout.addWidget(label)
window.setLayout(layout)# 4. 顯示窗口
window.show()# 5. 執行應用
sys.exit(app.exec())
4.2 主窗口與應用生命周期
應用的生命周期管理是GUI程序的重要部分:
- 初始化:創建QApplication實例和主窗口
- 事件循環:app.exec()啟動事件循環,處理用戶輸入和系統事件
- 終止:事件循環結束,應用關閉
4.3 使用面向對象方法重構
更實用的做法是將應用窗口封裝為類:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidgetclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("OOP PySide6 App")self.setGeometry(100, 100, 400, 200)self.setupUI()def setupUI(self):# 創建中央部件和布局central_widget = QWidget()layout = QVBoxLayout(central_widget)# 添加控件label = QLabel("Hello from OOP PySide6 App!")layout.addWidget(label)# 設置中央部件self.setCentralWidget(central_widget)if __name__ == "__main__":app = QApplication(sys.argv)window = MainWindow()window.show()sys.exit(app.exec())
4.4 簡單交互示例
添加按鈕和事件響應,創建一個簡單的交互應用:
import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton
)
from PySide6.QtCore import Qtclass CounterApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Counter App")self.setGeometry(100, 100, 300, 200)# 計數器初始值self.counter = 0# 設置UIself.setupUI()def setupUI(self):# 中央部件和布局central_widget = QWidget()layout = QVBoxLayout(central_widget)# 顯示計數的標簽self.label = QLabel(f"Count: {self.counter}")self.label.setAlignment(Qt.AlignCenter)layout.addWidget(self.label)# 增加按鈕increment_button = QPushButton("Increment")increment_button.clicked.connect(self.increment)layout.addWidget(increment_button)# 重置按鈕reset_button = QPushButton("Reset")reset_button.clicked.connect(self.reset)layout.addWidget(reset_button)# 設置中央部件self.setCentralWidget(central_widget)def increment(self):self.counter += 1self.label.setText(f"Count: {self.counter}")def reset(self):self.counter = 0self.label.setText(f"Count: {self.counter}")if __name__ == "__main__":app = QApplication(sys.argv)window = CounterApp()window.show()sys.exit(app.exec())
這個簡單的計數器應用展示了PySide6的基本功能:窗口創建、控件布局、事件處理和狀態管理。
5. 常用控件詳解
PySide6提供了豐富的控件庫,以下是最常用的控件及其用法。
5.1 按鈕與輸入控件
5.1.1 QPushButton (按鈕)
最基本的交互控件:
from PySide6.QtWidgets import QPushButton# 創建按鈕
button = QPushButton("Click Me")# 設置快捷鍵
button.setShortcut("Ctrl+C")# 連接點擊事件
button.clicked.connect(on_button_clicked)# 設置按鈕圖標
from PySide6.QtGui import QIcon
button.setIcon(QIcon("path/to/icon.png"))
5.1.2 QLabel (標簽)
用于顯示文本或圖像:
from PySide6.QtWidgets import QLabel
from PySide6.QtGui import QPixmap
from PySide6.QtCore import Qt# 文本標簽
text_label = QLabel("Hello World")
text_label.setAlignment(Qt.AlignCenter) # 文本居中# 圖像標簽
image_label = QLabel()
pixmap = QPixmap("image.jpg")
image_label.setPixmap(pixmap.scaled(200, 200, Qt.KeepAspectRatio))
5.1.3 QLineEdit (單行文本框)
用戶文本輸入控件:
from PySide6.QtWidgets import QLineEdit# 創建文本框
text_input = QLineEdit()
text_input.setPlaceholderText("Enter your name") # 設置占位文本
text_input.setMaxLength(50) # 設置最大長度# 獲取文本
def get_text():print(text_input.text())# 文本變化信號
text_input.textChanged.connect(lambda text: print(f"Text changed: {text}"))
5.1.4 QTextEdit (多行文本編輯器)
用于多行文本輸入和顯示:
from PySide6.QtWidgets import QTextEdit# 創建多行文本編輯器
text_editor = QTextEdit()
text_editor.setPlaceholderText("Write your message here...")# 設置和獲取文本
text_editor.setText("Initial text")
content = text_editor.toPlainText() # 獲取純文本
html_content = text_editor.toHtml() # 獲取HTML格式內容# 文本變化信號
text_editor.textChanged.connect(lambda: print("Text changed"))
5.1.5 QCheckBox (復選框)
用于布爾選擇:
from PySide6.QtWidgets import QCheckBox# 創建復選框
checkbox = QCheckBox("Enable feature")
checkbox.setChecked(True) # 默認選中# 狀態變化信號
checkbox.stateChanged.connect(lambda state: print(f"State: {state}"))
5.1.6 QRadioButton (單選按鈕)
用于互斥選擇:
from PySide6.QtWidgets import QRadioButton, QButtonGroup, QVBoxLayout, QWidget# 需要用QButtonGroup組織多個單選按鈕
group = QButtonGroup()# 創建單選按鈕
radio1 = QRadioButton("Option 1")
radio2 = QRadioButton("Option 2")
radio3 = QRadioButton("Option 3")# 添加到按鈕組
group.addButton(radio1, 1) # 第二個參數是ID
group.addButton(radio2, 2)
group.addButton(radio3, 3)# 默認選中
radio1.setChecked(True)# 信號連接
group.buttonClicked.connect(lambda button: print(f"Selected: {button.text()}"))
5.1.7 QComboBox (下拉列表)
用于從預定義列表中選擇:
from PySide6.QtWidgets import QComboBox# 創建下拉列表
combo = QComboBox()
combo.addItem("Option 1")
combo.addItem("Option 2")
combo.addItem("Option 3")# 帶數據的項
combo.addItem("Red", "#FF0000")
combo.addItem("Green", "#00FF00")
combo.addItem("Blue", "#0000FF")# 獲取選擇
def get_selection():index = combo.currentIndex()text = combo.currentText()data = combo.currentData()print(f"Selected: {text} at index {index} with data {data}")# 選擇變化信號
combo.currentIndexChanged.connect(get_selection)
5.1.8 QSlider (滑塊)
用于范圍值選擇:
from PySide6.QtWidgets import QSlider
from PySide6.QtCore import Qt# 創建水平滑塊
slider = QSlider(Qt.Horizontal)
slider.setMinimum(0)
slider.setMaximum(100)
slider.setValue(50) # 初始值
slider.setTickPosition(QSlider.TicksBelow) # 顯示刻度
slider.setTickInterval(10) # 刻度間隔# 值變化信號
slider.valueChanged.connect(lambda value: print(f"Value: {value}"))
5.1.9 QSpinBox (數字輸入)
用于數字輸入:
from PySide6.QtWidgets import QSpinBox# 創建數字輸入框
spin = QSpinBox()
spin.setMinimum(0)
spin.setMaximum(100)
spin.setValue(50) # 初始值
spin.setSingleStep(5) # 步長
spin.setPrefix("$") # 前綴
spin.setSuffix(" units") # 后綴# 值變化信號
spin.valueChanged.connect(lambda value: print(f"Value: {value}"))
5.2 容器控件
5.2.1 QGroupBox (分組框)
用于組織相關控件:
from PySide6.QtWidgets import QGroupBox, QVBoxLayout, QRadioButton# 創建分組框
group_box = QGroupBox("Select Option")layout = QVBoxLayout()# 添加內容
radio1 = QRadioButton("Option 1")
radio2 = QRadioButton("Option 2")
layout.addWidget(radio1)
layout.addWidget(radio2)# 設置布局
group_box.setLayout(layout)
5.2.2 QTabWidget (選項卡)
創建選項卡界面:
from PySide6.QtWidgets import QTabWidget, QWidget, QVBoxLayout, QLabel# 創建選項卡部件
tabs = QTabWidget()# 創建第一個選項卡
tab1 = QWidget()
tab1_layout = QVBoxLayout(tab1)
tab1_layout.addWidget(QLabel("This is the first tab"))# 創建第二個選項卡
tab2 = QWidget()
tab2_layout = QVBoxLayout(tab2)
tab2_layout.addWidget(QLabel("This is the second tab"))# 添加選項卡
tabs.addTab(tab1, "Tab 1")
tabs.addTab(tab2, "Tab 2")# 選項卡切換信號
tabs.currentChanged.connect(lambda index: print(f"Tab switched to: {index}"))
5.2.3 QScrollArea (滾動區域)
為大型內容提供滾動功能:
from PySide6.QtWidgets import QScrollArea, QWidget, QVBoxLayout, QLabel# 創建滾動區域
scroll = QScrollArea()
scroll.setWidgetResizable(True) # 允許內容部件調整大小# 創建內容部件
content = QWidget()
layout = QVBoxLayout(content)# 添加大量內容
for i in range(100):layout.addWidget(QLabel(f"Item {i}"))# 設置內容部件
scroll.setWidget(content)
5.2.4 QStackedWidget (堆疊部件)
在同一區域顯示多個部件,但一次只顯示一個:
from PySide6.QtWidgets import QStackedWidget, QWidget, QVBoxLayout, QLabel, QPushButton# 創建堆疊部件
stack = QStackedWidget()# 創建頁面
page1 = QWidget()
page1_layout = QVBoxLayout(page1)
page1_layout.addWidget(QLabel("This is page 1"))page2 = QWidget()
page2_layout = QVBoxLayout(page2)
page2_layout.addWidget(QLabel("This is page 2"))# 添加頁面
stack.addWidget(page1)
stack.addWidget(page2)# 切換頁面
stack.setCurrentIndex(0) # 顯示第一個頁面# 切換按鈕
def switch_page():current = stack.currentIndex()stack.setCurrentIndex(1 - current) # 在0和1之間切換button = QPushButton("Switch Page")
button.clicked.connect(switch_page)
5.3 對話框
5.3.1 QMessageBox (消息框)
用于顯示消息和獲取用戶響應:
from PySide6.QtWidgets import QMessageBox# 信息對話框
def show_info():QMessageBox.information(None, # 父窗口"Information", # 標題"Operation completed successfully.", # 消息QMessageBox.Ok # 按鈕)# 警告對話框
def show_warning():QMessageBox.warning(None,"Warning","This action might be dangerous.",QMessageBox.Ok | QMessageBox.Cancel)# 錯誤對話框
def show_error():QMessageBox.critical(None,"Error","A critical error has occurred.",QMessageBox.Ok)# 問題對話框
def ask_question():result = QMessageBox.question(None,"Confirmation","Are you sure you want to proceed?",QMessageBox.Yes | QMessageBox.No)if result == QMessageBox.Yes:print("User confirmed")else:print("User cancelled")
5.3.2 QFileDialog (文件對話框)
用于文件選擇:
from PySide6.QtWidgets import QFileDialog# 打開文件對話框
def open_file():file_path, _ = QFileDialog.getOpenFileName(None, # 父窗口"Open File", # 標題"", # 起始目錄"Text Files (*.txt);;All Files (*)" # 文件過濾器)if file_path:print(f"Selected file: {file_path}")# 保存文件對話框
def save_file():file_path, _ = QFileDialog.getSaveFileName(None,"Save File","","Text Files (*.txt);;All Files (*)")if file_path:print(f"Save to: {file_path}")# 選擇目錄對話框
def select_directory():directory = QFileDialog.getExistingDirectory(None,"Select Directory")if directory:print(f"Selected directory: {directory}")# 多文件選擇
def select_multiple_files():file_paths, _ = QFileDialog.getOpenFileNames(None,"Select Files","","Images (*.png *.jpg);;All Files (*)")if file_paths:print(f"Selected {len(file_paths)} files")
5.3.3 QInputDialog (輸入對話框)
用于獲取用戶輸入:
from PySide6.QtWidgets import QInputDialog# 獲取文本輸入
def get_text_input():text, ok = QInputDialog.getText(None, # 父窗口"Input", # 標題"Enter your name:" # 提示)if ok and text:print(f"User entered: {text}")# 獲取數字輸入
def get_int_input():number, ok = QInputDialog.getInt(None,"Input","Enter your age:",25, # 默認值0, # 最小值120 # 最大值)if ok:print(f"User entered: {number}")# 獲取下拉選擇
def get_item_selection():items = ["Red", "Green", "Blue", "Yellow"]item, ok = QInputDialog.getItem(None,"Select Color","Choose your favorite color:",items,0, # 默認選擇索引False # 是否可編輯)if ok and item:print(f"User selected: {item}")
5.3.4 QColorDialog (顏色對話框)
用于顏色選擇:
from PySide6.QtWidgets import QColorDialog
from PySide6.QtGui import QColor# 顏色選擇對話框
def choose_color():color = QColorDialog.getColor(QColor(255, 0, 0), # 初始顏色None, # 父窗口"Select Color" # 標題)if color.isValid():print(f"Selected color: RGB({color.red()}, {color.green()}, {color.blue()})")print(f"Hex: {color.name()}")
5.3.5 QFontDialog (字體對話框)
用于字體選擇:
from PySide6.QtWidgets import QFontDialog
from PySide6.QtGui import QFont# 字體選擇對話框
def choose_font():initial_font = QFont("Arial", 12)font, ok = QFontDialog.getFont(initial_font, # 初始字體None, # 父窗口"Select Font" # 標題)if ok:print(f"Selected font: {font.family()}, {font.pointSize()}pt")if font.bold():print("Font is bold")if font.italic():print("Font is italic")
5.4 菜單與工具欄
5.4.1 QMenuBar 和 QMenu (菜單欄和菜單)
創建應用菜單:
from PySide6.QtWidgets import QMainWindow, QMenuBar, QMenu, QAction
from PySide6.QtGui import QIcon, QKeySequenceclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Menu Example")self.setGeometry(100, 100, 800, 600)# 創建菜單欄menu_bar = self.menuBar()# 創建文件菜單file_menu = menu_bar.addMenu("&File")# 創建新建動作new_action = QAction(QIcon("new.png"), "&New", self)new_action.setShortcut(QKeySequence("Ctrl+N"))new_action.setStatusTip("Create a new file")new_action.triggered.connect(self.new_file)file_menu.addAction(new_action)# 創建打開動作open_action = QAction("&Open...", self)open_action.setShortcut(QKeySequence("Ctrl+O"))open_action.triggered.connect(self.open_file)file_menu.addAction(open_action)# 添加分隔符file_menu.addSeparator()# 創建退出動作exit_action = QAction("E&xit", self)exit_action.setShortcut(QKeySequence("Ctrl+Q"))exit_action.triggered.connect(self.close)file_menu.addAction(exit_action)# 創建編輯菜單edit_menu = menu_bar.addMenu("&Edit")# 添加子菜單format_menu = edit_menu.addMenu("&Format")format_menu.addAction("&Bold")format_menu.addAction("&Italic")def new_file(self):print("New file")def open_file(self):print("Open file")
5.4.2 QToolBar (工具欄)
創建工具欄:
from PySide6.QtWidgets import QMainWindow, QToolBar, QAction
from PySide6.QtGui import QIconclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Toolbar Example")# 創建工具欄toolbar = QToolBar("Main Toolbar")self.addToolBar(toolbar)# 添加動作new_action = QAction(QIcon("new.png"), "New", self)new_action.triggered.connect(self.new_file)toolbar.addAction(new_action)open_action = QAction(QIcon("open.png"), "Open", self)open_action.triggered.connect(self.open_file)toolbar.addAction(open_action)# 添加分隔符toolbar.addSeparator()# 添加可選擇的動作bold_action = QAction(QIcon("bold.png"), "Bold", self)bold_action.setCheckable(True) # 可選擇的bold_action.toggled.connect(lambda checked: print(f"Bold: {checked}"))toolbar.addAction(bold_action)def new_file(self):print("New file")def open_file(self):print("Open file")
5.4.3 QStatusBar (狀態欄)
添加狀態欄:
from PySide6.QtWidgets import QMainWindow, QStatusBar, QLabelclass MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("StatusBar Example")# 創建狀態欄status_bar = QStatusBar()self.setStatusBar(status_bar)# 添加永久消息status_bar.addPermanentWidget(QLabel("Ready"))# 顯示臨時消息(5秒)status_bar.showMessage("Loading...", 5000)
6. 布局管理系統
布局管理是GUI應用中非常重要的部分,PySide6提供了多種布局管理器來組織控件,使界面在不同大小的窗口下保持合理的排列。
6.1 QVBoxLayout (垂直布局)
控件從上到下垂直排列:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButtonwidget = QWidget()
layout = QVBoxLayout(widget) # 直接設置父部件# 添加控件
layout.addWidget(QPushButton("Button 1"))
layout.addWidget(QPushButton("Button 2"))
layout.addWidget(QPushButton("Button 3"))# 添加間距
layout.addSpacing(20) # 添加20像素的垂直間距# 設置控件之間的間距
layout.setSpacing(10) # 所有控件之間的間距為10像素# 設置邊距
layout.setContentsMargins(10, 10, 10, 10) # 左, 上, 右, 下
6.2 QHBoxLayout (水平布局)
控件從左到右水平排列:
from PySide6.QtWidgets import QWidget, QHBoxLayout, QPushButtonwidget = QWidget()
layout = QHBoxLayout(widget)# 添加控件
layout.addWidget(QPushButton("Left"))
layout.addWidget(QPushButton("Center"))
layout.addWidget(QPushButton("Right"))# 控制拉伸因子
# 當窗口大小變化時,各控件獲得不同比例的空間
layout = QHBoxLayout()
layout.addWidget(QPushButton("Small"), 1) # 拉伸因子為1
layout.addWidget(QPushButton("Medium"), 2) # 拉伸因子為2
layout.addWidget(QPushButton("Large"), 3) # 拉伸因子為3
6.3 QGridLayout (網格布局)
控件在二維網格中排列:
from PySide6.QtWidgets import QWidget, QGridLayout, QPushButton, QLabel, QLineEditwidget = QWidget()
layout = QGridLayout(widget)# 添加控件 - addWidget(widget, row, column, rowSpan, columnSpan)
layout.addWidget(QLabel("Name:"), 0, 0) # 第0行,第0列
layout.addWidget(QLineEdit(), 0, 1) # 第0行,第1列layout.addWidget(QLabel("Email:"), 1, 0) # 第1行,第0列
layout.addWidget(QLineEdit(), 1, 1) # 第1行,第1列# 跨越多行或多列
layout.addWidget(QPushButton("Submit"), 2, 0, 1, 2) # 第2行,跨越2列
6.4 QFormLayout (表單布局)
專門用于表單的布局管理器,自動處理標簽和字段的對齊:
from PySide6.QtWidgets import QWidget, QFormLayout, QLineEdit, QSpinBox, QComboBoxwidget = QWidget()
layout = QFormLayout(widget)# 添加行 - addRow(label, field)
layout.addRow("Name:", QLineEdit())
layout.addRow("Age:", QSpinBox())
layout.addRow("Country:", QComboBox())# 使用已有控件作為標簽
label = QLabel("Email:")
field = QLineEdit()
layout.addRow(label, field)# 僅添加標簽
layout.addRow("Contact Information")# 僅添加控件
layout.addRow(QPushButton("Submit"))
6.5 嵌套布局
布局可以嵌套,創建復雜的界面:
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTextEdit, QLabel
)class NestedLayoutExample(QWidget):def __init__(self):super().__init__()self.setWindowTitle("Nested Layouts")self.setGeometry(100, 100, 600, 400)# 主布局main_layout = QVBoxLayout(self)# 上部分:標題main_layout.addWidget(QLabel("Message Composer"))# 中部分:編輯區域main_layout.addWidget(QTextEdit())# 下部分:按鈕行(水平布局)button_layout = QHBoxLayout()button_layout.addWidget(QPushButton("Save Draft"))button_layout.addWidget(QPushButton("Discard"))button_layout.addStretch(1) # 添加可拉伸的空間,使按鈕靠左對齊button_layout.addWidget(QPushButton("Send"))# 將按鈕布局添加到主布局main_layout.addLayout(button_layout)
6.6 高級布局技巧
6.6.1 布局與控件的尺寸策略
控制控件在布局中的大小行為:
from PySide6.QtWidgets import QPushButton, QSizePolicybutton = QPushButton("Resizable Button")# 設置尺寸策略
button.setSizePolicy(QSizePolicy.Expanding, # 水平策略QSizePolicy.Fixed # 垂直策略
)# 可用的尺寸策略:
# - QSizePolicy.Fixed: 控件具有固定大小
# - QSizePolicy.Minimum: 控件的最小大小提示是其最小尺寸
# - QSizePolicy.Maximum: 控件的最大大小提示是其最大尺寸
# - QSizePolicy.Preferred: 控件可以擴展,但沒有必要
# - QSizePolicy.Expanding: 控件應該優先擴展
# - QSizePolicy.MinimumExpanding: 控件應該擴展,但可以縮小到最小大小
# - QSizePolicy.Ignored: 忽略控件的大小提示
6.6.2 間隔器的使用
使用彈性間隔器控制控件的位置:
from PySide6.QtWidgets import QHBoxLayout, QPushButton, QWidgetwidget = QWidget()
layout = QHBoxLayout(widget)# 添加一個按鈕靠左
layout.addWidget(QPushButton("Left"))# 添加可拉伸空間
layout.addStretch(1)# 添加一個按鈕靠右
layout.addWidget(QPushButton("Right"))# 結果:按鈕將分別靠左和靠右,中間有彈性空間
6.6.3 邊距和間距
調整布局的邊距和間距:
from PySide6.QtWidgets import QVBoxLayoutlayout = QVBoxLayout()# 設置所有邊距(左、上、右、下)
layout.setContentsMargins(10, 20, 10, 20)# 設置控件之間的間距
layout.setSpacing(15)
6.6.4 對齊方式
控制控件在布局中的對齊方式:
from PySide6.QtWidgets import QHBoxLayout, QPushButton
from PySide6.QtCore import Qtlayout = QHBoxLayout()# 創建按鈕
button = QPushButton("Aligned Button")# 添加控件時指定對齊方式
layout.addWidget(button, 0, Qt.AlignTop | Qt.AlignLeft)# 可用的對齊標志:
# - Qt.AlignLeft: 水平左對齊
# - Qt.AlignRight: 水平右對齊
# - Qt.AlignHCenter: 水平居中對齊
# - Qt.AlignTop: 垂直頂部對齊
# - Qt.AlignBottom: 垂直底部對齊
# - Qt.AlignVCenter: 垂直居中對齊
# - Qt.AlignCenter: 水平和垂直都居中對齊
7. 信號與槽機制
信號與槽是Qt/PySide6最為獨特的機制,它實現了組件之間的解耦通信,是理解和掌握PySide6開發的核心。
7.1 基礎概念
信號與槽的基本概念:
- 信號(Signal):在特定事件發生時發出的通知
- 槽(Slot):接收信號的函數或方法
- 連接(Connection):將信號連接到槽,建立事件響應關系
from PySide6.QtWidgets import QApplication, QPushButtonapp = QApplication([])
button = QPushButton("Click Me")# 信號:button的clicked信號
# 槽:print_message函數
def print_message():print("Button clicked!")# 連接:將clicked信號連接到print_message槽
button.clicked.connect(print_message)button.show()
app.exec()
7.2 信號種類與發射
7.2.1 內置信號
PySide6控件都有預定義的信號:
# 常見控件的內置信號
button.clicked.connect(handler) # 按鈕被點擊
checkbox.stateChanged.connect(handler) # 復選框狀態變化
spinbox.valueChanged.connect(handler) # 數值改變
lineedit.textChanged.connect(handler) # 文本改變
slider.valueChanged.connect(handler) # 滑塊值改變
7.2.2 自定義信號
在自定義類中定義信號:
from PySide6.QtCore import QObject, Signalclass Counter(QObject):# 定義一個無參數的信號countChanged = Signal()# 定義帶一個int參數的信號valueChanged = Signal(int)# 定義帶多個參數的信號rangeChanged = Signal(int, int)# 定義帶不同類型參數的多個重載dataChanged = Signal([int], [str])def __init__(self):super().__init__()self._value = 0def setValue(self, value):if self._value != value:self._value = value# 發射信號self.valueChanged.emit(value)self.countChanged.emit()
使用自定義信號:
# 創建實例
counter = Counter()# 連接信號
counter.valueChanged.connect(lambda val: print(f"Value changed to: {val}"))
counter.countChanged.connect(lambda: print("Count changed"))# 觸發信號
counter.setValue(10) # 輸出 "Value changed to: 10" 和 "Count changed"
7.3 槽的種類與連接
7.3.1 函數槽
使用普通函數作為槽:
def handle_click():print("Button clicked")button.clicked.connect(handle_click)
7.3.2 方法槽
使用類方法作為槽:
class MyWidget(QWidget):def __init__(self):super().__init__()self.button = QPushButton("Click Me")self.button.clicked.connect(self.handle_click)layout = QVBoxLayout(self)layout.addWidget(self.button)def handle_click(self):print("Button clicked in class context")
7.3.3 Lambda表達式
使用Lambda表達式進行簡單的信號處理:
# 無參數
button.clicked.connect(lambda: print("Clicked!"))# 有參數
spinbox.valueChanged.connect(lambda value: print(f"Value: {value}"))# 調用其他函數并傳遞額外參數
slider.valueChanged.connect(lambda value: self.updateValue(value, "slider"))
7.3.4 部分函數
使用functools.partial創建部分應用的函數:
from functools import partialdef handle_value(source, value):print(f"Value from {source}: {value}")# 連接到部分應用的函數
slider1.valueChanged.connect(partial(handle_value, "slider1"))
slider2.valueChanged.connect(partial(handle_value, "slider2"))
7.4 信號連接管理
7.4.1 斷開連接
斷開信號與槽的連接:
# 斷開特定槽
button.clicked.disconnect(specific_handler)# 斷開所有連接到該信號的槽
button.clicked.disconnect()
7.4.2 阻塞信號
臨時阻止信號發射:
# 阻塞所有信號
widget.blockSignals(True)# 進行一些不需要觸發信號的操作
widget.setValue(100)# 恢復信號
widget.blockSignals(False)
7.4.3 連接類型
Qt提供了不同類型的信號連接:
from PySide6.QtCore import Qt# 默認連接 - 如果信號和槽在同一線程,直接調用;否則排隊
button.clicked.connect(handler)# 直接連接 - 信號發射時立即調用槽,無論線程
button.clicked.connect(handler, Qt.DirectConnection)# 隊列連接 - 總是將調用排隊,即使在同一線程
button.clicked.connect(handler, Qt.QueuedConnection)# 阻塞隊列連接 - 如果在不同線程,將阻塞直到槽返回
button.clicked.connect(handler, Qt.BlockingQueuedConnection)
7.5 實例:自定義信號與槽
創建一個帶有自定義信號與槽的溫度轉換器:
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,QLabel, QDoubleSpinBox, QComboBox
)
from PySide6.QtCore import Signal, Slotclass TemperatureConverter(QWidget):# 定義信號temperatureChanged = Signal(float, str)def __init__(self):super().__init__()self.setWindowTitle("Temperature Converter")self.initUI()self.setupConnections()def initUI(self):main_layout = QVBoxLayout(self)# 創建輸入布局input_layout = QHBoxLayout()self.temp_spinbox = QDoubleSpinBox()self.temp_spinbox.setRange(-273.15, 1000000)self.temp_spinbox.setValue(0)self.temp_spinbox.setDecimals(2)self.unit_combo = QComboBox()self.unit_combo.addItems(["Celsius", "Fahrenheit", "Kelvin"])input_layout.addWidget(self.temp_spinbox)input_layout.addWidget(self.unit_combo)main_layout.addLayout(input_layout)# 創建輸出布局self.result_layout = QVBoxLayout()self.celsius_label = QLabel("0.00 °C")self.fahrenheit_label = QLabel("32.00 °F")self.kelvin_label = QLabel("273.15 K")self.result_layout.addWidget(self.celsius_label)self.result_layout.addWidget(self.fahrenheit_label)self.result_layout.addWidget(self.kelvin_label)main_layout.addLayout(self.result_layout)def setupConnections(self):# 連接控件信號到自定義槽self.temp_spinbox.valueChanged.connect(self.onTemperatureInput)self.unit_combo.currentTextChanged.connect(self.onUnitChanged)# 連接自定義信號到自定義槽self.temperatureChanged.connect(self.updateTemperatures)@Slot(float)def onTemperatureInput(self, value):unit = self.unit_combo.currentText()self.temperatureChanged.emit(value, unit)@Slot(str)def onUnitChanged(self, unit):value = self.temp_spinbox.value()self.temperatureChanged.emit(value, unit)@Slot(float, str)def updateTemperatures(self, value, unit):if unit == "Celsius":celsius = valuefahrenheit = celsius * 9/5 + 32kelvin = celsius + 273.15elif unit == "Fahrenheit":fahrenheit = valuecelsius = (fahrenheit - 32) * 5/9kelvin = celsius + 273.15elif unit == "Kelvin":kelvin = valuecelsius = kelvin - 273.15fahrenheit = celsius * 9/5 + 32self.celsius_label.setText(f"{celsius:.2f} °C")self.fahrenheit_label.setText(f"{fahrenheit:.2f} °F")self.kelvin_label.setText(f"{kelvin:.2f} K")if __name__ == '__main__':app = QApplication([])window = TemperatureConverter()window.show()app.exec()
這個示例展示了:
- 創建自定義信號(
temperatureChanged
) - 使用
@Slot
裝飾器定義槽函數 - 連接內置控件信號到自定義槽
- 連接自定義信號到自定義槽
- 在槽函數中處理業務邏輯
8. 樣式與主題定制
PySide6提供了多種方式來定制應用的外觀和風格,從簡單的樣式調整到完整的主題定制。
8.1 基本樣式屬性
通過樣式表(QSS)為單個控件設置樣式:
from PySide6.QtWidgets import QPushButtonbutton = QPushButton("Styled Button")# 設置單一樣式屬性
button.setStyleSheet("background-color: #4CAF50; color: white;")# 多行樣式
button.setStyleSheet("""background-color: #4CAF50;color: white;border: none;padding: 8px 16px;font-size: 14px;
""")
8.2 Qt樣式表(QSS)
QSS類似于CSS,允許使用選擇器和屬性定義樣式:
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidgetapp = QApplication([])
window = QMainWindow()
central_widget = QWidget()
layout = QVBoxLayout(central_widget)# 添加一些按鈕
normal_button = QPushButton("Normal Button")
danger_button = QPushButton("Danger Button")
danger_button.setObjectName("dangerButton")
success_button = QPushButton("Success Button")
success_button.setProperty("type", "success")layout.addWidget(normal_button)
layout.addWidget(danger_button)
layout.addWidget(success_button)window.setCentralWidget(central_widget)# 應用樣式表
window.setStyleSheet("""/* 默認按鈕樣式 */QPushButton {background-color: #e0e0e0;border: none;padding: 8px 16px;color: #333;font-weight: bold;border-radius: 4px;}QPushButton:hover {background-color: #d0d0d0;}QPushButton:pressed {background-color: #c0c0c0;}/* 通過對象名選擇器 */QPushButton#dangerButton {background-color: #f44336;color: white;}QPushButton#dangerButton:hover {background-color: #d32f2f;}/* 通過屬性選擇器 */QPushButton[type="success"] {background-color: #4CAF50;color: white;}QPushButton[type="success"]:hover {background-color: #388E3C;}
""")window.show()
app.exec()
8.3 常用樣式屬性
8.3.1 背景與前景
/* 背景顏色 */
background-color: #f0f0f0;/* 背景圖像 */
background-image: url(background.png);
background-repeat: no-repeat;
background-position: center;/* 文本顏色 */
color: #333333;
8.3.2 邊框與輪廓
/* 邊框 */
border: 1px solid #999999;
border-width: 1px;
border-style: solid;
border-color: #999999;/* 特定邊的邊框 */
border-left: 2px dashed red;
border-top: 1px dotted blue;/* 圓角 */
border-radius: 4px;
border-top-left-radius: 8px;
8.3.3 文本格式
/* 字體 */
font-family: "Arial", sans-serif;
font-size: 14px;
font-weight: bold;/* 文本對齊 */
text-align: center;/* 文本裝飾 */
text-decoration: underline;
8.3.4 尺寸與布局
/* 尺寸 */
width: 100px;
min-width: 50px;
max-width: 200px;
height: 30px;/* 內邊距 */
padding: 8px;
padding-left: 16px;/* 外邊距 */
margin: 5px;
margin-top: 10px;
8.4 狀態相關樣式
為控件的不同狀態定義樣式:
/* 基本狀態 */
QPushButton {background-color: #e0e0e0;
}/* 懸停狀態 */
QPushButton:hover {background-color: #d0d0d0;
}/* 按下狀態 */
QPushButton:pressed {background-color: #c0c0c0;
}/* 選中狀態 */
QCheckBox:checked {color: green;
}/* 禁用狀態 */
QPushButton:disabled {background-color: #a0a0a0;color: #707070;
}/* 焦點狀態 */
QLineEdit:focus {border: 2px solid #0078d7;
}
8.5 自定義應用主題
創建完整的應用主題:
class ThemeManager:@staticmethoddef apply_dark_theme(app):app.setStyleSheet("""/* 全局樣式 */QWidget {background-color: #2b2b2b;color: #e0e0e0;font-family: "Segoe UI", Arial, sans-serif;}/* 主窗口 */QMainWindow {background-color: #1e1e1e;}/* 菜單 */QMenuBar {background-color: #2b2b2b;}QMenuBar::item {background-color: transparent;padding: 6px 10px;}QMenuBar::item:selected {background-color: #3a3a3a;}QMenu {background-color: #2b2b2b;border: 1px solid #555555;}QMenu::item {padding: 6px 20px;}QMenu::item:selected {background-color: #3a3a3a;}/* 按鈕 */QPushButton {background-color: #0078d7;color: white;border: none;padding: 8px 16px;border-radius: 4px;}QPushButton:hover {background-color: #0086f0;}QPushButton:pressed {background-color: #006ac1;}QPushButton:disabled {background-color: #555555;color: #888888;}/* 輸入框 */QLineEdit, QTextEdit, QPlainTextEdit {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px;color: #e0e0e0;}QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {border: 1px solid #0078d7;}/* 下拉框 */QComboBox {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px 8px;color: #e0e0e0;}QComboBox::drop-down {subcontrol-origin: padding;subcontrol-position: top right;width: 20px;border-left: 1px solid #555555;}/* 復選框 */QCheckBox {spacing: 8px;}QCheckBox::indicator {width: 16px;height: 16px;}/* 選項卡 */QTabWidget::pane {border: 1px solid #555555;}QTabBar::tab {background-color: #2b2b2b;border: 1px solid #555555;padding: 6px 12px;}QTabBar::tab:selected {background-color: #333333;}/* 滾動條 */QScrollBar:vertical {background-color: #2b2b2b;width: 12px;margin: 12px 0 12px 0;}QScrollBar::handle:vertical {background-color: #505050;min-height: 20px;border-radius: 6px;}QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {border: none;background: none;}""")@staticmethoddef apply_light_theme(app):# 實現淺色主題...pass# 使用示例
if __name__ == "__main__":app = QApplication([])ThemeManager.apply_dark_theme(app)# 創建應用主窗口和其他界面元素...# ...app.exec()
8.6 動態切換主題
實現主題切換功能:
class ThemeSwitchableApp(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("Theme Switcher")self.setGeometry(100, 100, 800, 600)# 創建中央部件central_widget = QWidget()self.setCentralWidget(central_widget)# 創建布局layout = QVBoxLayout(central_widget)# 添加一些控件title = QLabel("Theme Switching Demo")title.setAlignment(Qt.AlignCenter)title.setFont(QFont("Arial", 18))self.text_edit = QTextEdit()self.text_edit.setPlaceholderText("Type something here...")self.combo = QComboBox()self.combo.addItems(["Option 1", "Option 2", "Option 3"])self.checkbox = QCheckBox("Enable feature")# 主題切換按鈕theme_layout = QHBoxLayout()self.light_theme_btn = QPushButton("Light Theme")self.light_theme_btn.clicked.connect(self.apply_light_theme)self.dark_theme_btn = QPushButton("Dark Theme")self.dark_theme_btn.clicked.connect(self.apply_dark_theme)theme_layout.addWidget(self.light_theme_btn)theme_layout.addWidget(self.dark_theme_btn)# 添加控件到布局layout.addWidget(title)layout.addWidget(self.text_edit)layout.addWidget(self.combo)layout.addWidget(self.checkbox)layout.addLayout(theme_layout)# 設置初始主題self.current_theme = "light"self.apply_light_theme()def apply_light_theme(self):if self.current_theme == "light":returnself.current_theme = "light"# 提供一個簡單的淺色主題self.setStyleSheet("""QWidget {background-color: #f0f0f0;color: #333333;}QPushButton {background-color: #e0e0e0;border: 1px solid #d0d0d0;padding: 6px 12px;border-radius: 4px;}QPushButton:hover {background-color: #d0d0d0;}QTextEdit, QComboBox {background-color: white;border: 1px solid #d0d0d0;border-radius: 3px;padding: 4px;}""")def apply_dark_theme(self):if self.current_theme == "dark":returnself.current_theme = "dark"# 提供一個簡單的深色主題self.setStyleSheet("""QWidget {background-color: #2b2b2b;color: #e0e0e0;}QPushButton {background-color: #0078d7;color: white;border: none;padding: 6px 12px;border-radius: 4px;}QPushButton:hover {background-color: #0086f0;}QTextEdit, QComboBox {background-color: #333333;border: 1px solid #555555;border-radius: 3px;padding: 4px;color: #e0e0e0;}""")
9. Qt Designer 可視化設計
Qt Designer是一個強大的可視化界面設計工具,與PySide6集成使用可以大大提高開發效率。
9.1 Qt Designer 基礎
Qt Designer隨PySide6一起安裝,可以通過命令行啟動:
pyside6-designer
主要功能區域:
- 控件面板:可用的控件和布局
- 對象檢查器:顯示控件層次結構
- 屬性編輯器:修改選中控件的屬性
- 信號/槽編輯器:可視化連接信號和槽
- 資源瀏覽器:管理圖像等資源
- 動作編輯器:創建和管理應用動作
9.2 創建界面
在Qt Designer中設計界面的基本步驟:
- 選擇模板(如Widget、Main Window、Dialog)
- 從控件面板拖放控件到設計區域
- 設置布局(右鍵區域選擇布局類型)
- 調整控件屬性(在屬性編輯器中)
- 設置控件的對象名(objectName屬性)
- 保存為.ui文件
9.3 加載.ui文件
9.3.1 使用QUiLoader
動態加載.ui文件:
from PySide6.QtWidgets import QApplication
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile, QIODeviceapp = QApplication([])# 加載UI文件
ui_file = QFile("mainwindow.ui")
ui_file.open(QIODevice.ReadOnly)loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()# 訪問UI中的控件
window.pushButton.clicked.connect(lambda: print("Button clicked!"))
window.lineEdit.textChanged.connect(lambda text: print(f"Text: {text}"))window.show()
app.exec()
9.3.2 使用uic編譯
將.ui文件轉換為Python代碼:
pyside6-uic mainwindow.ui -o ui_mainwindow.py
然后在代碼中使用:
from PySide6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindowclass MainWindow(QMainWindow):def __init__(self):super().__init__()# 設置UIself.ui = Ui_MainWindow()self.ui.setupUi(self)# 連接信號self.ui.pushButton.clicked.connect(self.on_button_clicked)self.ui.lineEdit.textChanged.connect(self.on_text_changed)def on_button_clicked(self):print("Button clicked!")def on_text_changed(self, text):print(f"Text: {text}")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
9.4 多界面管理
管理多個界面文件:
class Application:def __init__(self):self.app = QApplication([])self.main_window = self.load_ui("mainwindow.ui")self.dialog = Nonedef load_ui(self, ui_file_name):ui_file = QFile(ui_file_name)ui_file.open(QIODevice.ReadOnly)loader = QUiLoader()window = loader.load(ui_file)ui_file.close()return windowdef show_dialog(self):# 懶加載對話框if not self.dialog:self.dialog = self.load_ui("dialog.ui")# 連接對話框信號self.dialog.accepted.connect(self.on_dialog_accepted)self.dialog.show()def on_dialog_accepted(self):print("Dialog accepted")def run(self):# 連接主窗口信號self.main_window.actionShowDialog.triggered.connect(self.show_dialog)self.main_window.show()return self.app.exec()if __name__ == "__main__":app = Application()sys.exit(app.run())
9.5 表單繼承
繼承Designer創建的UI類:
from PySide6.QtWidgets import QApplication, QMainWindow
from ui_mainwindow import Ui_MainWindow# 繼承UI類
class MainWindow(QMainWindow, Ui_MainWindow):def __init__(self):super().__init__()# 設置UIself.setupUi(self)# 添加信號連接self.pushButton.clicked.connect(self.on_button_clicked)self.actionExit.triggered.connect(self.close)def on_button_clicked(self):self.statusBar().showMessage("Button clicked!")self.label.setText(f"Hello, {self.lineEdit.text()}")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
9.6 資源文件管理
9.6.1 創建資源文件
創建資源文件(resources.qrc):
<!DOCTYPE RCC>
<RCC version="1.0"><qresource prefix="/images"><file>icons/new.png</file><file>icons/open.png</file><file>icons/save.png</file></qresource><qresource prefix="/styles"><file>styles/dark.qss</file><file>styles/light.qss</file></qresource>
</RCC>
9.6.2 編譯資源文件
pyside6-rcc resources.qrc -o resources_rc.py
9.6.3 使用資源
在.ui文件中引用資源:
import resources_rc
在代碼中使用資源:
from PySide6.QtGui import QIcon
from PySide6.QtCore import QFile, QTextStream# 使用圖標
icon = QIcon(":/images/icons/save.png")
button.setIcon(icon)# 加載樣式表
style_file = QFile(":/styles/styles/dark.qss")
style_file.open(QFile.ReadOnly | QFile.Text)
stream = QTextStream(style_file)
style_sheet = stream.readAll()
app.setStyleSheet(style_sheet)
10. 多線程與并發處理
在GUI應用程序中,長時間運行的任務應該在單獨的線程中執行,以避免界面凍結。
10.1 QThread 基本用法
使用QThread創建獨立線程:
from PySide6.QtCore import QThread, Signal
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QProgressBar, QVBoxLayout, QWidget
import timeclass Worker(QThread):# 定義信號progress = Signal(int)completed = Signal()def run(self):# 線程的主要執行代碼for i in range(101):# 執行耗時操作time.sleep(0.1) # 模擬耗時操作# 發出進度信號self.progress.emit(i)# 發出完成信號self.completed.emit()class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle("QThread Example")self.resize(400, 200)# 創建中央部件central_widget = QWidget()self.setCentralWidget(central_widget)# 創建布局layout = QVBoxLayout(central_widget)# 創建進度條self.progress_bar = QProgressBar()layout.addWidget(self.progress_bar)# 創建按鈕self.start_button = QPushButton("Start Task")self.start_button.clicked.connect(self.start_task)layout.addWidget(self.start_button)# 創建工作線程self.worker = Worker()self.worker.progress.connect(self.update_progress)self.worker.completed.connect(self.task_completed)def start_task(self):self.start_button.setEnabled(False)self.progress_bar.setValue(0)self.worker.start()def update_progress(self, value):self.progress_bar.setValue(value)def task_completed(self):self.start_button.setEnabled(True)self.start_button.setText("Start Again")app = QApplication([])
window = MainWindow()
window.show()
app.exec()
10.2 QRunnable 和線程池
使用QThreadPool管理多個并發任務:
from PySide6.QtCore import QRunnable, QThreadPool, QObject, Signal, Slot# 信號需要QObject
class WorkerSignals(QObject):progress = Signal(int)completed = Signal()error = Signal(str)class Task(QRunnable):def __init__(self, fn, *args, **kwargs):super().__init__()self.fn = fnself.args = argsself.kwargs = kwargsself.signals = WorkerSignals()@Slot()def run(self):try:# 執行函數result = self.fn(*self.args, **self.kwargs)except Exception as e:self.signals.error.emit(str(e))else:self.signals.completed.emit()# 使用線程池
def start_tasks():# 獲取全局線程池pool = QThreadPool.globalInstance()print(f"使用最大 {pool.maxThreadCount()} 個線程")# 創建和提交任務for i in range(5):task = Task(long_running_function, i)pool.start(task)
11. 數據持久化
PySide6應用通常需要保存用戶設置、緩存或其他數據。
11.1 QSettings
用于存儲應用程序設置:
from PySide6.QtCore import QSettings# 創建設置對象
settings = QSettings("MyCompany", "MyApp")# 保存設置
settings.setValue("window/size", window.size())
settings.setValue("window/position", window.pos())
settings.setValue("user/name", "John Doe")
settings.setValue("user/preferences", {"theme": "dark", "language": "en"})# 讀取設置
window_size = settings.value("window/size")
user_name = settings.value("user/name", "Guest") # 提供默認值
user_prefs = settings.value("user/preferences", {})# 檢查鍵是否存在
if settings.contains("user/name"):print("User name is set")# 刪除設置
settings.remove("user/name")# 清除所有設置
settings.clear()
11.2 SQLite數據庫集成
使用內置sqlite3模塊或QSqlDatabase:
from PySide6.QtSql import QSqlDatabase, QSqlQuery# 創建連接
db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("myapp.db")if not db.open():print("Cannot open database")print(db.lastError().text())return# 創建表
query = QSqlQuery()
query.exec_("""CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,email TEXT UNIQUE)
""")# 插入數據
query.prepare("INSERT INTO users (name, email) VALUES (:name, :email)")
query.bindValue(":name", "John Doe")
query.bindValue(":email", "john@example.com")
query.exec_()# 查詢數據
query.exec_("SELECT * FROM users")
while query.next():user_id = query.value(0)name = query.value(1)email = query.value(2)print(f"User: {user_id}, {name}, {email}")# 關閉數據庫
db.close()
12. 高級圖形與動畫
PySide6提供了豐富的圖形和動畫功能。
12.1 自定義繪圖
使用QPainter進行自定義繪圖:
from PySide6.QtWidgets import QWidget
from PySide6.QtGui import QPainter, QPen, QBrush
from PySide6.QtCore import Qtclass DrawingWidget(QWidget):def __init__(self):super().__init__()self.setMinimumSize(300, 200)def paintEvent(self, event):painter = QPainter(self)painter.setRenderHint(QPainter.Antialiasing)# 設置畫筆(輪廓)pen = QPen(Qt.blue, 2)painter.setPen(pen)# 設置畫刷(填充)brush = QBrush(Qt.green)painter.setBrush(brush)# 繪制矩形painter.drawRect(50, 50, 100, 50)# 繪制圓形painter.setPen(QPen(Qt.red, 3))painter.setBrush(QBrush(Qt.yellow))painter.drawEllipse(200, 50, 80, 80)# 繪制文本painter.setPen(Qt.black)painter.drawText(50, 150, "Hello, PySide6!")
12.2 動畫效果
使用QPropertyAnimation創建平滑動畫:
from PySide6.QtCore import QPropertyAnimation, QEasingCurve, Propertyclass AnimatedButton(QPushButton):def __init__(self, text):super().__init__(text)self._opacity = 1.0def opacity(self):return self._opacitydef setOpacity(self, opacity):self._opacity = opacityself.update()# 創建屬性opacity = Property(float, opacity, setOpacity)def paintEvent(self, event):painter = QPainter(self)painter.setOpacity(self._opacity)super().paintEvent(event)# 創建動畫
button = AnimatedButton("Animated Button")
animation = QPropertyAnimation(button, b"opacity")
animation.setDuration(1000) # 1秒
animation.setStartValue(1.0)
animation.setEndValue(0.2)
animation.setEasingCurve(QEasingCurve.InOutQuad)
animation.start()
13. 國際化與本地化
使PySide6應用支持多語言。
13.1 使用QTranslator
from PySide6.QtCore import QTranslator, QLocale# 創建翻譯器
translator = QTranslator()# 加載翻譯文件(根據系統語言)
locale = QLocale.system().name() # 如 "zh_CN"
if translator.load(f"myapp_{locale}", "translations"):app.installTranslator(translator)
13.2 使用tr()進行文本標記
class MainWindow(QMainWindow):def __init__(self):super().__init__()self.setWindowTitle(self.tr("My Application"))label = QLabel(self.tr("Hello, World!"))button = QPushButton(self.tr("Click Me"))# 帶復數和上下文的翻譯items_count = 5message = self.tr("%n item(s) selected", "", items_count)# 帶上下文的翻譯context_message = self.tr("Open", "File menu action")
14. 實戰項目開發
14.1 應用架構設計
使用模型-視圖-控制器(MVC)或模型-視圖-視圖模型(MVVM)架構:
# 模型 - 處理數據
class TodoModel:def __init__(self):self.items = []def add_item(self, text, due_date=None):self.items.append({"text": text, "completed": False, "due_date": due_date})def complete_item(self, index):if 0 <= index < len(self.items):self.items[index]["completed"] = True# 視圖模型 - 連接模型和視圖
class TodoViewModel(QObject):item_added = Signal(str, object)item_completed = Signal(int)def __init__(self, model):super().__init__()self.model = modeldef add_item(self, text, due_date=None):self.model.add_item(text, due_date)self.item_added.emit(text, due_date)def complete_item(self, index):self.model.complete_item(index)self.item_completed.emit(index)# 視圖 - 用戶界面
class TodoView(QMainWindow):def __init__(self, view_model):super().__init__()self.view_model = view_modelself.setupUi()# 連接視圖模型的信號self.view_model.item_added.connect(self.add_item_to_ui)self.view_model.item_completed.connect(self.mark_item_completed)
14.2 項目質量保證
- 使用類型提示
- 添加單元測試
- 使用版本控制
- 編寫文檔
- 進行代碼審查
15. 打包與發布
15.1 使用PyInstaller打包
將PySide6應用打包成可執行文件:
# 安裝PyInstaller
pip install pyinstaller# 打包應用
pyinstaller --name=MyApp --windowed --onefile main.py# 指定圖標和其他資源
pyinstaller --name=MyApp --windowed --onefile --icon=app_icon.ico main.py
15.2 使用cx_Freeze打包
另一個打包選項:
# setup.py
from cx_Freeze import setup, Executablesetup(name="MyApp",version="1.0",description="My PySide6 Application",executables=[Executable("main.py", target_name="MyApp.exe", icon="app_icon.ico")],options={"build_exe": {"packages": ["PySide6"],"include_files": ["icons/", "translations/"]}}
)
運行:
python setup.py build
16. 總結
本文全面介紹了PySide6桌面應用開發,從基礎概念、控件使用、布局管理,到高級特性如多線程、動畫和國際化。PySide6結合了Qt強大的功能和Python的簡潔性,是創建專業級桌面應用的理想選擇。
掌握PySide6可以讓開發者創建跨平臺、功能豐富的應用程序,從簡單工具到復雜企業軟件。通過持續學習和實踐,你可以充分發揮PySide6的潛力,構建出美觀、高效且用戶友好的桌面應用。
作者:climber1121
鏈接:https://blog.csdn.net/climber1121
來源:CSDN
版權聲明:本文為博主原創文章,轉載請附上原文出處鏈接和本聲明。