PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互) - Go语言中文社区

PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)


目录

1,QTimer

2,QThread

3,网页交互(QWebEngineView和QWebChannel)

3.1显示网页页面

3.2,JS与Pyqt交互(QwebEngineView 和QwebChannel)

3.2.1,PyQt调用js (runJavaSCript)

3.2.2 js调用python程序(QwebChannel)比较复杂。其实是实现了js与python程序调用

3.2.3 runjavaScript和 Qwebchannel传递信息的不同用处


 

多线程技术涉及三种方法,其中一个是使用计时器模块QTimer,一种是使用多线程模块QThread,还有一种是使用事件处理的功能

1,QTimer

如果在应用程序中周期性地进行某项操作,使用QTimer,Qtimer类提供了重复的和单次的定时器要是用定时器,需要先创建一个QTimer实例,将其timeout信号连接到相应的槽,并调用start()。然后,定时器以恒定的间隔发出timeout信号

Qtimer类中常用方法:

start(millseconds):启动或重新自动定时器,时间间隔为毫秒,如果定时已经运行,它将被停止并重新启动,如果sigleShot信号为真,定时器将被激活一次

stop()停止计时器

常用的信号:

signalShot:在给定的时间间隔调用一个槽函数时发射此信号

timeout:当定时器超时时发射此信号

注意:start()之后,每秒都会调用update()

from PyQt5.QtWidgets import QGridLayout,QApplication,QWidget,QPushButton,QLabel
import sys
from PyQt5.QtCore import QTimer,QDateTime

class TimerDemo(QWidget):
	def __init__(self):
		super(TimerDemo, self).__init__()
		self.initUI()
	def initUI(self):
		self.setWindowTitle('demo')
		self.setGeometry(300,300,300,200)
		layout=QGridLayout(self)
		#定义计时器,并连接槽函数
		self.timer=QTimer()
		self.timer.timeout.connect(self.show_time)

		self.label=QLabel()
		layout.addWidget(self.label,0,0,1,2)

		self.btn1=QPushButton('start',self)
		layout.addWidget(self.btn1,1,0)
		self.btn1.clicked.connect(self.start_program)

		self.btn2=QPushButton('end',self)
		layout.addWidget(self.btn2,1,1)
		self.btn2.clicked.connect(self.end_program)

	def show_time(self):
		date=QDateTime.currentDateTime()
		now=date.toString('yyyy-MM--dd hh:mm:ss')
		self.label.setText(now)

	def start_program(self):
		self.timer.start(1000)
		self.btn1.setEnabled(False)
		self.btn2.setEnabled(True)
	def end_program(self):
		self.timer.stop()
		self.btn2.setEnabled(False)
		self.btn1.setEnabled(True)

if __name__=='__main__':
	app=QApplication(sys.argv)
	demo=TimerDemo()
	demo.show()
	sys.exit(app.exec_())
from PyQt5.QtWidgets import QLabel,QApplication
import sys
from PyQt5.QtCore import QTimer,Qt


if __name__ == '__main__':
	app=QApplication(sys.argv)
	label=QLabel('五秒退出')
	label.setWindowFlags(Qt.SplashScreen|Qt.FramelessWindowHint)
	label.show()
	#设置窗口无边框
	
	timer=QTimer()
	timer.singleShot(5000,app.quit)#*********
	sys.exit(app.exec_())

2,QThread

QThread是Qt线程类中最核心的底层类,由于PyQt的跨平台特性,QThread隐藏所有与平台相关的代码

要使用QThread开始一个线程,可以创建它的一个子类,然后覆盖其QThread.run()函数

QThread类中常用的方法和信号:

start():启动线程

wait():阻止线程,知道满足如下条件之一,*与此QThread对象关联的线程已完成执行,即从run()返回时。如果线程

完成执行,此函数将返回True:如果线程尚未启动,此函数也返回True。*等待时间的单位是毫秒,如果事件是ULONG_MAX(默认值)。则等待,永远不会超时。如果等待超时,此函数将返回False

QThread类中常用的信号

started:在开始执行run()函数之前,从相关线程发射此信号

finished:当线程完成业务逻辑时,从相关线程发射此信号

流程:

class Thread(QThread):     #继承QThread
      def __init__(self):
            super(Thread,self).__init__()
      def run(self):
      #线程相关的代码
      passs
========================
#创建一个新的线程
thread=Thread()
thread.start()

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

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)
    top = QWidget()
    top.resize(300, 120)

    # 垂直布局类QVBoxLayout
    layout = QVBoxLayout(top)

    # 加显示屏,按钮到布局中
    lcdNumber = QLCDNumber()
    layout.addWidget(lcdNumber)
    button = QPushButton("测试")
    layout.addWidget(button)

    #实例化定时器与多线程类
    timer = QTimer()
    workThread = WorkThread()

    button.clicked.connect(work)
    # 每次计时结束,触发 countTime
    timer.timeout.connect(countTime)

    top.show()
    sys.exit(app.exec_())

上面的代码实现了界分离UI线程和工作线程

3,处理密集型耗时的事情(频繁调用QApplication.processEVents)

有时候需要处理一些跟界面无关的但非常耗时的事情,这些事情跟界面在同一个线程中,由于时间太长,导致无法响应,处于假死状态。这种情况下,有一种方法是使用多线程,即在子线程中处理文件保存,主线程负责界面相关

如果不使用多线程,最简单的办法就是在文件保存过程中频繁调用QApplication.processEVent()。该函数的作用是让程序处理那些还没处理的时间,然后再把使用权返回给调用者

from PyQt5.QtWidgets import QProgressDialog,QLineEdit,QLCDNumber,QGridLayout,QApplication,QWidget,QPushButton,QLabel
import sys
from PyQt5.QtCore import pyqtSignal,QTimer,QDateTime,Qt,QThread

class ProgressEventDemo(QWidget):
	def __init__(self):
		super(ProgressEventDemo, self).__init__()
		self.initUI()
	def initUI(self):
		self.setWindowTitle('demo')
		layout=QGridLayout(self)
		
		self.label=QLabel('文件数量')
		layout.addWidget(self.label,0,0)
		self.edit=QLineEdit('10000')
		layout.addWidget(self.edit,0,1)
		self.btn=QPushButton('开始')
		layout.addWidget(self.btn,1,1)
		self.btn.clicked.connect(self.progrress)

	def progrress(self):
		num=int(self.edit.text())
		dialog=QProgressDialog()
		dialog.setWindowTitle('please wait..')
		dialog.setLabelText('正在操作。。')
		dialog.setCancelButtonText('取消')
		dialog.setMinimumDuration(5)
		dialog.setWindowModality(Qt.WindowModal)
		dialog.setRange(0,num)

		for i in range(num):
			dialog.setValue(i)
			if dialog.wasCanceled():
				print('操作失败')
				break
			else:
				QApplication.processEvents()#可以实现一边执行耗时程序,一边刷新页面的功能
				for j in range(100):
					print(8000)

			print('-------------------')


		
		

		
		


if __name__=='__main__':
	app=QApplication(sys.argv)
	demo=ProgressEventDemo()
	demo.show()
	sys.exit(app.exec_())

3,网页交互(QWebEngineView和QWebChannel

参考:https://www.jianshu.com/p/ffd2252113f1

https://www.cnblogs.com/yaoyu126/p/7524625.htmlHtml 与本地代码交互实例

打印热敏小票:https://www.jianshu.com/p/8298c2474630

3.1显示网页页面

import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QHBoxLayout,QWidget,QMainWindow,QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
class UiDemo(QWidget):
	def __init__(self):
		super().__init__()
		self.initUi()
	def initUi(self):
		self.setWindowTitle('demo')
		self.setGeometry(300,300,400,300)
		layout=QHBoxLayout(self)

		browser=QWebEngineView()
		browser.load(QUrl("http://www.baidu.com"))
		layout.addWidget(browser)

if __name__ == '__main__':
	app=QApplication(sys.argv)
	demo=UiDemo()
	demo.show()
	sys.exit(app.exec_())

3.2,JS与Pyqt交互(QwebEngineView 和QwebChannel)

 

注意:是因为PyQt版本过高,降低版本即可

3.2.1,PyQt调用js (runJavaSCript)

可以这样理解:在pyqt的组件界面对显示的网页页面进行更改(举例,如点击PyQt界面的QpushButton 改变网页中的字体的颜色。而不是点击网页中的Button标签按钮),同时可以进行pyqt和网页间的信息传递。相当于pyqt组件控制网页的组件

流程:编写html和js---->pyqt加载html----->利用QwebEngineView.page().runJavaScript()方法加载js。因为是异步,加载js的同时有返回值

前端:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function Transport(value) {      //Pyqt中调用
            alert(value)
            var name=document.getElementById('name').value;
            var password=document.getElementById('password').value;

            var np=name+':'+password;
             document.getElementById('np').value=np;

             document.getElementById('submit').style.display='block';
            return np;    //会返回到PyQT中

        }
    </script>
</head>
<body>
<form>
    <p>name: <input type="text" id="name"></p>
    <p>password: <input type="text" id="password"></p>
    <p>name+password: <input type="text" id="np"></p>
    <p> <input style="display:none" type="submit" value="submit" id="submit"></p>
</form>
</body>
</html>
后端:
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QHBoxLayout,QPushButton
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl,QObject
from PyQt5.QtWebChannel import QWebChannel

class GuiDemo(QWidget):
    def __init__(self):
        super(GuiDemo, self).__init__()
        self.initUi()
    def initUi(self):
        self.setWindowTitle('demo')
        self.setGeometry(300,300,500,300)
        layout=QHBoxLayout(self)

        #显示web页面
        self.view=QWebEngineView()
        self.view.load(QUrl('E:/workspace/test/test/runjs/index.html'))
        layout.addWidget(self.view)

        #按钮,当点击按钮是调用html中的js函数
        btn=QPushButton('点击调用js')
        layout.addWidget(btn)
        btn.clicked.connect(self.use_Js)

    def js_callback(self,result):
        print(result)


    def use_Js(self):
        #向js中传值
        value='hehe'
        self.view.page().runJavaScript('Transport("'+value+'");',self.js_callback)  #第一个参数是调用html中js。第二个参数是js的返回值  。注意:第一个参数有传值的话,一定有双引号






if __name__=='__main__':
    app=QApplication(sys.argv)
    demo=GuiDemo()
    demo.show()
    sys.exit(app.exec_())

效果:

3.2.2 js调用python程序(QwebChannel)比较复杂。其实是实现了js与python程序调用

可以这样理解将网页js不能处理的操作交给python操作(比如打印网页,点击加载的网页按钮调用Pyqt中的Qprint进行打印)。即使没有pyqt界面也可以(利用在实现网页打印,可以直接利用js调用pyton中的方法。浏览器相当于一个Qt编写的界面,然后加载html)

原理:将python的一个对象映射到javascript

流程:编写html和js(要加载qwebchannel.js,以及将没定义的处理类隐射到js中)-------->pyqt加载html------->定义通道Qwebchannel -------->定义处理类(编写处理类作为槽函数pyqtSlot)-------------->将处理类注册到通道(相当于连接)---------->将页面与channel连接|view.setwebChannel(channel)

 

实现网页截图:需要html2canvas.js 参考官网https://html2canvas.hertzen.com/

js实现页面打印

前端的代码:

前端
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <script type="text/javascript" src="qwebchannel.js"></script>  <!--用来映射-->
    <script type="text/javascript" src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> <!--在线调用截图js-->

    <title>QWebChannel测试</title>
    <script>
        function callback(result) {
            alert(result)
        }
        window.onload = function () {
            new QWebChannel(qt.webChannelTransport, function (channel) {  //必须,将python程序映射到js
                window.pyjs = channel.objects.pyjs;

            });
        }
        function do_print() {
            if (pyjs!==null){
                //将网页保存为图片数据流
                html2canvas(document.querySelector("#capture")).then(canvas => {
                    var data_url=canvas.toDataURL();
                    pyjs.print_img(data_url,callback);  //调用pyqt中的函数进行,将图片传入,异步传回success
                });


            }
        }



    </script>
</head>
<body>
<div id="capture">
    <div class="print_container">
        <h3>便利店</h3>
        <span>***************************************</span>
        <div class="section3">
            <label>订单号:12121213234343</label>
            <label>下单时间:2019-1-1 16:31:14</label>
            <label>收银员:小明</label>
        </div>
        <span>***************************************</span>
        <div class="section4">
            <div style="border-bottom: 1px solid #DADADA;">
                <table style="width: 100%;">
                    <thead>
                    <tr>
                        <td width="60%">品名</td>
                        <td width="20%">数量</td>
                        <td width="20%">金额</td>
                    </tr>
                    </thead>
                    <tbody>
                    <tr>
                        <td>今麦郎</td>
                        <td>1</td>
                        <td>100.00</td>
                    </tr>
                    </tbody>
                </table>
            </div>
            <div class="total">
                <label class="left">合 计</label>
                <label class="right">100.00</label>
                <div class="clearfix"></div>
                <label class="left">收款金额</label>
                <label class="right">100</label>
                <div class="clearfix"></div>
                <label class="left">找零金额</label>
                <label class="right">0.00</label>
                <div class="clearfix"></div>
            </div>
            <div style="text-align: right;">
                <label>顾客已付款</label>
            </div>
            <span>***************************************</span>
        </div>
        <div class="section5">
            <label>电话:</label>
        </div>
        <span>***************************************</span>
        <div class="section5">
            <label>欢迎光临,谢谢惠顾!</label>
            <label>便利店</label>
        </div>
    </div>
</div>
<div>
    <button onclick="do_print()">进行图像打印</button>
</div>

</body>
</html>
后端
import sys,base64
from PyQt5.QtWidgets import QApplication,QWidget,QHBoxLayout,QLabel
from PyQt5.QtCore import QObject, pyqtSlot, QUrl
from PyQt5.QtGui import QImage,QTextDocument,QTextCursor
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView

class Test(QObject):
    def __init__(self):
        super().__init__()
    @pyqtSlot(str,result=str)
    def print_img(self,img_url):
        #去掉头部的base64标示
        img_url=img_url.replace('data:image/png;base64,', '')
        #将base64解码成二进制
        url=base64.b64decode(img_url)
        #QImage加载二进制,形成图片流
        image=QImage()
        image.loadFromData(url)


        '''直接输出打印到pdf'''
        printer=QPrinter()
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOutputFileName('test.pdf')
        #实例化一个富文本
        document=QTextDocument()
        cursor=QTextCursor(document)
        cursor.insertImage(image)
        #调用print()方法 参数为当前实例化的打印函数
        document.print(printer)

        return 'sucess'         #处理成功传回success



if __name__ == '__main__':
    app = QApplication(sys.argv)
    #加载网页
    view=QWebEngineView()
    view.load(QUrl('E:/workspace/test/test/test.html'))
    # '''流程:定义通道,连接通道'''
    # #定义通道
    channel = QWebChannel()   #必须定义成全局的,否则会出错,不能在定义界面的类里面里面定义会出错
    # #定义pyqt操作函数
    test = Test()  # 必须定义成全局的,否则会出错,不能在定义界面的类里面会出错
    # #通道与操作函数连接,也就是注册
    channel.registerObject('pyjs', test)
    # #网页连接通道
    view.page().setWebChannel(channel)

    view.show()
    sys.exit(app.exec_())

输出:

成功得到pdf文件

 

3.2.3 runjavaScript和 Qwebchannel传递信息的不同用处

个人总结:仅当参考

runjavaScript:主要用来传递PyQt与页面(也就是js)之间的数据

Qwebchannel:主要用来完成一些js无法完成的功能,相当有后端操作

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/chengmo123/article/details/96962026
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 20:58:38
  • 阅读 ( 1246 )
  • 分类:前端

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢