前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PyQt5 高级界面控制(多线程、网页交互、调用JavaScript)

PyQt5 高级界面控制(多线程、网页交互、调用JavaScript)

作者头像
Michael阿明
发布2022-06-05 12:03:18
2K0
发布2022-06-05 12:03:18
举报

文章目录

learn from 《PyQt5 快速开发与实战》 https://doc.qt.io/qtforpython/index.html https://www.riverbankcomputing.com/static/Docs/PyQt5

1. 多线程

1.1 QTimer

  • 周期性的发出timeout信号
代码语言:javascript
复制
# _*_ 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()

代码语言:javascript
复制
# _*_ 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 内核

代码语言:javascript
复制
# _*_ 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 页面进行双向的数据交互
代码语言:javascript
复制
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 代码收集用户提交的数据
代码语言: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>

代码语言:javascript
复制
<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>
在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1. 多线程
    • 1.1 QTimer
      • 1.2 QThread
        • 界面卡住例子
        • 分离UI和工作线程
      • 1.3 事件处理
        • 显示本地 html
        • 显示 html 代码
        • 调用 JavaScript
        • JavaScript 调用 PyQt代码
    • 2. 网页交互
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档