PyQt5笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PyQt5笔记
⼀、简介
pyqt5做为Python的⼀个模块,它有620多个类和6000个函数和⽅法。
这是⼀个跨平台的⼯具包,它可以运⾏在所有主要的操作系统,包括UNIX,Windows,Mac OS。
pyqt5是双重许可。
开发者可以在GPL和商业许可之间进⾏选择。
pyqt5的类别分为⼏个模块,包括以下:
QtCore
QtGui
QtWidgets
QtMultimedia
QtBluetooth
QtNetwork
QtPositioning
Enginio
QtWebSockets
QtWebKit
QtWebKitWidgets
QtXml
QtSvg
QtSql
QtTest
QtCore:包含了核⼼的⾮GUI功能。
此模块⽤于处理时间、⽂件和⽬录、各种数据类型、流、URL、MIME类型、线程或进程。
QtGui包含类窗⼝系统集成、事件处理、⼆维图形、基本成像、字体和⽂本。
qtwidgets模块包含创造经典桌⾯风格的⽤户界⾯提供了⼀套UI元素的类。
QtMultimedia包含的类来处理多媒体内容和API来访问相机和收⾳机的功能。
Qtbluetooth模块包含类的扫描设备和连接并与他们互动。
描述模块包含了⽹络编程的类。
这些类便于TCP和IP和UDP客户端和服务器的编码,使⽹络编程更容易和更便携。
Qtpositioning包含类的利⽤各种可能的来源,确定位置,包括卫星、Wi-Fi、或⼀个⽂本⽂件。
Enginio模块实现了客户端库访问Qt云服务托管的应⽤程序运⾏时。
Qtwebsockets模块包含实现WebSocket协议类。
QtWebKit包含⼀个基于Webkit2图书馆Web浏览器实现类。
Qtwebkitwidgets包含的类的基础webkit1⼀⽤于qtwidgets应⽤Web浏览器的实现。
QtXml包含与XML⽂件的类。
这个模块为SAX和DOM API提供了实现。
QtSvg模块提供了显⽰SVG⽂件内容的类。
可伸缩⽮量图形(SVG)是⼀种描述⼆维图形和图形应⽤的语⾔。
QtSql模块提供操作数据库的类。
QtTest包含的功能,使pyqt5应⽤程序的单元测试
1 安装PyQt5
直接pip安装PyQt5,可能是pip/pip3
pip install PyQt51
由于Qt Designer已经在Python3.5版本从PyQt5转移到了tools,因此我们还需要安装pyqt5-tools
pip install pyqt5-tools1
到此,PyQt5就安装完成了
也可使⽤国内源安装
pip install -i https:///simple pyqt5
pip install -i https:///simple pyqt5-tools
通过下⾯若⼲可选的操作来检查是否已经安装成功:
Win+S呼出Cornata主⾯板(搜索框),输⼊designer,如果看到跟下图类似的结果说明PyQt Designer已经被安装
在cmd中输⼊pyuic5,如果返回“Error: one input ui-file must be specified”说明安装成功。
测试代码
import sys
from PyQt5 import QtWidgets,QtCore
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
widget.resize(360,360)
widget.setWindowTitle("hello,pyqt5")
widget.show()
sys.exit(app.exec_())
2 PyQt5汉化
把汉化包复制进去,就是下图的第⼀个⽂件就是汉化包,找到pyqt-tools/ Qt/ bin⽂件夹,例:D:\python\Lib\site-
packages\pyqt5_tools\Qt\bin,在这个⽬录下建⽴⼀个名为translations的空⽂件夹。
将汉化包⽂件(designer_zh_CN.qm)复制到建⽴的translations⽂件夹内,即可实现Qt Designer的汉化。
3 QT Designer+pycharm配合使⽤
QtDesigner:设计UI界⾯,但⽣成.ui⽂件,需要通过PyUIC转换成.py⽂件
3.1 配置 Qt Designer
Working directory:$FileDir$
3.2 配置PyUIC:
Program:python的安装⽬录下的python.exe⽂件
Arguments:-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
3.3 配置Pyrcc:
Program:python的安装⽬录下的Scripts⽂件夹的pyrcc5.exe⽂件
Arguments:$FileName$ -o $FileNameWithoutExtension$_rc.py
3.4 .ui转换成.py:
启动qt designer
先点击Designer设计ui界⾯,保存后如图⽣成了ui⽂件
记得选这个创建窗⼝
然后点击ui⽂件,点击PyUIC,就能将ui⽂件转换成py⽂件了
或者cmd运⾏
pyuic5 -o ui.py untitled.ui
3.5 使⽤转换后的程序
导⼊库
import sys
最后⾯写⼊程序⼊⼝:
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_Form() # ui_from是类名
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
转换后的代码
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'untitled.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.pushButton = QtWidgets.QPushButton(Form)
self.pushButton.setGeometry(QtCore.QRect(110, 130, 75, 23))
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(Form)
self.pushButton_2.setGeometry(QtCore.QRect(210, 210, 75, 23))
self.pushButton_2.setObjectName("pushButton_2")
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton.setText(_translate("Form", "PushButton"))
self.pushButton_2.setText(_translate("Form", "PushButton"))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_Form()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
运⾏:
⼆、Qt Designer使⽤教程
2.1 启动Qt Designer
初次启动会弹出这个“New Form”窗⼝,⼀般来说选择“Main Window”然后点击“Create”就可以了。
下⽅有个“Show this Dialogue on Startup”的checkbox,如果不想每次启动都看到这个“New Form”窗⼝,可以取消勾选。
创建“Main Window”之后,我们会看到如下画⾯
下⾯就来简单介绍下整个画⾯的构成:
左侧的“Widget Box”就是各种可以⾃由拖动的组件
中间的“MainWindow - untitled”窗体就是画布
右上⽅的"Object Inspector"可以查看当前ui的结构
右侧中部的"Property Editor"可以设置当前选中组件的属性
右下⽅的"Resource Browser"可以添加各种素材,⽐如图⽚,背景等等,⽬前可以不管
2.2 控件使⽤
通常来说,编写GUI有两种⽅法:第⼀种就是直接使⽤⽅便快捷的Qt Designer,第⼆种就是写代码。
在有Qt Designer的情况下,是完全不推荐费时费⼒去⼿写GUI代码的。
Qt Designer可以所见即所得,并且可以⽅便的修改并做出各种调整
添加⽂本
在左侧的“Widget Box”栏⽬中找到“Display Widgets”分类,将“Label”拖拽到屏幕中间的“MainWindow”画布上,你就获得了⼀个仅⽤于显⽰⽂字的⽂本框,如下图所⽰。
编辑⽂本
双击上图中的“TextLabel”,就可以对⽂本进⾏编辑,这⾥我们将其改成“HelloWorld!”,如下图所⽰。
如果⽂字没有完全展⽰出来,可以⾃⾏拖拽空间改变尺⼨。
特别提醒,编辑完⽂本之后记得敲击回车令其⽣效!
添加按钮
使⽤同样的⽅法添加⼀个按钮(PushButton)并将其显⽰的⽂本改成“HelloWorld!”,如下图所⽰。
修改窗⼝标题
下⾯修改窗⼝标题。
选中右上⽅的"Object Inspector"中的“MainWindow”,然后在右侧中部的"Property Editor"中找到“windowTitle”这个属性,在Value这⼀栏进⾏修改,修改完记得敲击回车
编辑菜单栏
注意到画布的左上⽅有个“Type Here”,双击它即可开始编辑菜单栏。
菜单栏⽀持创建多级菜单以及分割线(separator)。
我随意创建了⼀些菜单项⽬,如下图所⽰。
预览
使⽤快捷键Ctrl+R预览当前编写的GUI(或者从菜单栏的Form > Preview / Preview in进⼊)
保存
如果觉得完成了,那就可以保存成*.ui的⽂件,这⾥我们保存为HelloWorld.ui。
为了⽅便演⽰,我将⽂件保存到D盘
⽣成Python代码
使⽤cmd将⽬录切到D盘并执⾏下⾯的命令。
请⾃⾏将下⾯命令中的name替换成⽂件名,⽐如本例中的“HelloWorld.ui”
pyuic5 -o name.py name.ui
运⾏Python代码
此时尝试运⾏刚刚⽣成的“HelloWorld.py”是没⽤的,因为⽣成的⽂件并没有程序⼊⼝。
因此我们在同⼀个⽬录下另外创建⼀个程序叫
做“main.py”,并输⼊如下内容。
在本例中,gui_file_name就是HelloWorld,请⾃⾏替换。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import gui_file_name
if__name__ == '__main__':
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = gui_file_name.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())123456789101112
然后运⾏“main.py”,你就能看到刚刚编写的GUI了!
组件⾃适应
如果你刚刚尝试去缩放窗⼝,会发现组件并不会⾃适应缩放,因此我们需要回到Qt Designer中进⾏⼀些额外的设置。
点击画布空⽩处,然后在上⽅⼯具栏找到grid layout或者form layout,在本例中我们使⽤grid layout。
两种layout的图标如下图所⽰。
顺带⼀提,上图中layout的左边有三条横线以及三条竖线的图标,这两个是⽤于对齐组件,⾮常实⽤。
设置grid layout后,我们使⽤
Ctrl+R预览,这次组件可以⾃适应了!因为我们已经将UI(HelloWorld.py/HelloWorld.ui)跟逻辑(main.py)分离,因此直接重复步骤7-8即可完成UI的更新,⽆需改动逻辑(main.py)部分。
2.3 控件函数
刚刚写的HelloWorld中,我们设置的按钮(PushButton)是没有实际作⽤的,因为我们并没有告诉这个按钮应该做什么。
实际上,要让这个按钮做点什么只需要增加⼀⾏代码就可以了。
2.3.1 获取按钮id
打开HelloWorld.ui,在designer中选中对应的按钮,从“Property Editor”中可以得知这个按钮的“objectName”叫做“pushButton”,如下图所⽰。
2.3.2 设置触发
Qt中有“信号和槽(signal and slot)”这个概念,不过⽬前⽆需深究,也⽆需在Designer中去设置对应按钮的“信号和槽”,直接
在“main.py”中“MainWindow.show()”的后⾯加⼊下⾯这样的⼀⾏代码
ui.pushButton.clicked.connect(click_success)
下⾯简单解释下这⾏代码
pushButton就是刚刚获取的按钮id
clicked就是信号,因为是点击,所以我们这⾥⽤clicked
click_success就是对应要调⽤的槽,注意这⾥函数并不写成click_success()
2.3.2 设置函数
既然刚刚设置了按钮的触发并绑定了⼀个函数click_success,我们就要在“main.py”中实现它。
⽰例如下
def click_success():
print("啊哈哈哈我终于成功了!")
运⾏!
UI跟逻辑分离的好处就在这⾥,我们这次不⽤去管“HelloWorld.py”了,直接运⾏修改完的“main.py”。
点击按钮,这次你会发现在控制台中有了我们预设的输出。
三、实例开发
以⼀个简单的城市天⽓预报为例,演⽰使⽤PyQt5开发⼀个GUI程序的基本流程。
3.1 获取天⽓数据
3.2 设计界⾯UI
打开Qt Designer,可参考下图设计Weather.ui:
我们主要⽤到的控件有Button, GroupBox, Label,ComboBox,TextEdit,同时定义了两个按钮queryBtn及clearBtn,分别⽤来查询及清空天⽓数据。
我们需要绑定槽函数,⽅法如下
1. 在Qt Designer右下⾓选择信号/槽编辑器,点击+号新增
2. 分别选择queryBtn及clearBtn,选择信号 clicked(), 接收者 Dialog 及槽 accept() (我没找到绑定⾃定义槽函数的⽅法...)
最后选择保存为 Weather.ui⽂件。
3.3 转换.ui⽂件为.py⽂件
在PyCharm中选中Weather.ui⽂件后,右键选择 External Tools - PyUIC,即可⽣成Weather.py,实际运⾏命令如下:
C:\Python38\python.exe -m PyQt5.uic.pyuic Weather.ui -o Weather.py
其中,我们需要把两个按钮绑定的槽函数:
self.queryBtn.clicked.connect(Dialog.accept)
self.clearBtn.clicked.connect(Dialog.accept)
修改为⾃定义函数:
self.queryBtn.clicked.connect(Dialog.queryWeather)
self.clearBtn.clicked.connect(Dialog.clearText)
最终Weather.py内容如下:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'Weather.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(400, 300)
self.groupBox = QtWidgets.QGroupBox(Dialog)
self.groupBox.setGeometry(QtCore.QRect(0, 0, 391, 241))
self.groupBox.setObjectName("groupBox")
self.textEdit = QtWidgets.QTextEdit(self.groupBox)
self.textEdit.setGeometry(QtCore.QRect(20, 50, 351, 181))
self.textEdit.setObjectName("textEdit")
boBox = QtWidgets.QComboBox(self.groupBox)
boBox.setGeometry(QtCore.QRect(100, 20, 91, 20))
boBox.setObjectName("comboBox")
boBox.addItem("")
boBox.addItem("")
boBox.addItem("")
bel = QtWidgets.QLabel(self.groupBox)
bel.setGeometry(QtCore.QRect(30, 20, 61, 21))
bel.setObjectName("label")
self.queryBtn = QtWidgets.QPushButton(Dialog)
self.queryBtn.setGeometry(QtCore.QRect(40, 250, 75, 23))
self.queryBtn.setMaximumSize(QtCore.QSize(75, 16777215))
self.queryBtn.setObjectName("queryBtn")
self.clearBtn = QtWidgets.QPushButton(Dialog)
self.clearBtn.setGeometry(QtCore.QRect(250, 250, 75, 23))
self.clearBtn.setMaximumSize(QtCore.QSize(75, 16777215))
self.clearBtn.setObjectName("clearBtn")
self.retranslateUi(Dialog)
self.queryBtn.clicked.connect(Dialog.queryWeather)
self.clearBtn.clicked.connect(Dialog.clearText)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.groupBox.setTitle(_translate("Dialog", "城市天⽓预报"))
boBox.setItemText(0, _translate("Dialog", "北京"))
boBox.setItemText(1, _translate("Dialog", "上海"))
boBox.setItemText(2, _translate("Dialog", "天津"))
bel.setText(_translate("Dialog", "城市"))
self.queryBtn.setText(_translate("Dialog", "查询"))
self.clearBtn.setText(_translate("Dialog", "清空"))
3.4 调⽤MainDialog
在MainDialog中调⽤界⾯类Ui_Dialog,然后在其中中添加查询天⽓的业务逻辑代码,这样就做到了界⾯显⽰和业务逻辑的分离。
新增demo.py⽂件,在MainDialog类中定义了两个槽函数queryWeather()和clearText(),以便在界⾯⽂件Weather.ui中定义的两个按钮(queryBtn 和clearBtn) 触发clicked 信号与这两个槽函数进⾏绑定。
# coding:utf-8
import sys
import Weather
from PyQt5.QtWidgets import QApplication, QDialog
import requests
class MainDialog(QDialog):
def__init__(self, parent=None):
super(QDialog, self).__init__(parent)
self.ui = Weather.Ui_Dialog()
self.ui.setupUi(self)
def queryWeather(self):
cityName = boBox.currentText()
cityCode = self.getCode(cityName)
r = requests.get("/api/weather/city/{}".format(cityCode))
print(r.json())
if r.json().get('status') == 200:
weatherMsg = '城市:{}\n⽇期:{}\n天⽓:{}\nPM 2.5:{} {}\n温度:{}\n湿度:{}\n风⼒:{}\n\n{}'.format(
r.json()['cityInfo']['city'],
r.json()['data']['forecast'][0]['ymd'],
r.json()['data']['forecast'][0]['type'],
int(r.json()['data']['pm25']),
r.json()['data']['quality'],
r.json()['data']['wendu'],
r.json()['data']['shidu'],
r.json()['data']['forecast'][0]['fl'],
r.json()['data']['forecast'][0]['notice'],
)
else:
weatherMsg = '天⽓查询失败,请稍后再试!'
self.ui.textEdit.setText(weatherMsg)
def getCode(self, cityName):
cityDict = {"北京": "101010100",
"上海": "101020100",
"天津": "101030100"}
return cityDict.get(cityName, '101010100')
def clearText(self):
self.ui.textEdit.clear()
if__name__ == '__main__':
myapp = QApplication(sys.argv)
myDlg = MainDialog()
myDlg.show()
sys.exit(myapp.exec_())
最终运⾏显⽰效果如下:
四、PyQt5中多线程模块QThread使⽤⽅法
多线程模块QThread解决PyQt界⾯程序唉执⾏耗时操作时,程序卡顿出现的⽆响应以及界⾯输出⽆法实时显⽰的问题。
⽤户使⽤⼯具过程中出现这些问题时会误以为程序出错,从⽽把程序关闭。
这样,导致⼯具的⽤户使⽤体验不好。
使⽤PyQt界⾯程序,点击运⾏按钮后,程序在显⽰框中每秒打印1个数字。
程序代码如下:
# -*- coding: utf-8 -*-
import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow
from QThread_Example_UI import Ui_Form
class MyMainForm(QMainWindow, Ui_Form):
def__init__(self, parent=None):
super(MyMainForm, self).__init__(parent)
self.setupUi(self)
self.runButton.clicked.connect(self.display)
for i in range(20):
time.sleep(1)
self.listWidget.addItem(str(i))
if__name__ == "__main__":
app = QApplication(sys.argv)
myWin = MyMainForm()
myWin.show()
sys.exit(app.exec_())
程序运⾏过程结果如下(点击Run按钮后界⾯出现未响应字样,同时程序也没有出现每隔1秒打印1个数字,实际结果是循环结束后20个数字⼀同展⽰)
问题分析
上述实现的GUI程序都是单线程运⾏,对于需要执⾏⼀个特别耗时的操作时就会出现该问题现象。
要解决这种问题可以考虑使⽤多线程模块QThread。
多线程模块QThread基本原理
QThread是Qt的线程类中最核⼼的底层类。
由于PyQt的的跨平台特性,QThread要隐藏所有与平台相关的代码要使⽤的QThread开始⼀个线程,可以创建它的⼀个⼦类,然后覆盖其它QThread.run()函数。
class Thread(QThread):
def__init__(self):
super(Thread,self).__init__()
def run(self):
pass
接下来创建⼀个新的线程
thread = Thread()
thread.start()
可以看出,PyQt的线程使⽤⾮常简单,建⽴⼀个⾃定义的类(如Thread),⾃我继承⾃QThread ,并实现其run()⽅法即可。
在使⽤线程时可以直接得到Thread实例,调⽤其start()函数即可启动线程,线程启动之后,会⾃动调⽤其实现的run()的函数,该⽅法就是线程的执⾏函数。
业务的线程任务就写在run()函数中,当run()退出之后线程就基本结束了,QThread有started和finished信号,可以为这两个信号指定槽函数,在线程启动和结束之时执⾏⼀段代码进⾏资源的初始化和释放操作,更灵活的使⽤⽅法是,在⾃定义的QThread实例中⾃定义信号,并将信号连接到指定的槽函数,当满⾜⼀定的业务条件时发射此信号。
QThread类中的常⽤⽅法
start():启动线程
wait():阻⽌线程,直到满⾜如下条件之⼀
(1)与此QThread对象关联的线程已完成执⾏(即从run返回时),如果线程完成执⾏,此函数返回True,如果线程尚未启动,也返回True
(2)等待时间的单位是毫秒,如果时间是ULONG_MAX(默认值·),则等待,永远不会超时(线程必须从run返回),如果等待超时,此函数将会返回False
sleep():强制当前线程睡眠多少秒
QThread类中的常⽤信号
started:在开始执⾏run函数之前,从相关线程发射此信号
finished:当程序完成业务逻辑时,从相关线程发射此信号
使⽤QThread重新实现程序解决问题
先继承QThread类并重新实现其中的run()函数,也就是说把耗时的操作放⼊run()函数中。
代码如下:
# -*- coding: utf-8 -*-
import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow
from QThread_Example_UI import Ui_Form
class MyMainForm(QMainWindow, Ui_Form):
def__init__(self, parent=None):
super(MyMainForm, self).__init__(parent)
# 实例化线程对象
self.work = WorkThread()
self.runButton.clicked.connect(self.execute)
def execute(self):
# 启动线程
self.work.start()
# 线程⾃定义信号连接的槽函数
self.work.trigger.connect(self.display)
def display(self,str):
# 由于⾃定义信号时⾃动传递⼀个字符串参数,所以在这个槽函数中要接受⼀个参数 self.listWidget.addItem(str)
class WorkThread(QThread):
# ⾃定义信号对象。
参数str就代表这个信号可以传⼀个字符串
trigger = pyqtSignal(str)
def__int__(self):
# 初始化函数
super(WorkThread, self).__init__()
def run(self):
#重写线程执⾏的run函数
#触发⾃定义信号
for i in range(20):
time.sleep(1)
# 通过⾃定义信号把待显⽰的字符串传递给槽函数
self.trigger.emit(str(i))
if__name__ == "__main__":
app = QApplication(sys.argv)
myWin = MyMainForm()
myWin.show()
sys.exit(app.exec_())。