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版本过高,降低版本即可

PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)_第1张图片

3.2.1,PyQt调用js (runJavaSCript)

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

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

前端:



    
    Title
    


name:

password:

name+password:

后端:
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_())

效果:

PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)_第2张图片

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实现页面打印

前端的代码:

前端



    
      
     

    QWebChannel测试
    


后端
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_())

输出:

PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)_第3张图片

成功得到pdf文件

PyQt学习(14):多线程(Qtimer和Qthread),网页交互(QWebEngineView,js与python的交互)_第4张图片

 

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

个人总结:仅当参考

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

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

你可能感兴趣的:(pyqt5)