一、基础示例
import sys
from PySide6.QtWidgets QApplication, QLabel
app = QApplication(sys.argv)
label = QLabel()
label.show()
app.()
PySide6 是 Qt 框架的 Python 绑定版本。本文涵盖基础示例创建、.ui 文件加载方式(生成类或直接加载)、控件样式设置(setStyleSheet 及 QSS 文件)以及信号与槽机制的详细讲解。内容包括信号声明、槽函数装饰器、线程连接类型及重载处理,帮助开发者快速掌握 PySide6 桌面应用开发核心流程。

import sys
from PySide6.QtWidgets QApplication, QLabel
app = QApplication(sys.argv)
label = QLabel()
label.show()
app.()
运行效果:

对于使用 PySide6 的 Widget 应用程序,必须先从 PySide6.QtWidgets 模块中导入相应的类。导入完成后,需要创建一个 QApplication 实例。
由于 Qt 可以接收来自命令行的参数,我们可以将任何参数传递给 QApplication 对象,但通常不需要传递任何参数,因此可以保持默认,或者使用以下方法:
app = QApplication([])
在创建 QApplication 对象之后,我们创建了一个 QLabel 对象。QLabel 是一个可以显示文本(简单文本或富文本,如 HTML)或者图像的控件:
# This HTML approach will be valid too!
label = QLabel("<font color=red size=40>Hello World!</font>")
创建 QLabel 对象之后,调用了它的 show() 方法。
最后,调用 app.exec() 进入 Qt 主循环并开始执行 Qt 代码。

在 python 代码中使用 pyside6-designer 设计好的 .ui 文件:


与 UI 文件交互的标准方式是从中生成一个 Python 类,这得益于 pyside6-uic 工具。在控制台上运行以下命令:
pyside6-uic mainwindow.ui -o ui_mainwindow.py


导入生成的文件 ui_mainwindow.py:
from ui_mainwindow import Ui_MainWindow
示例代码如下:
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QFile
from ui_mainwindow import Ui_MainWindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
运行效果:

新的基本类 MainWindow 只包含两行新代码,用于从 UI 文件中加载生成的 Python 类:
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
要直接加载 UI 文件,我们需要从 QtUiTools 模块引入一个类:
from PySide6.QtUiTools import QUiLoader
QUiLoader 让我们可以动态加载 .ui 文件并立即使用它:
ui_file = QFile("mainwindow.ui")
ui_file.open(QFile.ReadOnly)
loader = QUiLoader()
window = loader.load(ui_file)
window.show()
示例代码如下:
# File: main.py
import sys
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import QFile, QIODevice
if __name__ == "__main__":
app = QApplication(sys.argv)
ui_file_name = "mainwindow.ui"
ui_file = QFile(ui_file_name)
if not ui_file.open(QIODevice.ReadOnly):
print(f"Cannot open {ui_file_name}: {ui_file.errorString()}")
sys.exit(-1)
loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()
if not window:
print(loader.errorString())
sys.exit(-1)
window.show()
sys.exit(app.exec())
运行效果:

Qt Widgets 会根据不同的操作系统使用默认主题。然而,我们也可以为每个控件自定义样式,例如:
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel
if __name__ == "__main__":
app = QApplication()
w = QLabel("This is a placeholder text")
w.setAlignment(Qt.AlignCenter)
w.show()
sys.exit(app.exec())
执行这段代码后,我们会看到一个简单的 QLabel 位于中心,并带有占位文本:

我们可以使用类似 CSS 的语法来为它设置样式,例如 background-color 和 font-family:
import sys
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QLabel
if __name__ == "__main__":
app = QApplication()
w = QLabel("This is a placeholder text")
w.setAlignment(Qt.AlignCenter)
w.setStyleSheet("""
background-color: #262626;
color: #FFFFFF;
font-family: Titillium;
font-size: 18px;
""")
w.show()
sys.exit(app.exec())

更简单的替代方法是使用 Qt 样式表,它是一个或多个 .qss 文件,用于定义 UI 元素的样式。qss 文件与 CSS 文件非常相似,但我们需要指定 Widget 控件,并可选择性地指定对象的名称:
QLabel{background-color: red;}
QLabel#title{font-size: 20px;}
以上代码中,第一个样式为所有 QLabel 对象定义了背景颜色,而后一个样式仅为标题对象设置样式。
定义好样式文件后,使用 QApplication.setStyleSheet(str) 函数来应用它:
if __name__ == "__main__":
app = QApplication()
w = Widget()
w.show()
with open("style.qss", "r") as f:
_style = f.read()
app.setStyleSheet(_style)
sys.exit(app.exec())

拥有一个通用的 qss 文件可以将代码的样式部分解耦,而不必将其混杂在整体功能中,并且我们可以轻松地启用或禁用它。
来看这个新的示例,包含更多的控件:
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
menu_widget = QListWidget()
for i in range(10):
item = QListWidgetItem(f"Item {i}")
item.setTextAlignment(Qt.AlignCenter)
menu_widget.addItem(item)
text_widget = QLabel("placeholder")
button = QPushButton("Something")
content_layout = QVBoxLayout()
content_layout.addWidget(text_widget)
content_layout.addWidget(button)
main_widget = QWidget()
main_widget.setLayout(content_layout)
layout = QHBoxLayout()
layout.addWidget(menu_widget, 1)
layout.addWidget(main_widget, 4)
self.setLayout(layout)
这显示了一个两栏的控件,左边是一个 QListWidget,右边是一个 QLabel 和一个 QPushButton,代码运行效果如下:

向 style.qss 文件添加内容就可以修改它的外观效果:
QListWidget{color: #FFFFFF;background-color: #33373B;}
QListWidget::item{height: 50px;}
QListWidget::item:selected{background-color: #2ABf9E;}
QLabel{background-color: #FFFFFF;qproperty-alignment: AlignCenter;}
QPushButton{background-color: #2ABf9E;padding: 20px;font-size: 18px;}

信号和槽是 Qt 的一个功能,允许图形控件与其他图形控件或 Python 代码进行通信。接下来我们创建一个 Qt 应用,它有一个按钮控件,每次点击它时,会将设定好的消息记录到 Python 控制台。
首先导入必要的 PySide6 类和 Python 的 sys 模块:
import sys
from PySide6.QtWidgets import QApplication, QPushButton
from PySide6.QtCore import Slot
创建一个将消息记录到控制台的 Python 函数:
# Greetings
@Slot()
def say_hello():
print("Button clicked, Hello!")
现在,如前面的例子所示,我们必须创建 QApplication 来运行 PySide6 代码:
# Create the Qt Application
app = QApplication(sys.argv)
创建一个可点击的按钮,它是一个 QPushButton 实例:
# Create a button
button = QPushButton("Click me")
在显示按钮之前,我们必须将其连接到之前定义的 say_hello() 函数。有两种方法可以做到这一点:使用老式方法或更符合 Python 风格的新方法,在这里,我们使用新方法。
QPushButton 有一个预定义的信号叫做 clicked,每次按钮被点击时都会触发,我们把这个信号连接到 say_hello() 函数:
# Connect the button to the function
button.clicked.connect(say_hello)
最后,我们显示按钮并启动 Qt 主循环:
# Show the button
button.show()
# Run the main Qt loop
app.exec()
运行效果:

完整代码如下:
import sys
from PySide6.QtWidgets import QApplication, QPushButton
from PySide6.QtCore import Slot
# Greetings
@Slot()
def say_hello():
print("Button clicked, Hello!")
# Create the Qt Application
app = QApplication(sys.argv)
# Create a button
button = QPushButton("Click me")
# Connect the button to the function
button.clicked.connect(say_hello)
# Show the button
button.show()
# Run the main Qt loop
app.exec()
由于 Qt 的特性,QObjects 需要一种通信方式,这就是该机制成为 Qt 核心功能的原因。
可以把信号和槽理解为我们与家里灯光的互动方式:当我们控制灯的开关(信号)时,得到的结果可能是灯泡被打开或关闭(槽)。
在开发界面时,我们可以通过点击按钮的效果得到一个实际的例子:点击 是信号,而槽则是按钮被点击时 发生的操作,比如关闭窗口、保存文档等。
所有继承自 QObject 或其子类(如 QWidget)的类都可以包含信号和槽。一个对象在它状态发生变化时会发出信号,该变化可能对其他对象有意义的,它并不关心或知道是否有任何对象接收了它发出的信号。
槽可以用于接收信号,但它们也是普通的成员函数,就像一个对象不知道是否有接收它的信号的其他对象一样,槽也不知道是否有信号与它连接。
我们可以将任意数量的信号连接到一个槽上,同时一个信号也可以连接到多个槽上,甚至可以将一个信号直接连接到另一个信号。(这将使第二个信号在第一个信号发出时立即被触发。)
Qt 的控件有许多预定义的信号和槽,例如,QAbstractButton(Qt 中按钮的基类)有一个 clicked() 信号,QLineEdit(单行输入框)有一个名为 clear() 的槽。因此,可以通过在 QLineEdit 右侧放置一个 QToolButton,并将其 clicked() 信号连接到 clear() 槽来实现带有清除按钮的文本输入框,这是通过信号的 connect() 方法完成的:
button = QToolButton()
line_edit = QLineEdit()
button.clicked.connect(line_edit.clear)
connect() 返回一个 Connection 对象,可以使用 disconnect() 方法来断开连接。
除了连接到槽,信号也可以连接到自由函数:
import sys
from PySide6.QtWidgets import QApplication, QPushButton
def function():
print("The 'function' has been called!")
app = QApplication()
button = QPushButton("Call function")
button.clicked.connect(function)
button.show()
sys.exit(app.exec())
connect() 函数接受一个可选的 ConnectionType 参数,该参数指定与线程和事件循环相关的行为。
连接可以通过代码明确指定,或者对于控件表单,在
Qt Widgets Designer的信号槽编辑器中设计。
在 Python 中编写类时,信号被声明为 Signal 类的类级变量。一个基于 QWidget 的按钮发出 clicked() 信号的示例如下所示:
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import QWidget
class Button(QWidget):
clicked = Signal(Qt.MouseButton)
def mousePressEvent(self, event):
self.clicked.emit(event.button())
Signal 的构造函数接受一个由 Python 类型和 C 类型组成的元组或列表:
signal1 = Signal(int) # Python types
signal2 = Signal(QUrl) # Qt Types
signal3 = Signal(int, str, int) # more than one type
signal4 = Signal((float,), (QDate,)) # optional types
除此之外,它还可以接收一个名为 name 的命名参数来定义信号名称,如果未传入任何值,新信号将与分配给它的变量同名:
# TODO
# signal5 = Signal(int, name='rangeChanged')
# ... rangeChanged.emit(...)
Signal 的另一个有用选项是 arguments name,对于 QML 应用程序来说,可以通过名称引用发出的值,这非常有用:
# TODO
# sumResult = Signal(int, arguments=['sum'])
# ... Connections { target:... function onSumResult(sum){// do something with'sum'} }
QObject 派生类中的槽应该使用装饰器 @Slot 来标注,同样,要定义一个签名,只需像 Signal 类一样传递类型即可。
@Slot(str)
def slot_function(self, s):
...
Slot() 也接受 name 和 result 关键字。result 关键字定义将返回的类型,可以是 C 类型或 Python 类型。name 关键字的行为与 Signal() 中的相同。如果 name 没有传值,则新槽的名称将与被装饰的函数名称相同。
建议对所有通过信号连接使用的方法添加 @Slot 装饰器。不添加会导致运行时开销,因为在创建连接时,该方法会被添加到 QMetaObject 中。这对于使用 QML 注册的 QObject 类尤其重要,缺少装饰器可能会引入 bug。
可以通过设置日志类别 qt.pyside.libpyside 的激活警告来诊断缺失的装饰器。例如,可以通过设置环境变量来实现:
export QT_LOGGING_RULES="qt.pyside.libpyside.warning=true"
在多线程应用程序中,信号可能由属于不同于接收者的线程的发送者发出。对于非 Slot 类型的接收者,代码将在发送者的线程上下文中执行。
然而,对于使用 @Slot 装饰的 QObject 派生类的方法,这通常有所不同,因为它们与特定线程相关,这取决于 connect() 方法的最后一个参数,该参数的类型为 PySide6.QtCore.Qt.ConnectionType。
当传入 Qt.ConnectionType.AutoConnection(默认值)或 Qt.ConnectionType.QueuedConnection 时,接收者代码将在接收者对象的线程上下文中执行,这对于将后台线程的结果传递到需要使用主线程的 GUI 类非常有用。
实际上,可以使用相同名称的信号和槽,但参数类型列表不同,这是 Qt 5 的遗留特性,不推荐在新代码中使用。在 Qt 6 中,不同类型的信号具有不同的名称。
下面的示例使用两个处理程序来展示信号和槽的不同功能:
import sys
from PySide6.QtWidgets import QApplication, QPushButton
from PySide6.QtCore import QObject, Signal, Slot
class Communicate(QObject):
# create two new signals on the fly: one will handle
# int type, the other will handle strings
speak = Signal((int,), (str,))
def __init__(self, parent=None):
super().__init__(parent)
self.speak[int].connect(self.say_something)
self.speak[str].connect(self.say_something)
# define a new slot that receives a C 'int' or a 'str'
# and has 'say_something' as its name
@Slot(int)
@Slot(str)
def say_something(self, arg):
if isinstance(arg, int):
print("This is a number:", arg)
elif isinstance(arg, str):
print("This is a string:", arg)
if __name__ == "__main__":
app = QApplication(sys.argv)
someone = Communicate()
# emit 'speak' signal with different arguments.
# we have to specify the str as int is the default
someone.speak.emit(10)
someone.speak[str].emit("Hello everybody!")
运行效果:
This is a number: 10
This is a string: Hello everybody!

信号和槽也可以通过 SIGNAL() 和/或 SLOT() 函数传递的 C++ 方法签名字符串来指定:
from PySide6.QtCore import SIGNAL, SLOT
button.connect(SIGNAL("clicked(Qt::MouseButton)"), action_handler, SLOT("action1(Qt::MouseButton)"))
通常不推荐这么做,仅在少数情况下需要,例如信号只能通过 QMetaObject 访问时(QAxObject、QAxWidget、QDBusInterface 或 QWizardPage::registerField())。
wizard.registerField("text", line_edit, "text", SIGNAL("textChanged(QString)"))
在检查 QMetaObject 时,可以通过查询 QMetaMethod.methodSignature() 找到签名字符串:
mo = widget.metaObject()
for m in range(mo.methodOffset(), mo.methodCount()):
print(mo.method(m).methodSignature())
注意,槽应使用 @Slot 进行装饰。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online