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, QWidgetif __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程序")# 创建buttonbtn = 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,QPushButtonif __name__ == '__main__':app = QApplication(sys.argv)w = QWidget()w.setWindowTitle('label')# 在创建对象的时候指定父对象btn = QPushButton('btn',w)lab = QLabel('账号',w)# 设置显示位置与大小,x,y,w,hlab.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, QLabelif __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,QDesktopWidgetif __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 sysfrom PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButtonclass MyWindow(QWidget):def __init__(self):super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作# 设置大小self.resize(300, 300)# 设置标题self.setWindowTitle("垂直布局")# 垂直布局,创建布局器,是一种约束layout = QVBoxLayout()# 按钮1btn1 = QPushButton("按钮1")# 将btn1添加到布局器里面,不需要再设置父对象layout.addWidget(btn1)# 按钮2btn2 = QPushButton("按钮2")layout.addWidget(btn2)# 按钮3btn3 = 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 sysfrom PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButtonclass MyWindow(QWidget):def __init__(self):super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作# 设置大小self.resize(300, 300)# 设置标题self.setWindowTitle("垂直布局")# 垂直布局,创建布局器,是一种约束layout = QVBoxLayout()# 按钮1btn1 = QPushButton("按钮1")# 将btn1添加到布局器里面,不需要再设置父对象layout.addWidget(btn1)# 按钮2btn2 = QPushButton("按钮2")layout.addWidget(btn2)# 按钮3btn3 = 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 sysfrom PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QPushButtonclass MyWindow(QWidget):def __init__(self):super().__init__() # 一定要调用父类的__init__方法,因为它里面有很多对于UI空间的初始化操作# 设置大小self.resize(300, 300)# 设置标题self.setWindowTitle("水平布局")# 垂直布局,创建布局器,是一种约束layout = QHBoxLayout()# 按钮1btn1 = QPushButton("按钮1")# 将btn1添加到布局器里面,不需要再设置父对象layout.addWidget(btn1)# 按钮2btn2 = QPushButton("按钮2")layout.addWidget(btn2)# 按钮3btn3 = 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 sysfrom PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QRadioButtonclass 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 sysfrom 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 sysfrom 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()# 循环创建btnfor 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 sysfrom 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()# 创建单独的widgetwin1 = 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()# 创建一个要显示的具体内容的子widgetwidget = QWidget()widget.setLayout(self.stacked_layout)widget.setStyleSheet("background-color:gray;")# 创建2个按钮,用来点击进行切换布局器中的widgetbtn_press1 = QPushButton("抽屉1")btn_press2 = QPushButton("抽屉2")# 给按钮添加事件,即点击及进行切换抽屉布局器中的widget# 如果按钮被点击连接事件self.btn_press1_clickedbtn_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):# 设置抽屉布局器的当前索引值,即可切换显示哪个widgetself.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 sysfrom 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 sysfrom 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 sysfrom PyQt5.QtWidgets import QDialog, QPushButton, QApplicationclass 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 sysfrom PyQt5.QtWidgets import QApplication, QWidget, QPushButtonclass 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 timefrom PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QPushButtonclass 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 osfrom PyQt5.QtWidgets import QApplication
from PyQt5 import uicif __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 uicclass 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.lineEditself.password = self.ui.lineEdit_2login_btn = self.ui.pushButton_3forget_btn = self.ui.pushButton_4text_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()