使用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是在玩客网做逆转三国的时候随 ...
随机推荐
- crontab与系统时间不一致
将线上数据库迁移至虚拟机后,运维没有把时间修改. 在后期把时间修改完成后,在数据库上也要修改修改,但是定时任务的备份时间却不在凌晨4点执行,而是在中午12:10分执行. 原因是修改时间后,需要重启cr ...
- 分布式机器学习框架:MxNet 前言
原文连接:MxNet和Caffe之间有什么优缺点一.前言: Minerva: 高效灵活的并行深度学习引擎 不同于cxxnet追求极致速度和易用性,Minerva则提供了一个高效灵活的平台 ...
- Android使用NDK---函数参数传递-基本类型和数组
参考链接:http://www.cnblogs.com/luxiaofeng54/archive/2011/08/19/2145486.html 数据传输可分为 基本数据类型传输 和 引用数据类型的传 ...
- (转)Openlayers 2.X加载天地图
http://blog.csdn.net/gisshixisheng/article/details/44621923 概述: 在前面的章节,讲到了Arcgis for js加载天地图,在本节讲述如何 ...
- coredata示意图
NSPersistentStoreCoordinator(Persistent Store Coordinator),缩写为PSC:存储信息+结构信息(MOM) NSManagedObjectMode ...
- laravel中ubuntu下执行php artisan migrate总是报错
ubuntu14.0 + xampp + laravel5下 laravel中ubuntu下执行php artisan migrate总是报错: [PDOException] could not fi ...
- AtCoder Grand Contest 021完整题解
提示:如果公式挂了请多刷新几次,MathJex的公式渲染速度并不是那么理想. 总的来说,还是自己太弱了啊.只做了T1,还WA了两发.今天还有一场CodeForces,晚上0点qwq... 题解还是要好 ...
- [luogu4162 SCOI2009] 最长距离(最短路)
传送门 Solution 题目是最长路,其实是最短路ヽ(ー_ー)ノ 把进入障碍点的边设为1,其他为0.枚举每个点为起点找距离<=T的点,更新答案 Code //By Menteur_Hxy #i ...
- python爬虫09 | 上来,自己动 !这就是 selenium 的牛逼之处
作为一个男人 在最高光的时刻 就是说出那句 之后 还不会被人打 ... 虽然在现实生活中你无法这样 但是在这里 就让你体验一番 那种呼风唤雨的感觉 我们之前在爬取某些网站的时候 使用到了一些 pyth ...
- Linux命令及全称(部分)
man: manual 意思是手册,可以用这个命令查询其他命令的用法. pwd:print working directory 显示当前工作路径. su:swith user 切换用户,切换 ...