使用multiprocessing解决PyMuPDF不支持多线程加载导致的界面卡死无响应问题,及一个PyQt5实现的简易PDF阅读器例子
最近在用PyMuPDF实现一个PDF阅读器,发现PyMuPDF在加载某些epub时耗时非常长,有的长达10几秒,会导致界面卡死无响应。
尝试用多线程后台加载,发现还是不能解决问题,和作者交流(issue链接fitz.open blocks main thread even though I use it in a thread)后,作者说该库不支持真正的多线程,在多线程模式下也会阻塞主线程。
最后用multiprocessing解决该问题,我另外写了一个简单的PyQt5实现的PDF阅读器来说明如何解决该问题,效果图及代码如下。

#!python3
# -*- coding: utf-8 -*-
# this script demostrates how to use PyMuPDF in multiprocessing to avoid unresponsive GUI when fitz.open costs a long time
# author: yinkaisheng@live.com
import os
import sys
import time
import multiprocessing as mp
import queue
import fitz
from PyQt5 import QtCore, QtGui, QtWidgets class DocForm(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.process = None
self.queNum = mp.Queue()
self.queDoc = mp.Queue()
self.pageCount = 0
self.curPageNum = 0
self.lastDir = ''
self.timerSend = QtCore.QTimer(self)
self.timerSend.timeout.connect(self.onTimerSendPageNum)
self.timerGet = QtCore.QTimer(self)
self.timerGet.timeout.connect(self.onTimerGetPage)
self.timerWaiting = QtCore.QTimer(self)
self.timerWaiting.timeout.connect(self.onTimerWaiting)
self.initUI() def initUI(self):
vbox = QtWidgets.QVBoxLayout()
self.setLayout(vbox) hbox = QtWidgets.QHBoxLayout()
self.btnOpen = QtWidgets.QPushButton('OpenDocument', self)
self.btnOpen.clicked.connect(self.openDoc)
hbox.addWidget(self.btnOpen) self.btnPlay = QtWidgets.QPushButton('PlayDocument', self)
self.btnPlay.clicked.connect(self.playDoc)
hbox.addWidget(self.btnPlay) self.btnStop = QtWidgets.QPushButton('Stop', self)
self.btnStop.clicked.connect(self.stopPlay)
hbox.addWidget(self.btnStop) self.label = QtWidgets.QLabel('0/0', self)
self.label.setFont(QtGui.QFont('Verdana', 20))
hbox.addWidget(self.label) vbox.addLayout(hbox) self.labelImg = QtWidgets.QLabel('Document', self)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
self.labelImg.setSizePolicy(sizePolicy)
vbox.addWidget(self.labelImg) self.setGeometry(100, 100, 500, 600)
self.setWindowTitle('PyMuPDF Document Player')
self.show() def openDoc(self):
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open Document", self.lastDir,
"All Supported Files (*.pdf;*.epub;*.xps;*.oxps;*.cbz;*.fb2);;PDF Files (*.pdf);;EPUB Files (*.epub);;XPS Files (*.xps);;OpenXPS Files (*.oxps);;CBZ Files (*.cbz);;FB2 Files (*.fb2)", options=QtWidgets.QFileDialog.Options())
if path:
self.lastDir, self.file = os.path.split(path)
if self.process:
self.queNum.put(-1) # use -1 to notify the process to exit
self.timerSend.stop()
self.curPageNum = 0
self.pageCount = 0
self.process = mp.Process(target=openDocInProcess, args=(path, self.queNum, self.queDoc))
self.process.start()
self.timerGet.start(40)
self.label.setText('0/0')
self.queNum.put(0)
self.startTime = time.perf_counter()
self.timerWaiting.start(40) def playDoc(self):
self.timerSend.start(500) def stopPlay(self):
self.timerSend.stop() def onTimerSendPageNum(self):
if self.curPageNum < self.pageCount - 1:
self.queNum.put(self.curPageNum + 1)
else:
self.timerSend.stop() def onTimerGetPage(self):
try:
ret = self.queDoc.get(False)
if isinstance(ret, int):
self.timerWaiting.stop()
self.pageCount = ret
self.label.setText('{}/{}'.format(self.curPageNum + 1, self.pageCount))
else:#tuple, pixmap info
num, samples, width, height, stride, alpha = ret
self.curPageNum = num
self.label.setText('{}/{}'.format(self.curPageNum + 1, self.pageCount))
fmt = QtGui.QImage.Format_RGBA8888 if alpha else QtGui.QImage.Format_RGB888
qimg = QtGui.QImage(samples, width, height, stride, fmt)
self.labelImg.setPixmap(QtGui.QPixmap.fromImage(qimg))
except queue.Empty as ex:
pass def onTimerWaiting(self):
self.labelImg.setText('Loading "{}", {:.2f}s'.format(self.file, time.perf_counter() - self.startTime)) def closeEvent(self, event):
self.queNum.put(-1)
event.accept() def openDocInProcess(path, queNum, quePageInfo):
doc = fitz.open(path)
quePageInfo.put(doc.pageCount)
while True:
num = queNum.get()
if num < 0:
break
page = doc.loadPage(num)
pix = page.getPixmap()
quePageInfo.put((num, pix.samples, pix.width, pix.height, pix.stride, pix.alpha))
doc.close()
print('process exit') if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
form = DocForm()
sys.exit(app.exec_())
使用multiprocessing解决PyMuPDF不支持多线程加载导致的界面卡死无响应问题,及一个PyQt5实现的简易PDF阅读器例子的更多相关文章
- 解决ArcGIS API for Silverlight 加载地图的内外网访问问题
原文:解决ArcGIS API for Silverlight 加载地图的内外网访问问题 先上一个类,如下: public class BaseClass { public static string ...
- vs中 VMDebugger未能加载导致异常
,纠结了许久的一个问题,终于找到了解决 vs中 VMDebugger未能加载导致异常 错误号:80004005 搜了好多,没有一个给出完美的答案. 解决办法:工具->导入和导出设置,重置一下 ...
- 解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题
下面两种现象,用同一种方法解决 1.解决MWPhotoBrowser中的SDWebImage加载大图导致的内存警告问题 2.突然有一天首页访问图片很慢,至少隔20多秒所有图片才会出来.(解析:app使 ...
- 解决hibernate中的懒加载(延迟加载)问题
解决hibernate中的懒加载(延迟加载)问题 我们在开发的时候经常会遇到延迟加载问题,在实体映射时,多对一和多对多中,多的一样的属性默认是lazy="true"(即,默认是 ...
- Android 加载gif图片强大框架(支持预加载、缓存,还支持显示静态图片,一行代码全搞定)
之前项目中没有涉及到显示gif图片的功能,也没有着重研究过,最近项目中要用到显示gif图片,于是就在网上一顿搜,用过之后发现如下几个缺点. 1.加载大的gif图片会出现oom. 2.没有预加载和缓存功 ...
- 解决tableView中cell动态加载控件的重用问题
解决tableView中cell动态加载控件的重用问题 tableView的cell,有时候需要在运行时取得对应的数据后才能够动态的创建该cell中的控件并加载到该cell中,此时,你一定会遇到重用问 ...
- 支持无限加载的js图片画廊插件
natural-gallery-js是一款支持无限加载的js图片画廊插件.该js图片画廊支持图片的懒加载,可以对图片进行搜索,分类,还可以以轮播图的方式来展示和切换图片. 使用方法 在页面中引入下面的 ...
- 新浪微博客户端(13)-使用UIWebView加载OAuth授权界面
使用UIWebView加载OAuth授权界面 DJOAuthViewController.m #import "DJOAuthViewController.h" @interfac ...
- cocos2d-x游戏开发(十五)游戏加载动画loading界面
个人原创,欢迎转载:http://blog.csdn.net/dawn_moon/article/details/11478885 这个资源加载的loading界面demo是在玩客网做逆转三国的时候随 ...
随机推荐
- 用List表示多重性
练习目标-在类中使用List作为模拟集合操作: 在本练习中,将用List实现银行与客户间的多重关系. 任务 对银行来说,可添加Bank类. Bank 对象跟踪自身与其客户间的关系.用Customer对 ...
- DataGridView 单击赋值
void dataGridView1_Click(object sender, EventArgs e) { M_int_judge = ; btnSave.Enabled = true; btnSa ...
- Swiper 3D flow轮播使用方法
swiper 的3d轮播效果,移动端适用 (1). 如需使用Swiper的3d切换首先加载3D flow插件(js和css). <head> <link rel="styl ...
- 构造函数+原型的js混合模式
function Parent(){ this.name = "李小龙"; this.age = "30"; };Parent.prototype.lev=fu ...
- Apex语言(八)类和对象
1.类和对象 一切皆对象,是客观存在的万物,有标识.属性和行为.一个人,一台电脑,一辆轿车都是对象 类是创建对象的模板或蓝图,是对象的抽象,是对象的类型. 一个对象是一个类的一个实例,是一个类的变量. ...
- S-HR界面控件赋值取值
属性值: this.getField("entrys.variationReason").shrPromptBox("getValue").name
- [转载]查看Linux系统硬件信息实例详解
linux查看系统的硬件信息,并不像windows那么直观,这里我罗列了查看系统信息的实用命令,并做了分类,实例解说. cpu lscpu命令,查看的是cpu的统计信息. blue@blue-pc:~ ...
- codeforces 244B-Undoubtedly Lucky Numbers 搜索
题意:给你一个n,求不大于n的并且仅由两种或者一种数字组成的数的个数.(有点绕,,简单点就是,看看小于等于n点数中,,有多少数字只有一种数字,或者有两种数字组成) “哎,自己还是太菜了,训练的时候只做 ...
- 面试官问你如何解决web高并发这样回答就好了
所谓高并发,就是同一时间有很多流量(通常指用户)访问程序的接口.页面及其他资源,解决高并发就是当流量峰值到来时保证程序的稳定性. 我们一般用QPS(每秒查询数,又叫每秒请求数)来衡量程序的综合性能,数 ...
- Problem 16
Problem 16 pow(2, 15) = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.2的15次方等于32768,而这些数 ...