本文内容总结自王铭东老师的PyQt5快速入门,视频链接为:
【PyQt5 快速入门】 https://www.bilibili.com/video/BV1LT4y1e72X/?p=20&share_source=copy_web&vd_source=7f1982c56a437c7b00c3695687b7086d
前言
PyQt是pyhthon语言的GUI(开发图形用户界面)
安装PyQt
- 1.安装
pip install pyqt5
- 2.验证
进入python解释器
输入 from PyQt5 import QtWdgets
查看版本 from PyQt5.QtCore import *
print(QT_VERSION_STR)
第一个PyQt程序
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QWidget # 引入了PyQt5.QtWidgets模块,这个模块包含了基本的组件。
if __name__ == '__main__':
app = QApplication(sys.argv) # 每个PyQt5应用都必须创建一个应用对象。sys.argv是一组命令行参数的列表。
# print(sys.argv) # 'd:/python_Develop/pyq_code/demo_1.py' 这里是要执行的代码
w = QWidget() # QWidge控件是一个用户界面的基本控件,它提供了基本的应用构造器。默认情况下,构造器是没有父级的,没有父级的构造器被称为窗口(window)。
w.resize(500, 500) # resize()方法能改变控件的大小,这里的意思是窗口宽250px,高150px。
w.move(300, 300) # move()是修改控件位置的的方法。它把控件放置到屏幕坐标的(300, 300)的位置。注:屏幕坐标系的原点是屏幕的左上角。
w.setWindowTitle('My First PyQt Program') # 窗口添加了一个标题,标题在标题栏展示
w.show() # show()能让控件在桌面上显示出来。控件在内存里创建,之后才能在显示器上显示出来。
sys.exit(app.exec_()) # 程序进入循环等待状态,事件处理器这个时候开始工作。主循环从窗口上接收事件,并把事件传入到派发到应用控件里。
# 当调用exit()方法或直接销毁主控件时,主循环就会结束。sys.exit()方法能确保主循环安全退出。外部环境能通知主控件怎么结束。
模块介绍
PyQt中有非常多的功能模块,开发中最常用的功能模块主要有三个:
- QtCore:包含了核心的非GUI的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime类文件、进程与线程一起使用
- QtGui:包含了窗口系统、事件处理、2D图像、基本绘画、字体和文字类
- QtWidgets:包含了一些列创建桌面应用的UI元素
PyQt官网的所有模块,地址:https://www.riverbankcomputing.com/static/Docs/PyQt5/module_index.html#ref-module-index
C++具体实现的API文档,地址:https://doc.qt.io/qt-5/qtwidgets-module.html
基础组件介绍
按钮
按钮对应的控件名称为 QPushButton
,位于 PyQt5.QtWidgets
里面 类似按钮还有QRadioButton
# 导入按钮库
from PyQt5.QtWidgets import QPushButton
# 在窗口里面添加名字为“关闭"的按钮控件
btn = QPushButton("关闭")
# 设置按钮的父亲是当前窗口,等于是添加到窗口中显示
btn.setParent(w)
文本
纯文本控件名称为 QLabel
, 位于 PyQt5.QtWidgets
里面
纯文本控件仅仅作为标识显示而已,类似输入内容前的一段标签提示(账号 、密码)
from PyQt5.QtWidgets import QLabel
# 下面创建一个Label,然后调用方法指定父类
label = QLabel("我是文本: ", w)
# 设置父对象
label.setParent(w) #在创建的时候指定了父对象:label = QLabel("我是文本: ", w)
# 显示位置与大小 : x, y , w, h
label.setGeometry(20, 20, 30, 30)
输入框
输入框的控件名称为 QLineEdit
, 位于 PyQt5.QtWidgets
里面
from PyQt5.QtWidgets import QLineEdit
# 文本框
edit = QLineEdit(w)
edit.setPlaceholderText("请输入账号")
edit.setGeometry(55, 20, 200, 20)
调整窗口大小
from PyQt5.QtWidgets import QWidget
# 实例化一个窗口
w = QWidget()
# 窗口的大小
w.resize(300, 300)
窗口显示在屏幕的中
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QDesktopWidget
# 调整窗口在屏幕中央显示
center_pointer = QDesktopWidget().availableGeometry().center() # 获取当前屏幕可用区域的中心点
x = center_pointer.x() # 中心点的x坐标
y = center_pointer.y()
#print(w.frameGeometry()) # 得到当前窗口的左上角坐标、宽、高的PyQt5.QtCore.QRect
#print(w.frameGeometry().getRect()) # 得到当前窗口的左上角坐标、宽、高的元组
old_x, old_y, width, height = w.frameGeometry().getRect()
w.move(x - width / 2, y - height / 2)
设置窗口icon
可以下载icon图标网站:https://www.iconfont.cn/collections/index?spm=a313x.7781069.1998910419.3(阿里巴巴)
from PyQt5.QtGui import QIcon
# 设置图标
w.setWindowIcon(QIcon('1.png'))
# PyQt是可以隐藏windows默认的标题栏 现在软件90%都隐藏了,重新设置
布局
在Qt的组件中共有四个布局组件:Vertical Layout水平布局组件、Horizontal Layout垂直布局组件、Grid Layout网格布局组件、Form Layout表单布局组件
在Qt的源码中布局分为四大类:QBoxLayout盒子布局类、QGridLayout网格布局类、QFormLayout表单布局类、QStackedLayout抽屉布局类
前面的代码中都是直接实例化组件的对象,设置属性,而布局开始,在代码中类便被广泛的应用。
盒子布局类案例
盒子布局类又分为两个子类QVBoxLayout
和 QHBoxLayout
负责垂直和水平布局
垂直布局类的案例
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton, QGroupBox, QMainWindow
from PyQt5.QtCore import Qt
class MyWindow(QWidget):
def __init__(self):
# 调用父类的__init__方法,因为它里面有很多对UI空间的初始化操作
super().__init__()
# 设置大小
self.resize(300, 300)
# 设置标题
self.setWindowTitle("垂直布局")
# 垂直布局
layout = QVBoxLayout() # 创建了一个布局器
# 在布局器中增加一个伸缩量,类似于弹簧,里面的参数表示QSpacerItem的个数,默认值为零
# 下面共添加了四个布局伸缩量,比列为1:1:1:2
layout.addStretch(1)
# 按钮1
btn1 = QPushButton("按钮1")
# 添加到布局器中
# layout.addWidget(btn1, Qt.AlignmentFlag.AlignTop)
layout.addWidget(btn1)
# 添加伸缩量
layout.addStretch(1)
# 按钮2
btn2 = QPushButton("按钮2")
# 添加到布局器
layout.addWidget(btn2)
# 添加伸缩量
layout.addStretch(1)
# 按钮3
btn3 = QPushButton("按钮3")
# 添加到布局器
layout.addWidget(btn3)
# 添加伸量
layout.addStretch(2)
# 让当前窗口使用这个布局排列
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
# 创建一个QWidget子类
w = MyWindow()
w.show()
sys.exit(app.exec_())
水平布局类案例
import sys
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton, QMainWindow
from PyQt5.QtCore import Qt
class MyWindow(QWidget):
def __init__(self):
# 调用父类的__init__方法,因为它里面有很多对UI空间的初始化操作
super().__init__()
# 设置大小
self.resize(300, 300)
# 设置标题
self.setWindowTitle("水平布局")
# 水平布局
layout = QHBoxLayout() # 创建了一个布局器
# 在布局器中增加一个伸缩量,类似于弹簧,里面的参数表示QSpacerItem的个数,默认值为零
# 下面共添加了四个布局伸缩量,比列为2:1:1:2
layout.addStretch(2)
# 按钮1
btn1 = QPushButton("按钮1")
# 添加到布局器中
# layout.addWidget(btn1, Qt.AlignmentFlag.AlignTop)
layout.addWidget(btn1)
# 添加伸缩量
layout.addStretch(1)
# 按钮2
btn2 = QPushButton("按钮2")
# 添加到布局器
layout.addWidget(btn2)
# 添加伸缩量
layout.addStretch(1)
# 按钮3
btn3 = QPushButton("按钮3")
# 添加到布局器
layout.addWidget(btn3)
# 添加伸量
layout.addStretch(2)
# 让当前窗口使用这个布局排列
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
# 创建一个QWidget子类
w = MyWindow()
w.show()
sys.exit(app.exec_())
垂直、水平布局类综合案例
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGroupBox, QVBoxLayout, QHBoxLayout, QRadioButton
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui() # 把原本放在构造函数内部的布局代码,给分离出来放到init_ui(),保证了代码的清爽
def init_ui(self):
# 最外层的是垂直布局器,内部嵌套两部分:爱好组是垂直布局和性别组水平布局
container = QVBoxLayout()
# -----创建第1个组,添加多个组件-----
# hobby 主要是保证他们是一个组。
hobby_box = QGroupBox("爱好")
# v_layout 保证三个爱好是垂直摆放
v_layout = QVBoxLayout()
btn1 = QRadioButton("抽烟")
btn2 = QRadioButton("喝酒")
btn3 = QRadioButton("烫头")
# 添加到v_layout中
v_layout.addWidget(btn1)
v_layout.addWidget(btn2)
v_layout.addWidget(btn3)
# 把v_layout添加到hobby_box中
hobby_box.setLayout(v_layout)
# -----创建第2个组,添加多个组件-----
# 性别组
gender_box = QGroupBox("性别")
# 性别容器
h_layout = QHBoxLayout()
# 性别选项
btn4 = QRadioButton("男")
btn5 = QRadioButton("女")
# 追加到性别容器中
h_layout.addWidget(btn4)
h_layout.addWidget(btn5)
# 添加到 box中
gender_box.setLayout(h_layout)
# 把爱好的内容添加到容器中
container.addWidget(hobby_box)
# 把性别的内容添加到容器中
container.addWidget(gender_box)
# 设置窗口显示的内容是最外层容器 布局器可以嵌套
self.setLayout(container)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
网格布局类案例
网格布局又称为九宫格布局
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QLineEdit, QGridLayout
class MyWindow(QWidget):
def __init__(self): # 构造函数
super().__init__() # 初始化父类的构造函数
self.init_ui() # 将布局代码分离到一起,保持代码清爽
def init_ui(self):
self.setWindowTitle("计算器") # 设置标题
# 准备数据
data = {
0: ["%", "CE", "C", "DEL"],
1: ["1/x", "x^2", "x^(1/2)", "/"],
2: ["7", "8", "9", "*"],
3: ["4", "5", "6", "-"],
4: ["1", "2", "3", "+"],
5: ["+/-", "0", ".", "="]
}
# 整体垂直布局 相当外部垂直布局内嵌套一个输入框和一个网格布局
layout = QVBoxLayout()
# 输入框
edit = QLineEdit()
edit.setPlaceholderText("请输入内容") # 不输入东西时的默认显示
# 把输入框添加到容器中
layout.addWidget(edit)
# 网格布局
grid = QGridLayout()
# 循环创建追加进去
for line_number, line_data in data.items():
# 此时line_number是第几行,line_data是当前行的数据,例如line_number为0时,line_data为['7', '8', '9', '+', '(']
for col_number, number in enumerate(line_data):
# 此时col_number是第几列,number是要显示的符号 例如:col_number为0,number为7 colo_number为1,number为8
btn = QPushButton(number) # 创建显示number的按钮
grid.addWidget(btn, line_number, col_number) # 网格布局加入按钮,要制定横纵坐标
# 把网格布局追加到容器中
layout.addLayout(grid) # 添加组件是:addWidget 添加布局是:addLayout
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
表单布局类案例
一般适用于提交数据form表单。比如: 登录,注册类似的场景
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QVBoxLayout, QFormLayout, QLineEdit, QPushButton, QApplication, QWidget
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
# 设定当前Widget的宽高(可以拉伸大小)
# self.resize(300, 200)
# 禁止改变宽高(不可以拉伸)
self.setFixedSize(300, 150)
# 外层容器 外层布局为垂直布局
container = QVBoxLayout()
# 表单容器
form_layout = QFormLayout()
# 创建1个输入框
edit = QLineEdit()
edit.setPlaceholderText("请输入账号")
form_layout.addRow("账号:", edit)
# 创建另外1个输入框
edit2 = QLineEdit()
edit2.setPlaceholderText("请输入密码")
form_layout.addRow("密码:", edit2)
# 将from_layout添加到垂直布局器中
container.addLayout(form_layout)
# 按钮
login_btn = QPushButton("登录")
login_btn.setFixedSize(100, 30)
# 把按钮添加到容器中,并且指定它的对齐方式
container.addWidget(login_btn, alignment=Qt.AlignRight)
# 设置当前Widget的布局器,从而显示这个布局器中的子控件
self.setLayout(container)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
抽屉布局类案例
QStackedLayout类提供了多页面切换的布局,一次只能看到一个界面。
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QStackedLayout, QLabel
class Window1(QWidget):
def __init__(self):
super().__init__()
QLabel("我是抽屉1要显示的内容", self)
self.setStyleSheet("background-color:green;") # 设置背景为绿色
class Window2(QWidget):
def __init__(self):
super().__init__()
QLabel("我是抽屉2要显示的内容", self)
self.setStyleSheet("background-color:red;") # 设置背景为红色
class MyWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.create_stacked_layout() # 创建布局器
self.init_ui()
def create_stacked_layout(self): # 不加self就是局部变量,因为创建完后后面的代码还会用
# 创建堆叠(抽屉)布局
self.stacked_layout = QStackedLayout()
# 创建单独的Widget
win1 = Window1()
win2 = Window2()
# 将创建的2个Widget添加到抽屉布局器中 这两个widget的索引为0, 1 默认显示下标为0的
self.stacked_layout.addWidget(win1)
self.stacked_layout.addWidget(win2)
def init_ui(self):
# 设置Widget大小以及固定宽高
self.setFixedSize(300, 270)
# 1. 创建整体的布局器
container = QVBoxLayout()
# 2. 创建1个要显示具体内容的子Widget
widget = QWidget()
widget.setLayout(self.stacked_layout) # 把上面创建的抽屉布局器,设置为当前布局器
widget.setStyleSheet("background-color:grey;")
# 3. 创建2个按钮,用来点击进行切换抽屉布局器中的Widget
btn_press1 = QPushButton("抽屉1")
btn_press2 = QPushButton("抽屉2")
# 给按钮添加事件(即点击后要调用的函数)
btn_press1.clicked.connect(self.btn_press1_clicked)
btn_press2.clicked.connect(self.btn_press2_clicked)
# 4. 将需要显示的空间添加到布局器中
container.addWidget(widget)
container.addWidget(btn_press1)
container.addWidget(btn_press2)
# 5. 设置当前要显示的Widget,从而能够显示这个布局器中的控件
self.setLayout(container)
def btn_press1_clicked(self):
# 设置抽屉布局器的当前索引值,即可切换显示哪个Widget
self.stacked_layout.setCurrentIndex(0)
def btn_press2_clicked(self):
# 设置抽屉布局器的当前索引值,即可切换显示哪个Widget
self.stacked_layout.setCurrentIndex(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
窗口
在Qt中窗口类仅有三个,分别为:QWidget、QMainWindow、QDialog
QWidget类演示
控件和窗口的父类,自由高度(什么都没有),没有划分菜单、工具栏、状态栏、主窗口等区域。
适合于创建纯净的窗口!!
import sys
from PyQt5.QtWidgets import QWidget, QLabel , QApplication
class mywnd(QWidget):
def __init__(self):
super(mywnd, self).__init__()
self.initUI()
def initUI(self):
label = QLabel("这是文字~~" )
label.setStyleSheet("font-size:30px;color:red")
label.setParent(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = mywnd()
#设置窗口标题
w.setWindowTitle("qwidget")
# 展示窗口
w.show()
# 程序进行循环等待状态
app.exec()
QMainWindow类演示
它是QWidget的子类,意味着可以在QWidget上加东西,包含菜单栏、工具栏、状态栏、标题栏等,中间部分则为主窗口区域
import sys
from PyQt5.QtWidgets import QMainWindow, QLabel, QApplication
class MyWindow(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
label = QLabel("这是文字~~")
label.setStyleSheet("font-size:30px;color:red")
# 调用父类中的menuBar,对菜单栏进行操作 MainWindow是QWidget的子类可以在其基础上进行扩展
menu = self.menuBar()
file_menu = menu.addMenu("文件")
file_menu.addAction("新建")
file_menu.addAction("打开")
file_menu.addAction("保存")
edit_menu = menu.addMenu("编辑")
edit_menu.addAction("复制")
edit_menu.addAction("粘贴")
edit_menu.addAction("剪切")
# 设置中心内容显示
self.setCentralWidget(label) # QMainWindow与QWidget不同之处 由于QMainWindow分三个部分,顶部的菜单来,最下面的状态栏和中间的内容区域
# 而QWidget这个区域都是显示内容,故要想让文本正常显示,必须设置到内容区域才能正常显示。
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
# 设置窗口标题
w.setWindowTitle("我是窗口标题....")
# 展示窗口
w.show()
# 程序进行循环等待状态
app.exec()
QDialog类演示
弹出来的对话窗口的基类
import sys
from PyQt5.QtWidgets import QDialog, QPushButton, QApplication
class MyDialog(QDialog):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
ok_btn = QPushButton("确定", self)
ok_btn.setGeometry(50, 50, 100, 30)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyDialog()
# 设置窗口标题
w.setWindowTitle("对话框")
# 展示窗口
w.show()
# 程序进行循环等待状态
app.exec()
信号和槽(超级重点)
信号(signal)
在Qt中可以将信号理解为事件或者状态,例如:按钮点击 、内容发生改变 、窗口的关闭事件 或者是 check选中了的状态。
当程序触发了某种状态或者发生了某种事件,即可发射出来一个信号
。
槽(slot)
若想捕获这个信号,然后执行相应的逻辑代码,那么就需要使用到 槽 , 槽实际上是一个函数, 当信号发射出来后,会执行与之绑定的槽函数
信号和槽链接
为了能够实现,当点击某个按钮时执行某个逻辑,需要把具体的信号和具体的槽函数绑定到一起.
对象.信号.connect(槽函数)
例如: btn.clicked.connect(self.click_my_btn) btn为按钮对象 clicked为点击信号 click_my_btn为槽函数
信号可以分为Qt自带的和我们自定义两种。
信号和槽的案例一:Qt自带信号
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class MyWindow(QWidget):
def __init__(self):
super().__init__() # 初始化父类
self.init_ui()
def init_ui(self):
# 更改当前Widge的宽高
self.resize(500, 300)
# 创建一个按钮
btn = QPushButton("点我点我", self)
# 设置窗口位置、宽高
btn.setGeometry(200, 200, 100, 30)
# 将按钮被点击时触发的信号与我们定义的函数click_my_btn进行绑定,即当按钮被触发会执行click_my_btn
btn.clicked.connect(self.click_my_btn)
def click_my_btn(self, arg):
# 槽函数,点击按钮则调用该函数
# 这里的参数正好是信号发出,传递的参数
print("点击按钮啦~", arg)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
信号和槽的案例二:自定义信号
import sys
import time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class MyWindow(QWidget):
# 声明一个信号 只能放在函数的外面
my_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.resize(300, 200)
btn = QPushButton("开始检测", self)
btn.setGeometry(100, 150, 100, 30)
# 绑定按钮的点击,点击按钮则开始检测
btn.clicked.connect(self.check)
# 绑定信号和槽
self.my_signal.connect(self.my_slot)
def my_slot(self, msg):
# 更新内容
print(msg)
def check(self):
for i, ip in enumerate(["192.168.1.%d" % x for x in range(1, 255)]):
msg = "模拟,正在检查 %s 上的漏洞...." % ip
if i % 5 == 0:
# 表示发射信号 对象.信号.发射(参数)
self.my_signal.emit(msg + "【发现漏洞】") # 相当于调用了my_slot函数
time.sleep(0.01)
'''
该自定义信号的执行顺序是: 先定义出自定义信号,接着将自定义信号和自定义槽函数绑定,截杀利用emit发送自定义信号,从而执行自定义槽函数
'''
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())