为什么在使用QWebView时,必须在QNetworkRequest完成前调用QApplication.processEvents()?

0 投票
1 回答
6592 浏览
提问于 2025-04-18 04:36

通常情况下,当我通过 Qt 4.8 进行网络请求时,我不需要特别调用 QApplication.processEvents() 方法(可以参考这个 StackOverflow 的代码示例)。

但是,当我在 QWebView 中通过 JavaScript 发起网络请求时,如果不一直调用这个方法直到请求完成,它就不会正常工作,下面的例子就说明了这一点(这里有个语法高亮的示例)。

说明:
如果我不调用 processEvents() 方法,网络请求根本不会发送,尽管我理解的情况是 finished 的槽函数似乎已经连接好了。

from PyQt4 import QtCore, QtGui, QtNetwork, QtWebKit

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        QtNetwork.QNetworkProxyFactory.setUseSystemConfiguration(True)
        self.view = QtWebKit.QWebView(self)
        self.setCentralWidget(self.view)
        self.view.setPage(QtWebKit.QWebPage(self.view))

        self.view.page().mainFrame().javaScriptWindowObjectCleared.connect(self.refreshJS)

        self.view.setHtml(
            '''<html>
            <body>
                    LOADING...
                    <script>
                    <!--
                            APP.request();
                    //-->
                    </script>
            </body>
            </html>'''
        )

    @QtCore.pyqtSlot()
    def request(self):
        request = QtNetwork.QNetworkRequest(QtCore.QUrl('http://localhost/test.php'))

        manager = QtNetwork.QNetworkAccessManager()
        manager.finished.connect(self.managerFinished)

        reply = manager.post(request, b'a=A')
        reply.finished.connect(self.finished)

        ############################################################
        ### FIXME: Request never even *sent* if this is missing  ###
        ############################################################

        while not reply.isFinished():
          QtGui.QApplication.processEvents()

        ##########################################################

        print('request FINISHED? '+str(reply.isFinished())+', ERROR '+str(reply.error()))

    def finished(self):
        print('finished')

    def managerFinished(self):
        print('managerFinished')

    def refreshJS(self):
        print('refreshJS')
        self.view.page().mainFrame().addToJavaScriptWindowObject('APP', self)

if __name__ == '__main__':
    import os, sys
    app = QtGui.QApplication(sys.argv)
    MainWindow().show()
    sys.exit(app.exec_())

1 个回答

3

你在使用QNetworkAccessManager时,有一个很大的区别和链接中提到的例子。注意到你使用的是同步方式:在'request'这个槽函数中,你通过等待回复来阻塞事件循环,这样QNetworkAccessManager就无法正常工作,除非你手动推送事件。而链接中的例子是异步的:它创建请求,连接'finished'信号,并让事件循环继续工作。这样事件循环就不会被阻塞,网络管理器就能正常运作。我理解在你的情况下,'request'槽函数需要是同步的,所以这里必须使用processEvents。

编辑

如果你想让request变成异步的,那么你需要确保QNetworkAccessManager对象在request方法之外是持久的。你可以在__init__(self)中初始化它,并用它来进行POST请求。可能QNetworkReply也需要持久化,你需要检查一下。

撰写回答