learnxinyminutes-docs 实战指南:用 PyQt4 快速上手 Qt 跨平台 GUI 编程
learnxinyminutes-docs 实战指南:用 PyQt4 快速上手 Qt 跨平台 GUI 编程
导读
本文基于 learnxinyminutes-docs 仓库中的 id/pyqt.md(印度尼西亚语译本)及其英文原版 pyqt.md,系统讲解如何使用 PyQt4 在 Python 中构建 Qt 跨平台桌面应用。Qt 是业界著名的 C++ 跨平台 GUI 框架,而 PyQt 是其在 Python 世界的官方绑定;读完本文,你将掌握 Qt 应用的最小骨架(QApplication + QWidget 事件循环模型)、控件的创建与父子关系组织、信号与槽(signals & slots)事件驱动机制,以及模态对话框的实现方式,并直接获得两个可复制运行、逐行注释的完整示例程序。
Qt 与 PyQt:跨平台 GUI 的原生体验
Qt 是一个广为人知的跨平台软件开发框架,能够在各种软件与硬件平台上以很少甚至零代码改动运行,同时保持原生应用程序的速度与能力。Qt 最初由 C++ 编写,而 PyQt 则是 Qt 面向 Python 的绑定库,让开发者可以用 Python 语法享受 Qt 全部控件与事件体系——这正是本文所依托的核心技术栈。
id/pyqt.md 明确说明,该文改编自 Aleksey Kholovchuk 撰写的 C++ 版 Qt 入门,代码示例在功能上与 C++ 原版等价,只是全部改用 PyQt 实现。这从侧面说明:PyQt 与 Qt 的编程模型一一对应,理解本文示例后,你也能顺畅阅读 Qt/C++ 生态的资料。
环境准备:基于 PyQt4 的代码前提
仓库内的 pyqt 系列文档(如 pyqt.md、zh-cn/pyqt.md、id/pyqt.md)均基于 PyQt4 编写,其模块结构为:
PyQt4.QtGui:GUI 控件库(窗口、按钮、标签、对话框等)PyQt4.QtCore:核心非 GUI 功能(信号、槽、事件循环辅助、枚举常量如Qt.ApplicationModal等)
需要说明的是,PyQt4 属于较早的 Qt 4 时代绑定版本,其模块划分(QtGui 同时容纳控件与窗口系统 API)与后续的 PyQt5/PyQt6(控件类迁移至 QtWidgets 模块)有所不同。若要在新环境运行文中代码,请先安装对应的 PyQt4 运行环境;若使用 PyQt5/PyQt6,只需将导入语句对应调整(如 from PyQt5.QtWidgets import QApplication, QWidget, QLabel),核心的编程模型与 API 命名基本保持延续。仓库中 qt.md、de/qt.md、zh-cn/qt.md 等文档还提供了 C++ 原生 Qt 的对照视角,可交叉阅读。
第一个 PyQt 程序:从零搭建"Hello World"窗口
以下代码是 id/pyqt.md 的核心示例之一,它完整展示了任何 PyQt 应用都必备的五步骨架:创建应用对象 → 创建控件 → 设置内容与几何属性 → 显示 → 进入事件循环。我们将逐行拆解。
import sys
from PyQt4 import QtGui
def window():
# Buat objek aplikasi(创建应用对象)
app = QtGui.QApplication(sys.argv)
# Buat sebuah widget, sebagai tempat di mana label kita akan ditempatkan
# (创建一个 widget,作为放置 label 的容器)
w = QtGui.QWidget()
# Tambahkan label untuk widget(为 widget 添加一个 label 子控件)
b = QtGui.QLabel(w)
# Set teks untuk label(设置 label 的文本)
b.setText("Halo, Dunia!")
# Set parameter penempatan dan ukuran(设置位置与尺寸参数)
w.setGeometry(100, 100, 200, 50)
b.move(50, 20)
# Set judul pada jendela(设置窗口标题)
w.setWindowTitle("PyQt")
# Tampilkan segalanya(显示所有内容)
w.show()
# Jalankan apa yang telah kita atur(运行已配置好的应用)
sys.exit(app.exec_())
if __name__ == '__main__':
window()
逐层拆解:应用对象与事件循环
QtGui.QApplication(sys.argv):每个 PyQt 程序都必须且只能创建一个QApplication实例,它是整个 GUI 程序的"心脏",负责管理事件循环、窗口系统资源与全局设置。sys.argv让应用能接收命令行参数。QtGui.QWidget():QWidget是所有控件的基类与通用容器。这里它作为顶级窗口使用,也是后续所有子控件的"父容器"。QtGui.QLabel(w):创建标签控件,并把w作为其父控件传入。父子关系是 Qt 对象生命周期管理的关键——父控件析构时会自动清理子控件,且子控件会随父控件一起显示、移动。b.setText("Halo, Dunia!"):设置标签文本。原文使用印尼语问候语"Halo, Dunia!"(即 Hello World),在 pyqt.md 英文原版中对应文本为"Hello World!",多语言译本均保留了各自的本地化示例。w.setGeometry(100, 100, 200, 50):以(x, y, 宽, 高)四个参数一次性设定窗口在屏幕上的位置(左上角距屏幕原点 100×100 像素)与尺寸(200×50 像素)。b.move(50, 20):将 label 相对父控件w移动到 (50, 20) 偏移处——坐标是相对父控件的局部坐标,这是理解 Qt 布局的基础。w.setWindowTitle("PyQt"):设置窗口标题栏文本。w.show():默认情况下控件是隐藏的,必须显式调用show()才会渲染。它会同时显示该控件及其所有子控件。sys.exit(app.exec_()):exec_()启动事件循环并阻塞当前线程,直到最后一个窗口关闭或应用退出;事件循环期间 Qt 持续分发鼠标、键盘、重绘等事件。sys.exit将应用退出码传递给操作系统。注意 PyQt4 中该方法名为exec_()(带下划线),这是为了避免与 Python 2 的内置exec关键字冲突——这正是仓库文档选择 PyQt4 时代的遗留命名。
代码组织:if __name__ == '__main__'
末尾的 if __name__ == '__main__': 是 Python 的标准入口约定:当该文件作为脚本直接运行时执行 window(),而作为模块被导入时不执行,从而保证代码可复用、可测试。
信号与槽:让界面响应交互
Qt 最具特色的机制是信号与槽(signals & slots):控件在特定事件发生时发射信号(如按钮被点击时的 clicked),你可以把信号连接到任意函数(即"槽"),实现事件驱动的解耦设计。第二个示例展示了这一机制在对话框场景下的完整运用。
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
def window():
app = QApplication(sys.argv)
w = QWidget()
# Buat tombol b dan tempelkan pada widget w(创建按钮并挂到 widget w 上)
b = QPushButton(w)
b.setText("Tekan aku!") # 按钮文本:"Press me!"(按我!)
b.move(50, 50)
# Perintahkan tombol b untuk memanggil fungsi ini ketika diklik
# (命令按钮 b 在被点击时调用此函数)
# Perhatikan bahwa kita tidak menggunakan simbol "()" pada pemanggilan fungsi kali ini
# (注意:这次调用函数时没有使用 "()" 符号)
b.clicked.connect(ShowDialog)
w.setWindowTitle("Dialog PyQt")
w.show()
sys.exit(app.exec_())
# Fungsi ini akan membuat jendela dialog dengan tombol
# yang menunggu untuk diklik untuk keluar dari program
# (此函数将创建一个带按钮的对话框窗口,按钮等待被点击后退出程序)
def ShowDialog():
d = QDialog()
b1 = QPushButton("ok", d)
b1.move(50, 50)
d.setWindowTitle("Dialog")
# Modalitas ini memberitahu popup untuk memblokir induk saat ini aktif
# (此模态设置告诉弹出窗口:在其激活期间阻塞父窗口)
d.setWindowModality(Qt.ApplicationModal)
# Pada klik, kita ingin seluruh proses untuk berhenti(点击时终止整个进程)
b1.clicked.connect(sys.exit)
d.exec_()
if __name__ == '__main__':
window()
连接信号的关键写法
b.clicked.connect(ShowDialog):这是信号与槽的核心调用。clicked是QPushButton在用户点击时发射的信号,connect把它与函数ShowDialog绑定。注意这里传入的是函数对象本身,而不是ShowDialog()——加上括号就变成立即执行并传入返回值,而非"等到点击时再触发"。这正是 Qt 事件驱动模型与普通命令式编程的本质区别,zh-cn/pyqt.md 译本特别补充:此写法下函数以对象方式传入,更多信号传递、带参绑定的细节属于 PyQt 信号机制的进阶话题。b1.clicked.connect(sys.exit):信号不仅可以连接自定义函数,也能直接连接内置可调用对象。点击"ok"按钮即触发sys.exit,终止整个进程——这也是文档注释"On click I'd like the entire process to end"的意图。
模态对话框:阻塞父窗口的交互
QDialog():QDialog是专门用于对话框的窗口类,常用于请求用户确认决策或展示信息,正对应文档所述"useful for asking the user to confirm a decision or to provide information"。d.setWindowModality(Qt.ApplicationModal):设置应用级模态(Qt.ApplicationModal枚举来自QtCore)。模态对话框激活期间会阻塞其父窗口(乃至整个应用)的输入,强制用户先处理对话框——这是"确认关键操作"类交互的标准做法。Qt枚举常量正是因此需要from PyQt4.QtCore import *的原因。d.exec_():与app.exec_()类似,为对话框启动一个嵌套的事件循环,阻塞直到对话框关闭,并返回用户操作结果(如 OK/Cancel 的按钮角色)。
从示例到实战:可运行的完整流程
将两个示例分别保存为独立脚本(如 hello.py 与 dialog.py),在安装好 PyQt4 的 Python 环境中直接运行:
python hello.py
python dialog.py
hello.py会弹出一个标题为 "PyQt"、尺寸 200×50 的窗口,中央偏右显示 "Halo, Dunia!" 文本。dialog.py会先显示带 "Tekan aku!" 按钮的主窗口;点击后弹出模态对话框 "Dialog",在对话框关闭(点击 "ok")之前,主窗口无法交互,点击 "ok" 则整个程序退出。
若你的环境使用 PyQt5/PyQt6,可将导入语句按如下方式迁移(PyQt5 起 GUI 控件统一移入 QtWidgets 模块,信号连接与模态 API 保持一致):
# PyQt5/PyQt6 等价写法示例
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QDialog
from PyQt5.QtCore import Qt
继续深入:仓库内的配套学习路径
learnxinyminutes-docs 为 PyQt 提供了多语言对照资料,便于你横向验证代码行为并深入不同侧面:
- pyqt.md:英文原版,两个示例的规范出处;
- zh-cn/pyqt.md:中文译本,对信号机制"无
()传函数对象"、模态阻塞父窗口等概念有更详尽的中文注释; - es/pyqt.md、fr/pyqt.md、de/pyqt.md、ru/pyqt.md、it/pyqt.md、pt-br/pyqt.md:西班牙语、法语、德语、俄语、意大利语、葡萄牙语译本,适合多语言学习者对照;
- qt.md(及 zh-cn/qt.md、de/qt.md、it/qt.md、ru/qt.md、pt-br/qt.md):同一入门内容的 C++ 原生 Qt 版本,可与本文的 PyQt 实现逐行对照,深刻体会两种语言在事件循环、信号槽写法上的异同。
这套文档体系本身就是"同一逻辑、多语言实现"的绝佳教材:把 C++ 版与 PyQt 版并排阅读,即可快速建立"Qt 概念 → Python 表达"的映射能力,为后续学习布局管理器(Layout)、模型/视图、绘图等 PyQt 高级主题打下坚实基础。