使用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是在玩客网做逆转三国的时候随 ...
随机推荐
- MYSQL 代码删除和添加表格列方法
一个表格建立后用代码删除或添加列: -- 删除列alter table teacher drop column create_time;-- 添加列alter table teacher add co ...
- response.getWriter().write()乱码问题
前台代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> & ...
- 【VB】时间戳转日期
DateAdd("s", TimeStamp / 1000, "1970-01-01 00:00:00")
- Deutsch lernen (01)
Was macht Martin? - Um 8.00 Uhr steht martin auf. aufstehen - aufstand - ist aufgestanden 起床 Um 6 Uh ...
- 揭开jQuery的面纱-jQuery选择器简介(二)
选择器并没有一个固定的定义,在某种程度上说,jQuery的选择器和样式表中的选择器十分相似.选择器具有如下特点: 1.简化代码的编写 2.隐式迭代 3.无须判断对象是否存在 “$”是选择器不可缺少的部 ...
- Callback-回调-回呼
很早以前看<Delphi 4从入门到精通>有这么一个概念——CallBack.然后在<Delphi 6从入门到精通>看同样的章节,翻译为“回调”,就有一个疑问了,什么是Call ...
- 很实用的html meta标签实现页面跳转
就算你是有很多年开发经验的web开发工程师,有着很多web开发经验,对于先进的web开发技术有着很深刻的研究,然而你却忽略了那些最最基础的东西!现在我来问你,你是否对html所有的标签都能熟练的使用呢 ...
- Nginx服务的地址重写
调整Nginx服务器配置,实现: 1.所有访问a.html的请求,重定向到b.html; 2.所有访问Nginx服务器(192.168.4.1)的请求重定向至www.baidu.com: 3.所有访问 ...
- 一个简单的执行程序的GNU automake自动生成Makefile的方法及案例
一个简单的执行程序的GNU automake自动生成Makefile的方法及案例 在GNU的世界里,存在Automake这样的工具进行自动生成Makefile文件,automake是由Perl语言编写 ...
- Cache占用过多内存导致Linux系统内存不足问题排查
问题描述 Linux服务器内存使用量超过阈值,触发报警. 问题排查 首先,通过free命令观察系统的内存使用情况,显示如下: total used free shared buffers cached ...