一次失败的QThread多线程加载Webpage.load()页面的尝试

本文讲述了作者在使用PyQt时遇到的QThread多线程加载网页问题,原本为了解决GUI卡住而采用多线程,但发现QWebFrame.load()已经是异步加载,错误地认为其导致界面卡顿。经过排查,发现真正原因是调用load()前的操作,修复后解决了问题,提醒开发者在解决问题时需深入理解并仔细检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题

前段时间在用PyQt4写网页加载部分时一开始用的从QWebView.load()(QWebFrame.load()),但由于这样会卡住GUI,于是换了QNetworkAccessManager模块来访问网络,返回内容用setcontent()传递给QWebView:

http://blog.youkuaiyun.com/chroming/article/details/51832244

当时还觉得解决了一个挺有意义的问题,毕竟搜索GUI卡住的问题确实有不少人因为load()导致GUI界面卡住。

由于QNetworkAccessManager是比较底层的访问网络模块,不少东西要自己处理。比如某些网址HTML中声明的编码与response header中声明的编码不同导致的网页显示乱码,某些网页无法显示等问题。对于乱码问题我还通过读取response header解决了一部分,但对于无法显示的问题却没找到原因。后来测试QWebFrame.load(),发现这些问题都不存在。于是考虑使用多线程加载网页,既能解决各种兼容性问题,又不会导致GUI卡住。

寻找解决办法

虽然Python自己有多线程模块,但因为程序里用了很多Qt模块,这里也考虑直接用Qt的自带多线程模块,QThread。搜了资料,写出了独立的多线程模块:

# -*- coding: utf-8 -*-
from PyQt4 import QtCore


# load加载页面线程
class loadUrl(QtCore.QThread):
    def __init__(self, qurl, qwebframe, parent=None):
        super(loadUrl, self).__init__(parent)
        self.qurl = qurl
        self.qwebframe = qwebframe

    def run(self):
        self.qwebframe.load(self.qurl)

在主代码中调用多线程模块:

class test():
    ...
    def testfun():
        ...
        from multiThread import loadUrl
        self.multiget = loadUrl(QUrl(input_url), self.webView.page().mainFrame())
        self.multiget.start()

结果出现了错误:

QObject: Cannot create children for a parent that is in a different thread

网上查到类似的情况:http://stackoverflow.com/questions/7143088/how-do-you-set-up-multiple-multithreaded-qwebviews-in-pyqt

回答如下:

There are multiple issues with your question and code:

  • You are talking about QWebFrame, but are actually passing a QWebView to your worker(s). Since this is a QWidget, it belongs to the main (GUI) thread and should not be modified by other threads.
  • One QWebView / QWebFrame can only load one URL at a time, so you cannot share it across multiple workers.
  • QWebFrame.load() loads data asynchronously, i.e. a call to load() returns immediately and there will be no data to read yet. You will have to wait for the loadFinished() signal to be emitted before you can access the data.
  • Since the actual loading is done by the networking layer of the operating system, and the *load()*method does not block, there is no need to run it in a separate thread in the first place. Why do you claim that this should be faster – it makes no sense.
  • Since you want to load hundreds of URLs in parallel (or about 10, you are mentioning both in the same sentence), are you sure that you want to use QWebFrame, which is a presentation class? Do you actually want to render the HTML or are you just interested in the data retrieved?

其中第一个issue回答了为什么会出现这个报错:主线程中的QWebFrame不能用子线程修改。但相比这条我更在意第三第四条:QWebFrame.load()是异步加载页面的,并不会阻塞主线程,不需要用子线程去加载页面。这就奇怪了,之前我用的时候明明是因为加载页面导致GUI卡住的。于是回去看自己的代码,发现了一点:

self.webView.load(QUrl(input_url))
app.restoreOverrideCursor()

于是随手把app.restoreOverrideCursor() 改到load之前。结果发现不卡了!一直以来困扰我的竟然是由于这个!

总结

这个问题让我花了一些冤枉时间来处理网页兼容性,结果发现只是一个小小的失误导致的。虽然之前在搜索网页卡住的问题时也看到有人说load()是异步加载,但由于自己的程序确实卡住了这个事实,也没深究为什么异步还会卡住。今后在处理问题时需要更加仔细。

参考资料:

  1. 《PyQt 分离UI主线程与工作线程》http://blog.youkuaiyun.com/Mr_Zing/article/details/46945011
  2. 《How do you set up multiple multithreaded QWebViews in PyQt?》http://stackoverflow.com/questions/7143088/how-do-you-set-up-multiple-multithreaded-qwebviews-in-pyqt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值