文章目录- 1. 多线程
- 1.1 QTimer
- 1.2 QThread
- 界面卡住例子
- 分离UI和工作线程
- 1.3 事件处理
- 2. 网页交互
- 显示本地 html
- 显示 html 代码
- 调用 JavaScript
- JavaScript 调用 PyQt代码
- 1.1 QTimer
- 1.2 QThread
- 界面卡住例子
- 分离UI和工作线程
- 1.3 事件处理
- 显示本地 html
- 显示 html 代码
- 调用 JavaScript
- JavaScript 调用 PyQt代码
learn from 《PyQt5 快速开发与实战》 https://doc.qt.io/qtforpython/index.html https://www.riverbankcomputing.com/static/Docs/PyQt5
1. 多线程
1.1 QTimer
- 周期性的发出
timeout
信号
# _*_ coding: utf-8 _*_
# @Time : 2022/5/29 23:42
# @Author : Michael
# @File : qtimer_demo.py
# @desc :
from PyQt5.QtCore import QTimer, QDateTime
from PyQt5.QtWidgets import QWidget, QListWidget, QLabel, QPushButton, QGridLayout, QApplication
class QtimerDemo(QWidget):
def __init__(self):
super(QtimerDemo, self).__init__()
self.setWindowTitle("QTimer Demo")
self.listFile = QListWidget()
self.label = QLabel('显示当前时间')
self.startBtn = QPushButton('开始')
self.stopBtn = QPushButton('停止')
layout = QGridLayout()
self.timer = QTimer()
self.timer.timeout.connect(self.showTime)
layout.addWidget(self.label, 0, 0, 1, 2)
layout.addWidget(self.startBtn, 1, 0, 1, 2)
layout.addWidget(self.stopBtn, 2, 0, 1, 2)
self.startBtn.clicked.connect(self.startTimer)
self.stopBtn.clicked.connect(self.stopTimer)
self.setLayout(layout)
def startTimer(self):
self.timer.start(1000) # 每隔1秒触发一次
self.startBtn.setEnabled(False)
self.stopBtn.setEnabled(True)
def stopTimer(self):
self.timer.stop()
self.startBtn.setEnabled(True)
self.stopBtn.setEnabled(False)
def showTime(self):
time = QDateTime().currentDateTime()
timedisplay = time.toString('yyyy-MM-dd hh:mm:ss')
self.label.setText(timedisplay)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = QtimerDemo()
win.show()
sys.exit(app.exec_())
一次性定时器
代码语言:javascript复制# _*_ coding: utf-8 _*_
# @Time : 2022/5/29 23:56
# @Author : Michael
# @File : qtimer_demo2.py
# @desc :
import sys
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QApplication, QLabel
if __name__ == '__main__':
app = QApplication(sys.argv)
label = QLabel('<font color=red size=40>Hello World, 3秒后会消失</font>')
label.setWindowFlags(Qt.SplashScreen | Qt.FramelessWindowHint) # 无边框窗口
label.show()
QTimer.singleShot(3000, app.quit) # 一次性定时器,可模仿程序启动画面
sys.exit(app.exec_())
1.2 QThread
创建QThread
的子类,覆写 QThread.run()
,调用 线程的start()
函数后,会自动调用 run()
# _*_ coding: utf-8 _*_
# @Time : 2022/5/30 0:14
# @Author : Michael
# @File : qthread1.py
# @desc :
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QListWidget, QPushButton, QGridLayout, QApplication
class MainWidget(QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
self.setWindowTitle("QThread 例子")
self.thread = Worker()
self.listFile = QListWidget()
self.btnStart = QPushButton('开始')
layout = QGridLayout(self)
layout.addWidget(self.listFile, 0, 0, 1, 2)
layout.addWidget(self.btnStart, 1, 1)
self.btnStart.clicked.connect(self.slotStart)
self.thread.sinOut.connect(self.slotAdd)
def slotAdd(self, file_inf):
self.listFile.addItem(file_inf)
def slotStart(self):
self.btnStart.setEnabled(False)
self.thread.start()
class Worker(QThread):
sinOut = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.working = True
self.num = 0
def __del__(self):
self.working = False
self.wait()
def run(self):
while self.working:
file_str = 'File index {0}'.format(self.num)
self.num = 1
# 发出信号
self.sinOut.emit(file_str)
# 线程休眠2秒
self.sleep(2)
if __name__ == "__main__":
app = QApplication(sys.argv)
demo = MainWidget()
demo.show()
sys.exit(app.exec_())
界面卡住例子
代码语言:javascript复制# _*_ coding: utf-8 _*_
# @Time : 2022/5/30 0:25
# @Author : Michael
# @File : thread_stuck.py
# @desc :
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLCDNumber, QPushButton
global sec
sec = 0
def setTime():
global sec
sec = 1
# LED显示数字 1
lcdNumber.display(sec)
def work():
# 计时器每秒计数
timer.start(1000)
for i in range(2000000000):
pass
timer.stop()
if __name__ == "__main__":
app = QApplication(sys.argv)
win = QWidget()
win.resize(300, 120)
# 垂直布局类QVBoxLayout
layout = QVBoxLayout(win)
# 加个显示屏
lcdNumber = QLCDNumber()
layout.addWidget(lcdNumber)
button = QPushButton("测试")
layout.addWidget(button)
timer = QTimer()
# 每次计时结束,触发setTime
timer.timeout.connect(setTime)
button.clicked.connect(work)
win.show()
sys.exit(app.exec_())
模拟下载,并计时
可以看到程序卡住了,计时器也没有走起来
PyQt 中所有的窗口都是在 UI 主线程中,这个线程中执行耗时的操作会阻塞 UI 线程,耗时的操作需要 开启新的线程 去执行
分离UI和工作线程
代码语言:javascript复制# _*_ coding: utf-8 _*_
# @Time : 2022/5/30 0:37
# @Author : Michael
# @File : threadsplit_ui_work.py
# @desc :
import sys
from PyQt5.QtCore import QTimer, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLCDNumber, QPushButton
global sec
sec = 0
class WorkThread(QThread):
trigger = pyqtSignal()
def __int__(self):
super(WorkThread, self).__init__()
def run(self):
for i in range(2000000000):
pass
# 循环完毕后发出信号
self.trigger.emit()
def countTime():
global sec
sec = 1
# LED显示数字 1
lcdNumber.display(sec)
def work():
# 计时器每秒计数
timer.start(1000)
# 计时开始
workThread.start()
# 当获得循环完毕的信号时,停止计数
workThread.trigger.connect(timeStop)
def timeStop():
timer.stop()
print("运行结束用时", lcdNumber.value())
global sec
sec = 0
if __name__ == "__main__":
app = QApplication(sys.argv)
win = QWidget()
win.resize(300, 120)
# 垂直布局类QVBoxLayout
layout = QVBoxLayout(win)
# 加个显示屏
lcdNumber = QLCDNumber()
layout.addWidget(lcdNumber)
button = QPushButton("测试")
layout.addWidget(button)
timer = QTimer()
workThread = WorkThread()
button.clicked.connect(work)
# 每次计时结束,触发 countTime
timer.timeout.connect(countTime)
win.show()
sys.exit(app.exec_())
1.3 事件处理
- 可以使用
QApplication.processEvents()
刷新页面,给人感觉不卡顿
上面卡住的例子中添加一句就可以不卡了
代码语言:javascript复制def work():
# 计时器每秒计数
timer.start(1000)
for i in range(2000000000):
QApplication.processEvents() # 添加这句刷新页面
pass
timer.stop()
2. 网页交互
pyqt5 使用 QWebEngineView
控件来展示 HTML ,其使用的 Chromium 内核
# _*_ coding: utf-8 _*_
# @Time : 2022/5/30 0:53
# @Author : Michael
# @File : web_load.py
# @desc :
from PyQt5.QtCore import QUrl
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QMainWindow, QApplication
class MainWin(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("load url")
self.setGeometry(300, 300, 1000, 600)
self.browser = QWebEngineView()
self.browser.load(QUrl("https://michael.blog.csdn.net/"))
self.setCentralWidget(self.browser)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = MainWin()
win.show()
sys.exit(app.exec_())
显示本地 html
代码语言:javascript复制url = QUrl("D:/gitcode/Python_learning/qt/ch5/index.html")
self.browser.load(url)
显示 html 代码
代码语言:javascript复制html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Hello michael</h1>
<h1>Hello PyQt5 from setHtml</h1>
</body>
</html>
"""
self.browser.setHtml(html)
调用 JavaScript
代码语言:javascript复制# _*_ coding: utf-8 _*_
# @Time : 2022/5/31 23:44
# @Author : Michael
# @File : webjs01.py
# @desc :
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton
from PyQt5.QtWebEngineWidgets import QWebEngineView
import sys
# 创建一个 application实例
app = QApplication(sys.argv)
win = QWidget()
win.setWindowTitle('Web页面中的JavaScript与 QWebEngineView交互例子')
# 创建一个垂直布局器
layout = QVBoxLayout()
win.setLayout(layout)
# 创建一个 QWebEngineView 对象
view = QWebEngineView()
view.setHtml('''
<html>
<head>
<title>A Demo Page</title>
<script language="javascript">
// Completes the full-name control and
// shows the submit button
function completeAndReturnName() {
var fname = document.getElementById('fname').value;
var lname = document.getElementById('lname').value;
var full = fname ' ' lname;
document.getElementById('fullname').value = full;
document.getElementById('submit-btn').style.display = 'block';
return full;
}
</script>
</head>
<body>
<form>
<label for="fname">First name:</label>
<input type="text" name="fname" id="fname"></input>
<br />
<label for="lname">Last name:</label>
<input type="text" name="lname" id="lname"></input>
<br />
<label for="fullname">Full name:</label>
<input disabled type="text" name="fullname" id="fullname"></input>
<br />
<input style="display: none;" type="submit" id="submit-btn"></input>
</form>
</body>
</html>
''')
# 创建一个按钮去调用 JavaScript代码
button = QPushButton('设置全名')
def js_callback(result):
print(result)
def complete_name():
view.page().runJavaScript('completeAndReturnName();', js_callback)
# QWebEngineView 对象的 page()方法返回一个 QWebEnginePage 对象
# QWebEnginePage 对象的 异步 runJavaScript()方法可以执行 JavaScript代码
# 需要回调函数来处理结果
# 按钮连接 'complete_name'槽,当点击按钮是会触发信号
button.clicked.connect(complete_name)
# 把QWebView和button加载到layout布局中
layout.addWidget(view)
layout.addWidget(button)
# 显示窗口和运行app
win.show()
sys.exit(app.exec_())
JavaScript 调用 PyQt代码
- PyQt 可以与加载的 Web 页面进行双向的数据交互
from PyQt5.QtCore import pyqtProperty
class MySharedObject(QWidget):
def __init__(self):
super(MySharedObject, self).__init__()
def _getStrValue(self):
#
return '100'
def _setStrValue(self, str):
#
print('获得页面参数 :%s' % str)
QMessageBox.information(self, "Information", '获得页面参数 :%s' % str)
# 需要定义对外暴露的方法
strValue = pyqtProperty(str, fget=_getStrValue, fset=_setStrValue)
- 首先,使用
QWebEngineView
对象加载 Web页面后,就可以获得页面中表单输入数据,在 Web 页面中通过 JavaScript 代码收集用户提交的数据
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebChannel import QWebChannel
channel = QWebChannel()
myObj = MySharedObject()
channel.registerObject("bridge", myObj)
view.page().setWebChannel(channel)
- 然后,在 Web 页面中,JavaScript 通过
桥连接
方式传递数据给PyQt - 最后,PyQt 接收到页面传递的数据,经过业务处理后,还可以把处理过的数据返给Web页面
html 需要引入 <script src="qwebchannel.js"></script>
<html>
<head>
<title>A Demo Page</title>
<meta charset="UTF-8">
<script src="qwebchannel.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
new QWebChannel(qt.webChannelTransport, function (channel) {
window.bridge = channel.objects.bridge;
alert('bridge=' bridge 'n从pyqt传来的参数=' window.bridge.strValue);
});
});
function onShowMsgBox() {
if (window.bridge) {
var fname = document.getElementById('fname').value;
window.bridge.strValue = fname;
}
}
</script>
</head>
<body>
<form>
<label for="姓名">user name:</label>
<input type="text" name="fname" id="fname"></input>
<br/>
<input type="button" value="传递参数到pyqt" onclick="onShowMsgBox()">
<input type="reset" value='重置'/>
</form>
</body>
</html>