解决界面阻塞问题

问题

前面我们的练习里开发了一个类似 Postman 的HTTP接口测试工具。

其中,具体发送请求消息的代码如下

    def sendRequest(self):

        method = self.ui.boxMethod.currentText()
        url    = self.ui.editUrl.text()
        payload = self.ui.editBody.toPlainText()

        # 获取消息头
        headers = {}
        # 此处省略一些对消息头的处理

        req = requests.Request(method,
                               url,
                               headers=headers,
                               data=payload
                               )

        prepared = req.prepare()

        self.pretty_print_request(prepared)
        s = requests.Session()
        
        try:
            # 发送请求并且接收响应消息
            r = s.send(prepared)
            # 打印出响应消息
            self.pretty_print_response(r)
        except:
            self.ui.outputWindow.append(
                traceback.format_exc())

这里有一个问题:

我们 点击发送按钮 发送HTTP消息消息,如果服务端接收处理的比较慢,就会导致下面这行代码中的send方法要比较长的时间才能返回。

r = s.send(prepared)

这会导致什么问题呢?

假设10秒钟后,才接收到服务端的响应消息,这时候,界面就会 僵死 10秒钟。

这期间,你点击界面没有任何反应。

为什么呢?

原因

这是因为,我们现在的代码都是在主线程中执行的。

其中最末尾的代码

app.exec_()

其实会让主线程进入一个死循环,循环不断的处理 用户操作的事件。

当我们点击发送按钮后,Qt的 核心代码就会接受到这个 点击事件,并且调用相应的 slot函数去处理。

因为我们代码做了这样的设置

        # 信号处理
        self.ui.buttonSend.clicked.connect(self.sendRequest)

指定了点击发送按钮由 sendRequest 方法处理。

如果这个sendRequest 很快能接收到 服务端的相应,那么sendRequest就可以很快的返回。

返回后, 整个程序又进入到 app.exec_() 里面接收各种 事件,并且调用相应的函数去处理。界面就不会僵死,因为所有的操作界面的事件,都能得到及时的处理。

但是,如果这个sendRequest 要很长时间才能返回,这段时间内,整个程序就停在 下面这行代码处

r = s.send(prepared)

自然就没有机会去处理其他的用户操作界面的事件了,当然程序就僵死了。

解决方法

典型的一种解决方法就是使用多线程去处理。

关于Python的多线程的讲解,可以点击参考我们这里的教程

修改代码如下


    def sendRequest(self):

        method = self.ui.boxMethod.currentText()
        url    = self.ui.editUrl.text()
        payload = self.ui.editBody.toPlainText()

        # 获取消息头
        headers = {}
        # 此处省略一些对消息头的处理

        req = requests.Request(method,
                               url,
                               headers=headers,
                               data=payload
                               )

        prepared = req.prepare()

        self.pretty_print_request(prepared)
        s = requests.Session()

        # 创建新的线程去执行发送方法,
        # 服务器慢,只会在新线程中阻塞
        # 不影响主线程
        thread = Thread(target = self.threadSend,
                        args= (s, prepared)
                        )
        thread.start()

    # 新线程入口函数
    def threadSend(self,s,prepared):

        try:
            r = s.send(prepared)
            self.pretty_print_response(r)
        except:
            self.ui.outputWindow.append(
                traceback.format_exc())

这样,通过创建新的线程去执行发送方法,服务器响应再慢,也只会在新线程中阻塞

主线程启动新线程后,就继续执行后面的代码,返回继续运行Qt的事件循环处理 ,可以响应用户的操作,就不会僵死了。

VIP 小班学员请联系老师获取完整代码示例和视频讲解。