Qt是基于C++实现的GUI,而PyQt就是用python调用Qt.
PyQt中有很多的功能模块,开发最常用的模块功能主要有3个
1) QtCore:包含核心的非GHI的功能,主要和时间,文件与文件夹,各种数据,流,URLs,进程与线程一起使用
2) QtGUi:包含窗口系统,事件处理,2D图像,基本绘画,字体和文字类
3) QtWidget:包含了一些创建桌面应用的UI元素
Qt for Python Quick start — Qt for Python
1. 安装环境
1.1 创建虚拟环境
1.2 安装PyQt5库
1.3 安装PyQt5时报错,
下载工具,进行安装 Microsoft C++ 生成工具 - Visual Studio
2.基本UI
# _*_ coding:utf-8 _*_
# @Time : 16:41
# @Author: george
# @File : 基本UI.py
# @Comment: ***
import sys
from PyQt5.QtWidgets import QApplication, QWidget
if __name__ == '__main__':
# 创建QApplication对象-传递列表给QApplication类,这个参数一般是当前程序对象运行环境参数
app = QApplication(sys.argv)
# 创建界面对象
w = QWidget()
# 设置窗口标题
w.setWindowTitle("第一个PyQt")
# 展示窗口
w.show()
# 程序进入循环等待状态,事件监测循环
app.exec_()
效果图
3.组件
3.1 Button
# _*_ coding:utf-8 _*_
# @Time : 17:16
# @Author: george
# @File : 按钮.py
# @Comment: ***
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle("第一个PyQt程序")
# 创建button
btn = QPushButton("按钮")
# 将按钮放入窗口显示
btn.setParent(w)
w.show()
app.exec_()
效果图:
3.2 Label
# _*_ coding:utf-8 _*_
# @Time : 17:29
# @Author: george
# @File : label.py
# @Comment: ***
import sys
from PyQt5.QtWidgets import QLabel, QWidget,QApplication,QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle('label')
# 在创建对象的时候指定父对象
btn = QPushButton('btn',w)
lab = QLabel('账号',w)
# 设置显示位置与大小,x,y,w,h
lab.setGeometry(20,20,300,40)
w.show()
app.exec_()
效果图:
3.3 LineEdit
# _*_ coding:utf-8 _*_
# @Time : 19:04
# @Author: george
# @File : lineEdit.py
# @Comment: ***
import sys
from PyQt5.QtWidgets import QWidget, QLineEdit, QPushButton, QApplication, QLabel
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle('lineedit')
# 标签
lab = QLabel('账号', w)
lab.setGeometry(20, 20, 30, 20)
# 输入框
edit = QLineEdit(w)
edit.setPlaceholderText("请输入账号")
edit.setGeometry(55, 20, 330, 20)
# 按钮
btn = QPushButton('注册', w)
btn.setGeometry(55, 50, 30, 20)
w.show()
app.exec_()
效果图:
3.4 窗口设置
窗口大小,背景图,自动居中等相关设置
# _*_ coding:utf-8 _*_
# @Time : 19:04
# @Author: george
# @File : lineEdit.py
# @Comment: ***
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QLineEdit, QPushButton, QApplication, QLabel,QDesktopWidget
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.setWindowTitle('lineedit')
# 调整窗口大小
w.resize(800, 600)
# 将窗口移动到屏幕0,0位置
w.move(0,0)
# 自动将app显示在屏幕中间
x,y,width,high = QDesktopWidget().availableGeometry().getRect()
app_x,app_y,app_width,app_high = w.frameGeometry().getRect()
w.move(width//2-app_width//2,high//2-app_high//2)
# 设置标题栏图标
w.setWindowIcon(QIcon("pyq.png"))
# 标签
lab = QLabel('账号', w)
lab.setGeometry(20, 20, 30, 20)
# 输入框
edit = QLineEdit(w)
edit.setPlaceholderText("请输入账号")
edit.setGeometry(55, 20, 330, 20)
# 按钮
btn = QPushButton('注册', w)
btn.setGeometry(55, 50, 30, 20)
w.show()
app.exec_()
效果图:
4.布局
在Qt里面布局分为四大类
QBoxLayout,QGridLayout,QFormLayout,QStackedLayout
4.1 QBoxLayout 盒子布局
4.1.1 垂直布局器
盒子布局,一般使用QHBoxLayout(水平) 和QVBoxLayout(垂直布局)两个子类
创建布局器->使用布局器->将组件添加到布局器里面
# _*_ coding utf-8 _*_
# george
# time: 2024/3/14下午5:54
# name: 垂直布局.py
# comment:
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton
class MyWindow(QWidget):
def __init__(self):
super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作
# 设置大小
self.resize(300, 300)
# 设置标题
self.setWindowTitle("垂直布局")
# 垂直布局,创建布局器,是一种约束
layout = QVBoxLayout()
# 按钮1
btn1 = QPushButton("按钮1")
# 将btn1添加到布局器里面,不需要再设置父对象
layout.addWidget(btn1)
# 按钮2
btn2 = QPushButton("按钮2")
layout.addWidget(btn2)
# 按钮3
btn3 = QPushButton("按钮3")
layout.addWidget(btn3)
# 让当前窗口使用这个布局器(排列规则)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
默认情况下,三个按钮的布局距离是平均分配
# _*_ coding utf-8 _*_
# george
# time: 2024/3/14下午5:54
# name: 垂直布局.py
# comment:
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton
class MyWindow(QWidget):
def __init__(self):
super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作
# 设置大小
self.resize(300, 300)
# 设置标题
self.setWindowTitle("垂直布局")
# 垂直布局,创建布局器,是一种约束
layout = QVBoxLayout()
# 按钮1
btn1 = QPushButton("按钮1")
# 将btn1添加到布局器里面,不需要再设置父对象
layout.addWidget(btn1)
# 按钮2
btn2 = QPushButton("按钮2")
layout.addWidget(btn2)
# 按钮3
btn3 = QPushButton("按钮3")
layout.addWidget(btn3)
# 添加伸缩器(理解为一个弹簧),这个参数2指的是比例关系,是占据的空间距离比
layout.addStretch(2)
# 让当前窗口使用这个布局器(排列规则)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
4.1.2 水平布局器
# _*_ coding utf-8 _*_
# george
# time: 2024/3/14下午5:54
# name: 垂直布局.py
# comment:
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QPushButton
class MyWindow(QWidget):
def __init__(self):
super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作
# 设置大小
self.resize(300, 300)
# 设置标题
self.setWindowTitle("水平布局")
# 垂直布局,创建布局器,是一种约束
layout = QHBoxLayout()
# 按钮1
btn1 = QPushButton("按钮1")
# 将btn1添加到布局器里面,不需要再设置父对象
layout.addWidget(btn1)
# 按钮2
btn2 = QPushButton("按钮2")
layout.addWidget(btn2)
# 按钮3
btn3 = QPushButton("按钮3")
layout.addWidget(btn3)
# 添加伸缩器(理解为一个弹簧),这个参数2指的是比例关系,是占据的空间距离比
layout.addStretch(3)
# 让当前窗口使用这个布局器(排列规则)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
4.12.3 布局嵌套
# _*_ coding utf-8 _*_
# george
# time: 2024/3/14下午6:59
# name: 布局器嵌套.py
# comment:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QRadioButton
class MyQwidget(QWidget):
def __init__(self):
super(MyQwidget, self).__init__()
full_layout = QVBoxLayout()
top_layout = QVBoxLayout()
lab1 = QLabel("爱好")
q1 = QRadioButton("抽烟")
q2 = QRadioButton("喝酒")
q3 = QRadioButton("烫头")
top_layout.addWidget(lab1)
top_layout.addWidget(q1)
top_layout.addWidget(q2)
top_layout.addWidget(q3)
full_layout.addLayout(top_layout)
lab2 = QLabel("性别")
full_layout.addWidget(lab2)
botoom_layout = QHBoxLayout()
q4 = QRadioButton("男")
q5 = QRadioButton("女")
botoom_layout.addWidget(q4)
botoom_layout.addWidget(q5)
full_layout.addLayout(botoom_layout)
self.setLayout(full_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyQwidget()
w.show()
app.exec()
注意: self.setLayout(full_layout),都是对象设置布局器,所以对于上下两个布局,也是需要两个被设置对象的.所以上面写的代码是有问题的,需要盒子
hobby_box = QGroupBox("爱好")
# _*_ coding utf-8 _*_
# george
# time: 2024/3/15下午1:37
# name: 布局嵌套2.0.py
# comment:
import sys
from PyQt5.QtWidgets import *
class MyWindows(QWidget):
def __init__(self):
super(MyWindows, self).__init__()
self.init_UI()
def init_UI(self):
# 最外层的垂直布局,包含爱好和性别
container = QVBoxLayout()
# 创建第一个组,添加第一个组件,
# v_layout保证3个爱好是垂直摆放
v_layout = QVBoxLayout()
hobby_box = QGroupBox("爱好")
btn1 = QRadioButton("抽烟")
btn2 = QRadioButton("喝酒")
btn3 = QRadioButton("烫头")
# 将三个btn添加到v_layout里面
v_layout.addWidget(btn1)
v_layout.addWidget(btn2)
v_layout.addWidget(btn3)
hobby_box.setLayout(v_layout)
# 创建第二个组,添加多个组件
# 性别组
h_layout = QHBoxLayout()
gender_box = QGroupBox("性别")
btn4 = QRadioButton("男")
btn5 = QRadioButton("女")
h_layout.addWidget(btn4)
h_layout.addWidget(btn5)
gender_box.setLayout(h_layout)
# 将爱好的内容添加到容器中
container.addWidget(hobby_box)
container.addWidget(gender_box)
# 设置窗口显示的内容是最外层容器
self.setLayout(container)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyWindows()
window.show()
app.exec_()
4.2 QGridLayout 网格布局
# _*_ coding utf-8 _*_
# george
# time: 2024/3/15下午2:03
# name: 网格布局.py
# comment:
import sys
from PyQt5.QtWidgets import *
class MyWindow(QWidget):
def __init__(self):
super(MyWindow, self).__init__()
self.initUI()
def initUI(self):
# 设置标题头
self.setWindowTitle("计算器")
# 准备数据
data = {
0: ["7", "8", "9", "+", "("],
1: ["4", "5", "6", "-", ")"],
2: ["1", "2", "3", "*", "<-"],
3: ["0", ".", "=", "/", "C"]
}
# 整体设置垂直布局
layout = QVBoxLayout()
# 设置输入框
edit = QLineEdit()
edit.setPlaceholderText("请输入内容")
# 将输入框添加到容器中
layout.addWidget(edit)
# 网格布局
grid = QGridLayout()
# 循环创建btn
for row_number,data_list in data.items():
for col_number,i in enumerate(data_list):
btn = QPushButton(i)
grid.addWidget(btn,row_number,col_number)
# 将网格布局追加到容器中
layout.addLayout(grid)
# 设置最外层容器的布局为垂直布局
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
4.3 QFormaLayout
一般适用于提交form表单,比如:登陆,注册等类似场景
4.4 StackedLayout 抽屉布局
提供了多页面切换的布局,一次只能看到一个界面,抽屉布局
对于抽屉布局器而言就是,一个widget使用了抽屉布局器,而抽屉布局器里面有2个或是多个子widget,这里面的子widget就是将要显示的界面.
# _*_ coding utf-8 _*_
# george
# time: 2024/3/15下午3:12
# name: 抽屉布局.py
# comment:
import sys
from PyQt5.QtWidgets import *
class Window1(QWidget):
def __init__(self):
super(Window1, self).__init__()
QLabel("我是抽屉1要显示的内容",self)
self.setStyleSheet("background-color:green;")
class Window2(QWidget):
def __init__(self):
super(Window2, self).__init__()
QLabel("我是抽屉2要显示的内容",self)
self.setStyleSheet("background-color:red;")
class MyWindow(QWidget):
def __init__(self):
super(MyWindow, self).__init__()
self.create_stacked_layout()
self.initUI()
def create_stacked_layout(self):
# 创建抽屉布局
self.stacked_layout = QStackedLayout()
# 创建单独的widget
win1 = Window1()
win2 = Window2()
# 将创建的2个widget添加到抽屉布局器中
self.stacked_layout.addWidget(win1)
self.stacked_layout.addWidget(win2)
def initUI(self):
# 设置widget大小以及固定宽高
self.setFixedSize(300, 270)
# 创建整体布局器
container = QVBoxLayout()
# 创建一个要显示的具体内容的子widget
widget = QWidget()
widget.setLayout(self.stacked_layout)
widget.setStyleSheet("background-color:gray;")
# 创建2个按钮,用来点击进行切换布局器中的widget
btn_press1 = QPushButton("抽屉1")
btn_press2 = QPushButton("抽屉2")
# 给按钮添加事件,即点击及进行切换抽屉布局器中的widget
# 如果按钮被点击连接事件self.btn_press1_clicked
btn_press1.clicked.connect(self.btn_press1_clicked)
btn_press2.clicked.connect(self.btn_press2_clicked)
# 将要显示的控件添加到container布局器中
container.addWidget(widget)
container.addWidget(btn_press1)
container.addWidget(btn_press2)
# 设置widget显示的内容使用的是container布局器
self.setLayout(container)
def btn_press1_clicked(self):
# 设置抽屉布局器的当前索引值,即可切换显示哪个widget
self.stacked_layout.setCurrentIndex(0)
def btn_press2_clicked(self):
self.stacked_layout.setCurrentIndex(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
5. 窗口
在Qt中,生成窗口有三种方式:
Qwidget,QMainWindow,QDialog
5.1 QWidget
空间和窗口的父类,自由度高(什么东西都没有),没有划分菜单,工具栏,状态栏,主窗口等区域
import sys
from PyQt5.QtWidgets import *
class MyWindow(QWidget):
def __init__(self):
super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作
self.initUI()
def initUI(self):
self.resize(300,300)
label = QLabel("这是文字",self)
label.setStyleSheet("font-size:30px;color:red")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
5.2 QMainWindow
是QWidget的子类,包含菜单栏,工具栏,状态栏,标题栏,中间部分为主窗口区域
# _*_ coding utf-8 _*_
# george
# time: 2024/3/15下午6:43
# name: 窗口.py
# comment:
import sys
from PyQt5.QtWidgets import *
class MyWindow(QMainWindow):
def __init__(self):
super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作
self.initUI()
def initUI(self):
self.resize(300,300)
label = QLabel("这是文字")
label.setStyleSheet("font-size:30px;color:red")
# 调用父类的menuBar,对菜单栏进行操作
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)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
app.exec()
5.3 QDialog
对话窗口的基类 ,不作为主窗口只是作为按钮点击时的弹窗
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/8 21:46
# @Author : George
import sys
from PyQt5.QtWidgets import QDialog, QPushButton, QApplication
class MyDialog(QDialog):
def __init__(self):
super().__init__()
self.UI_init()
def UI_init(self):
ok_btn = QPushButton("确定", self)
ok_btn.setGeometry(50,50,100,30)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyDialog()
w.setWindowTitle("Dialog")
w.show()
sys.exit(app.exec_())
奇怪的是窗口上面有?
6.信号与槽
6.1 信号(Signal)
信号其实就是事件(按钮点击,内容改变,窗口关闭)或者是状态(选中或是切换)当程序触发了某种状态或是发生了某种事件,就可发射出来一个信号
6.2 槽(slot)
若想捕获这个信号,并执行相应的逻辑代码,就需要用到槽。槽实际上是一个函数,当信号发射出来之后会执行与之绑定的槽函数。
6.3 将信号与槽连接
为了实现,当点击某个按钮时执行某个逻辑,需要将具体的信号和具体的槽函数绑定到一起。
6.3.1 接收信号
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/9 23:04
# @Author : George
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# 重新设置窗口的宽高
self.resize(500, 300)
btn = QPushButton('点我点我',self)
btn.setGeometry(200,200,100,30)
btn.clicked.connect(self.click_my_btn)
def click_my_btn(self,mgs):
#槽函数,点击按钮时就是调用此函数。这里的参数是信号发出时传递的参数
print("我正在点击按钮",mgs)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
6.3.2 自定义信号
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/9 23:33
# @Author : George
import sys
import time
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class Window(QWidget):
# 自定义一个信号,并且只能作为类属性
my_signal = pyqtSignal(str) # 定义的时候声明传递的参数是字符串
def __init__(self):
super().__init__()
self.initUI()
def initUI(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.3.%s" % x for x in range(1, 255)]):
print("模拟===正在%s检测系统漏洞......" % ip)
if i % 5 == 0:
# 这里是发射信号,并将参数传递给绑定的槽函数,传递的参数是字符串
self.my_signal.emit("【发现漏洞】") # 这里就是在调用函数
time.sleep(0.1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
7.Qt Designer
windows版本下载链接,使用迅雷会很快
https://build-system.fman.io/static/public/files/Qt%20Designer%20Setup.exe
7.1 Qt Designer的基本使用
整体界面
1)创建窗口
2)设计界面布局
3)预览效果
4)保存UI文件
按ctrl+s保存UI文件
7.2 python调用ui文件显示界面
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/11 21:22
# @Author : George
import sys
import os
from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
if __name__ == '__main__':
app = QApplication(sys.argv)
# 调用UI设计
ui = uic.loadUi(os.path.join(os.path.expanduser("~"),'Desktop/mini_ui.ui'))
# 展示窗口
ui.show()
app.exec()
显示结果:
7.3 给控件添加信号与槽
信号与槽的编辑器,但是并不建议使用因为太受限制,所以一般都是通过Qt Designer设计好了界面之后,自己在代码里面设计信号与槽
在代码里面调用设计好的控件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/11 21:54
# @Author : George
import sys
from pathlib import Path
from PyQt5.QtWidgets import QApplication, QWidget, QTextBrowser
from PyQt5 import uic
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.init_UI()
def init_UI(self):
ui_path = Path.home() / "Desktop/login.ui"
self.ui = uic.loadUi(ui_path)
# print(self.ui) # ui文件中最顶层对象 Form
# print(self.ui.__dict__) # 获取当前对象的所有属性,并且按照键值对的形式呈现
# print(self.ui.label) # 最顶层对象中间嵌套的QLabel
# print(self.ui.label.text()) # label的文本
# 正文开始
self.user_name = self.ui.lineEdit
self.password = self.ui.lineEdit_2
login_btn = self.ui.pushButton_3
forget_btn = self.ui.pushButton_4
text_browser = self.ui.textBrowser
# 登录按钮绑定槽函数
login_btn.clicked.connect(self.login)
def login(self):
"""实现登录逻辑:注意获取到输入框的内容是在点击按钮时,一开始的输入框是空的"""
if self.user_name.text() == "admin" and self.password.text() == "123":
print("Login Successful")
else:
print("username or password is wrong")
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.ui.show()
app.exec()