Qt简介


Qt是由 C++ 底层实现的,它支持的操作系统很多,如Windows、Linux、Unix、Android、iOS、嵌入式等等,而 PyQt 是由 Python 调用 Qt 实现的,PyQt 由 Riverbank Computing 开发,采用以下许可:

  • GPLv3:开源版本使用此协议,要求衍生作品也采用 GPLv3。
  • 商业许可:用于闭源或商业应用,需向 Riverbank Computing 购买许可证。

Python 官方 Wiki 中关于 PyQt 的介绍:PyQt - Python Wiki Qt官方网站:Qt Documentation | Home

PyQt环境准备


创建虚拟环境(可选)

在dos命令窗中导航到想要创建虚拟环境的目录,执行创建虚拟环境命令:

python -m venv 环境名称

此处我以环境名为py3_qt_test为例:

python -m venv py3_qt_test

安装PyQt5库命令

创建好虚拟环境后直接在当前目录下安装所需的pyqt5库:

pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple/

安装完成后可以查看模块安装列表,检查是否安装成功,检查安装列表命令如下:

pip list

Pasted image 20250710104754

也可以在当前安装PyQt的虚拟环境中执行包含如下的测试代码文件:

# 若能能够正常执行,则表明环境搭建成功
from PyQt5 import QtWidgets

# 可以查看PyQt版本信息
from PyQt5.QtCore import *
print(QT_VERSION_STR)

模块介绍


PyQt中存在非常多的功能模块,开发中最常用的功能模块主要由三个:

  • QtCore:包含了核心的非GUI的功能,主要和时间、文件与文件夹、各种数据流、URLs、mime类文件、进程、线程等一起使用
  • QtGui:包含了窗口系统、事件处理、2D图像、基本绘画、字体和文字类
  • QtWidgets:包含了一些列创建桌面应用的UI元素

Qt5的官方参考文档: Qt 5.15

基本UI


新建一个窗口

首先创建一个QApplication对象(只要是Qt制作的app,必须有且只有一个QApplication对象,sys.argv 当作参数的目的是将运行时的命令参数传递给QApplication对象),再使用创建窗口的QWidget()方法创建窗口,在创建前需要导入需要的模块及方法,如下代码案例:

首先进行导包:

import sys
from PyQt5.QtWidgets import QApplication,QWidget

创建第一个Qt程序:

if __name__ == '__main__':  
    # 创建一个应用实例  
    app = QApplication(sys.argv)  # argv用来获取命令行参数列表
    # 创建一个窗口  
    w = QWidget()  
    # 设置窗口标题  
    w.setWindowTitle('第一个PyQt5程序')  
    # 展示窗口  
    w.show()  
  
    # 程序进行循环等待状态,直到用户关闭窗口  
    app.exec_()  # 等同于tkinter的mainloop()

设置窗口或控件的大小

同时设置大小和位置

QWidget 对象有一个 setGeometry() 方法用于同时设置窗口或控件的大小和位置,代码如下:

# setGeometry 的参数依次为: x,y,width,height
w.setGeometry(20,20,300,300) # w 为QWidget对象

设置大小

QWidget 对象也提供了一个 resize() 方法用于设置窗口或控件的大小,代码如下:

# resize 的参数: width,height 
w.resize(300,300)  # w 为QWidget对象

setGeometryresize 的区别

区别点setGeometry()resize()
位置控制同时设置位置和大小仅设置大小,不改变位置
默认位置若未调用 move(),窗口会出现在指定位置窗口默认出现在系统决定的位置(通常是屏幕左上角附近)
与布局管理器配合可能干扰布局管理器的自动调整更适合与布局管理器配合(仅调整内容区域大小)

创建按钮

QtWidgets库中提供了QPushButton()方法用来创建按钮,可以通过QPushButton对象.信号(如click).connect(槽函数) 的方式绑定点击事件。代码如下:

'''在w窗口中创建一个按钮'''
# 创建一个按钮
btn = QPushButton("登录")
# 为按钮设置父控件
btn.setParent(w)
# 设置按钮的位置
btn.move(50,100) # 也可以使用setGeometry方法

除了上述创建控件的方式,即先创建控件再绑定父控件的方式之外,还可以直接再创建控件的同时绑定父控件,代码如下所示:

'''在w窗口中创建一个按钮'''
# 创建按钮同时绑定父控件
btn = QPushButton("登录",w)
# 设置按钮的位置
btn.move(50,100)

在创建按钮控件以及其他控件时也需要导入对应的UI元素方法,如下所示:

from PyQt5.QtWidgets import QPushButton, QLineEdit,QLabel

以上两种创建方式也适用于QLineEditQLabel等其他的控件

创建标签

QtWidgets库中提供了QLabel()方法用来创建按钮。代码如下:

'''在w窗口中创建一个标签'''
# 先创建后绑定父控件  
user = QLabel('账户:')  
user.setParent(w)  
user.setStyleSheet("font-size:16px;color:blue") # 对组件进行属性设置
user.move(50,50)
#  创建的同时并绑定父控件  
password = QLabel('密码:',w)
password.move(50,70)

创建输入框

QtWidgets库中提供了QLineEdit()方法用来创建输入框。可以使用QLineEdit所提供的setPlaceholderText(str) 的方法,创建输入框的提示词。代码如下:

'''在w窗口中创建一个输入框'''
#  创建的同时并绑定父控件  
edit = QLineEdit(w)
edit.setPlaceholderText('请输入用户名') # 提示词
w.move(100,50)

创建单选按钮

QtWidgets库中提供了QRadioButton()方法用来创建按钮,复选框的创建并不复杂,重点在于如何对这些控件进行布局排版,例如其代码如下:

# 在w窗口添加性别复选框
h = QHBoxLayout()  # 创建一个水平布局  
group = QGroupBox('性别')  # 创建性别组  
v = QVBoxLayout()  # 创建一个垂直布局  
btn1 = QRadioButton('男')  '''创建单选按钮的命令就这两句'''
btn2 = QRadioButton('女')  '''其他的代码都是为了布局'''
v.addWidget(btn1)  
v.addWidget(btn2)  
group.setLayout(v)  # 将垂直布局与组绑定  
h.addWidget(group)  
w.setLayout(h)

布局方式


盒子布局(QBoxLayout)

PyQt提供的盒子布局存在两种方式,分别是:

  • 水平布局 使用QHBQoxLayout方法
  • 垂直布局 使用QVBQoxLayout方法

垂直布局(QVBQoxLayout)

QtWidgets库中提供了QVBQoxLayout()方法用来创建一个垂直布局,QVBQoxLayout对象所提供的一些方法:

  • addWidget(控件对象)方法将控件添加入布局中
  • addLayout(布局对象)方法将其他布局嵌入当前布局中
  • addStretch(int)方法可以添加伸缩因子进行排版

具体实操代码如下所示:

layout = QVBoxLayout() # 创建一个垂直布局  
btn1 = QPushButton('按钮1')  
btn2 = QPushButton('按钮2')  
# 以下的addSretch最终达到的效果为btn1与btn2的分布为1:2
layout.addWidget(btn1) # 将按钮1加入垂直布局  
layout.addStretch(1) # 添加一个伸缩因子  
layout.addWidget(btn2) # 将按钮2加入垂直布局  
layout.addStretch(2) # 再添加一个伸缩因子 
# 在对垂直布局操作完成后记得为该布局添加父控件,例在w上显示垂直布局
w.setLayout(layout)

水平布局(QHBQoxLayout)

QtWidgets库中提供了QHBQoxLayout()方法用来创建一个水平布局,QHBQoxLayout对象也所提供的一些方法:

  • addWidget(控件对象)方法将控件添加入布局中
  • addLayout(布局对象)方法将其他布局嵌入当前布局中
  • addStretch(int)方法可以添加伸缩因子进行排版

具体实操代码如下所示:

layout = QHBoxLayout() # 创建一个水平布局  
btn1 = QPushButton('按钮1')  
btn2 = QPushButton('按钮2')  
# 以下的addSretch最终达到的效果为btn1与btn2的分布为1:2
layout.addWidget(btn1) # 将按钮1加入水平布局  
layout.addStretch(1) # 添加一个伸缩因子  
layout.addWidget(btn2) # 将按钮2加入水平布局  
layout.addStretch(2) # 再添加一个伸缩因子 
# 在对水平布局操作完成后记得为该布局添加父控件,例在w上显示水平布局
w.setLayout(layout)

网格布局(QGridLayout)

QtWidgets库中提供了QGridLayout()方法用来创建一个网格布局(也称九宫格布局),QGridLayout对象提供了addWidget(控件,行,列)方法用来向网格布局中添加内容,示例代码如下:

h = QVBoxLayout()  
inp = QLineEdit()  # 添加输入框  
inp.setPlaceholderText('请输入式子')  
h.addWidget(inp)  
w.setWindowTitle('九宫格布局')  
data = {  
    0:['7','8','9','+'],  
    1:['4','5','6','-'],  
    2:['1','2','3','*'],  
    3:['.','0','=','/']  
}  
grid = QGridLayout()  # 创建网格布局  
for key,value in data.items():  
    for i,v in enumerate(value):  
        btn = QPushButton(v)  
        grid.addWidget(btn,key,i)  # 添加内容
h.addLayout( grid)  
w.setLayout(h)

表单布局(QFromLayout)

QtWidgets库中提供了QFromLayout()方法用来创建一个表单布局,示例代码如下:

v = QVBoxLayout()  # 创建垂直布局
from_layout = QFormLayout() # 创建表单布局  
edit1 = QLineEdit()  # 创建输入框1
edit1.setPlaceholderText('请输入用户名')  
from_layout.addRow('用户名', edit1)  # 向表单布局添加一行
edit2 = QLineEdit()  # 创建输入框1
edit2.setPlaceholderText('请输入密码')  
from_layout.addRow('密码', edit2)  
v.addLayout(from_layout)  # 将表单布局嵌入垂直布局
v.addWidget(QPushButton('登录'), alignment=Qt.AlignCenter)  
w.setLayout(v) # 将垂直布局与窗口绑定

抽屉布局(QStackedLayout)

QtWidgets库中提供了QStackedLayout()方法用来创建一个抽屉布局,示例代码如下:

v = QVBoxLayout()  
stack_layout = QStackedLayout()  
  
win1 = win_1()  
win2 = win_2()  
stack_layout.addWidget(win1)  
stack_layout.addWidget(win2)  
  
btn1 = QPushButton('抽屉1')  
btn2 = QPushButton('抽屉2')  
# 绑定点击事件 对象.信号.connect(槽函数)  
btn1.clicked.connect(btn_press1)  
btn2.clicked.connect(btn_press2)  
  
v.addLayout(stack_layout)  
v.addWidget(btn1)  
v.addWidget(btn2)  
w.setLayout(v)

调用的函数部分的结果如下所示:

def win_1():  
    '''函数体'''
def win_2():  
    '''函数体'''
def btn_press1():  
    stack_layout.setCurrentIndex(0) # 设置当前索引为0  
def btn_press2():  
    stack_layout.setCurrentIndex(1)

在执行这些代码的过程中,它都处于如下框架内:

app = QApplication(sys.argv)  
w = QWidget()

'''设置窗口'''

w.show()  
sys.exit(app.exec_())

在使用这些布局时记得导入,导入方式如下所示:

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QVBoxLayout, QFormLayout, QStackedLayout, QGroupBox, QRadioButton, QHBoxLayout, QLabel

窗口居中


获取中央位置坐标

QtWidgets库中提供了QDesktopWidget().screenGeometry().center()方法用来获取屏幕的中央位置,返回值为一个坐标对象Qpoint。可以使用Qpoint对象.x,Qpoint对象.y来获取到该坐标的数值。如下代码所示:

# 使用QDesktopWidget方法  
center_pointer = QDesktopWidget().screenGeometry().center()   
x = center_pointer.x()  
y = center_pointer.y()

将窗口居中显示

当获取到中央位置后可以减去窗口的宽和高,再使用w.move(x-width//2,y-height//2)打掉将窗口居中显示的效果。例如当窗口w大小为(600,400)

w.move(x-600//2, y-400//2) # w.resize(600,400)

上述的计算中央坐标的方式需要手动输入窗口的大小,为了方便,我们可以使用自动计算居中,其中使用到了QWidget对象的frameGeometry()方法以获得QWidget对象的窗口大小及位置 ,以元组的形式返回。如下代码所示:

"""自动计算居中"""  
# 使用frameGeometry方法获取窗口坐标对象  
print(type(w.frameGeometry()),w.frameGeometry())  # 得到的QRect对象  
# 使用QRect对象的getRect方法得到坐标的元组  
tup = w.frameGeometry().getRect()  
print(type(w.frameGeometry().getRect()),w.frameGeometry().getRect())  
w.move(x-tup[2]//2, y-tup[3]//2)

创建窗口的形式


窗口的创建形式主要分为三种:

  • QWidget(基础窗口类)
  • QMainWindow(主窗口类)
  • QDialog(对话框类)

QWidget

QWidget是所有用户界面元素的基类,即控件和窗口的父类,是最基础的窗口组件,可独立作为窗口,也可作为其他组件的容器,自由度高(什么东西都没有),多应用于小型工具窗口、自定义控件的载体。示例代码如下所示:

import sys  
from PyQt5.QtWidgets import QApplication,QWidget  

if __name__ == '__main__':  
    app = QApplication(sys.argv) 
    w = QWidget()
    
    ''''此处补充窗口设置代码'''
     
    w.show()  
    app.exec_()

QMainWindow

QMainWindow专门用于创建应用程序的主窗口,继承自QWidget,是QWidget的子类,自带标准布局,包含菜单栏、工具栏、状态栏、标题栏等,中间部分为主窗口区域。多应用于文本编辑器、浏览器等拥有完整菜单和工具条的应用程序主界面。QMainWindow提供了一些方法如下:

  • menuBar:菜单栏
  • toolBar:工具栏
  • statusBar:状态栏
  • centralWidget:中心部件

menuBar为例,当使用menuBar创建一个对象后,该对象将可以使用它包含的几个方法:

  • addMenu(str):向menuBar中添加选项
  • addAction(str):向各个选项中添加动作
  • setNativeMenuBar(False):使mac系统菜单栏失效,让其按照Windows菜单栏样式显示

示例代码如下所示:

import sys  
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel  
  
if __name__ == '__main__':  
    app = QApplication(sys.argv)  
    w = QMainWindow()  
    
    # 创建菜单栏
    menu = w.menuBar()   
    menu.setNativeMenuBar(False) 
    file_menu = menu.addMenu("文件")  
    file_menu.addAction("新建")  
    file_menu.addAction("打开")   
    user_menu = menu.addMenu("用户")  
    user_menu.addAction("登录")  
    user_menu.addAction("注册")  
    w.setMenuBar(menu)  
    # 设置主窗口区域  
    label = QLabel("这是主区域",w)  
    w.setCentralWidget( label) 
      
    w.show()  
    sys.exit(app.exec_())

QDialog

QDialog用于创建对话框窗口,继承自QWidget,主要用于与用户进行短期交互(如提示、设置、确认等),多应用于打开文件对话框、设置对话框、确认删除等提示框。QDialog主要有两种特点:

  • 模态(阻塞父窗口操作),通过exec()方法显示模态对话框
  • 非模态(不阻塞父窗口操作),通过show()显示非模态对话框
特性模态对话框 (Modal)非模态对话框 (Modeless)
阻塞行为阻塞父窗口,必须关闭后才能操作其他窗口不阻塞父窗口,可同时操作多个窗口
创建方法exec()setModal(True)+show()show()
使用场景必须立即处理的交互(如确认删除)辅助工具(如浮动搜索框)

模态对话框 (阻塞式),打开对话框后,无法点击主窗口的任何部分。示例代码如下:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QPushButton, QVBoxLayout, QLabel

class ModalDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("模态对话框")
        layout = QVBoxLayout()
        layout.addWidget(QLabel("我是模态对话框!关闭我才能操作主窗口"))
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("主窗口")
        self.setGeometry(300, 300, 300, 200)
        
        btn = QPushButton("打开模态对话框", self)
        btn.clicked.connect(self.open_modal)
        self.setCentralWidget(btn)

    def open_modal(self):
        dialog = ModalDialog()
        dialog.exec()  # 关键:使用exec()阻塞主窗口

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

非模态对话框 (非阻塞式),对话框和主窗口可同时操作,互不影响。示例代码如下:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QPushButton, QVBoxLayout, QLabel

class ModelessDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("非模态对话框")
        layout = QVBoxLayout()
        layout.addWidget(QLabel("我是非模态对话框!可同时操作主窗口"))
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("主窗口")
        self.setGeometry(300, 300, 300, 200)
        
        btn = QPushButton("打开非模态对话框", self)
        btn.clicked.connect(self.open_modeless)
        self.setCentralWidget(btn)
        self.dialog = None  # 防止对话框被垃圾回收

    def open_modeless(self):
        if not self.dialog:
            self.dialog = ModelessDialog()
        self.dialog.show()  # 关键:使用show()不阻塞

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

提示:非模态对话框需保存引用(如self.dialog),否则可能被Python垃圾回收机制销毁。模态对话框因exec()会保持对象存活。