在发送 http 请求时让 PyQt5 标签动态更新 [英] Have PyQt5 Labels dynamically update while sending http requests

查看:57
本文介绍了在发送 http 请求时让 PyQt5 标签动态更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我一直在使用基于模块的 http 请求开发异步 python scraper.为此,我一直在使用 asks 和 importlib,我想制作一个简单的小图形用户界面,它可以更新请求的状态代码.我做到了.

So I've been working on a async python scraper with http requests based on modules. So for that I've been using asks and importlib, and I'd like to make a simple little GUI that updates with the status codes of requests. And I did.

一切都很好,但我似乎遇到了一个问题,因为请求已成功发送,GUI 显示出来,但它仅在所有请求发送后才显示,而不是在发送请求时动态显示

Everything is great, but I seem to have an issue as the requests are sent sucessfully, the GUI shows up, but it only shows up once all the requests are sent, and not dynamically while the requests are being sent

我一直在玩教程和 Qtimer,但在我看到的所有教程和帮助主题中,例如:

I've been playing around with tutorials and Qtimer, but in all the tutorials and help threads I've seen such as:

PyQt5:更新标签?

我已尝试根据我的情况实施代码,但我唯一能做的就是让 GUI 在发送请求的同时显示出来,但它保持冻结(不响应)直到所有请求已完成

I have tried to implement the code to my situation, but the only thing I've manage to do is for the GUI to show up at the same time the requests are sent, but it stays frozen (not responding) until all the requests are finished

import trio
from asks import Session
import importlib
from PyQt5.QtWidgets import QLabel, QMainWindow, QApplication, QWidget, QVBoxLayout
from PyQt5 import QtCore
import qdarkstyle
app = QApplication(sys.argv)
app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())


module = importlib.import_module("get_module")
good = 0
bad = 0
total = 0

class Menu(QMainWindow):

    def __init__(self):
        global good, bad, total
        super().__init__()
        self.setWindowTitle("Status Codes")

        self.central_widget = QWidget()               
        self.setCentralWidget(self.central_widget)    
        lay = QVBoxLayout(self.central_widget)
        
        self.resize(500, 350)
        ok = QLabel("200: <font color='green'>0</font>")
        ok.setAlignment(QtCore.Qt.AlignHCenter)
        bad = QLabel("400: <font color='yellow'>0</font>")
        bad.setAlignment(QtCore.Qt.AlignHCenter)
        total = QLabel("Total: <font color='#00FF00'>0</font>")
        total.setAlignment(QtCore.Qt.AlignHCenter)
        r_total, r_good, r_bad  = self.check()
        QtCore.QTimer.singleShot(1000, lambda: self.updateLabels(r_total, r_good, r_bad))
        lay.addWidget(ok)
        lay.addWidget(bad)
        lay.addWidget(total)
        self.show()
        

    def check(self):
        async def worker1(s):
            global ok
            global bad
            global total
            if module.method.lower() == "get":
                r = await s.get(module.request(), params=module.settings())
            elif module.method.lower() == "post":
                r = await s.post(module.request(), data=module.settings())
            if any(x in r.status_code for x in module.error):
                print("BAD -- " + module.request())
                r_total += 1
                r_invalid += 1
            else:
                print("GOOD -- " + module.request())
                r_total += 1
                r_valid += 1
                print(r.text)
        async def worker2(s):
            global ok
            global bad
            global total
            if module.method.lower() == "get":
                r = await s.get(module.request(), params=module.settings())
            elif module.method.lower() == "post":
                r = await s.post(module.request(), data=module.settings())
            if any(x in r.status_code for x in module.error):
                print("BAD -- " + module.request())
                r_total += 1
                r_invalid += 1
            else:
                print("GOOD -- " + module.request())
                r_total += 1
                r_valid += 1
                print(r.text)                    
            
    async def example():
            
        s = Session(connections=module.connections)
        for i in range(10):
            async with trio.open_nursery() as nursery:
                nursery.start_soon(worker1, s)
                nursery.start_soon(worker2, s)

        trio.run(example)
        print("Total:", r_total)
        print("Total good:", r_valid)
        print("Total bad:", r_invalid)
        return r_total, r_valid, r_invalid
    def updateLabels(self, r_total, r_card, r_invalid):
        good.setText("200: <font color='green'>%s</font>" % (r_valid))
        bad.setText("400: <font color='#00FF00'>%s</font>" % (r_invalid))
        total.setText("Total: <font color='#F40D30'>%s</font>" % (r.total))

        
    
             
if __name__ == '__main__':        
    ex = Menu()
    sys.exit(app.exec_())

现在我想让它做的是 GUI 显示出来,并且动态地(或每 1 秒)200、400 和总标签显示发出了多少请求,以及返回 200 和 400 的请求数.

Now what I would like it to do is that the GUI shows up and that dynamically (or every 1 second) the 200, 400 and total labels show how many requests have been made, and how many returned 200 and 400.

然而,它会显示它们(它确实显示总数、总数 200 和总数 400)但只有在所有请求都完成后而不是动态地显示

However instead it'll show them (it does show the total, total 200 and total 400) but only once all the requests are finished and not dynamically on the side

推荐答案

异步任务阻塞了 GUI 的线程,所以这些任务必须在另一个线程中执行并通过信号发送给 GUI,这也允许我们分开业务逻辑和 GUI 具有更清晰的代码:

Asynchronous tasks block the thread of the GUI, so those tasks must be executed in another thread and send it through signals to the GUI, also that allows us to separate the business logic and the GUI having a cleaner code:

get_module.py

# coding: utf8

#======================= IMPORT AREA =======================
#here's where you import any module needed for the website
from random import randint

#===========================================================

#====================== SETTINGS AREA ======================
#here's where you declare the settings of the website such
#as method, error key, success key, custom settings, etc...
name = "GET Test Config"
method = 'GET' #Method is either GET, POST or CUSTOM
error = ['400', '401', '402', '403', '404', '405', '406', '407', '408', '409', '410', '411', '412', '413', '414', '415', '416', '417', '418', '421', '422', '423', '424', '425', '426', '428', '429', '431', '451','500', '501', '502', '503', '504', '505', '506', '507', '508', '510', '511']
connections = 5
#===========================================================

#====================== DEFINITON AREA =====================
#here's where the definitions are made.
#There's 2 defs:
#def request(): Which returns the url (with or without modifications)
#def settings(): returns the data to send in the GET request


#====== SETTINGS AREA ======
def request():
    url = "https://httpbin.org/anything"
    return url

def settings():
    data = {'example':'example'}
    return (data)

#===========================================================

main.py

import importlib

import multio
import qdarkstyle
import trio
from PyQt5 import QtCore, QtWidgets
from asks import Session


module = importlib.import_module("get_module")


class TaskWorker(QtCore.QObject):
    totalChanged = QtCore.pyqtSignal(int)
    validChanged = QtCore.pyqtSignal(int)
    invalidChanged = QtCore.pyqtSignal(int)

    def __init__(self, parent=None):
        super(TaskWorker, self).__init__(parent)
        self._total = 0
        self._valid = 0
        self._invalid = 0

    @QtCore.pyqtProperty(int, notify=totalChanged)
    def total(self):
        return self._total

    @total.setter
    def total(self, value):
        if self._total == value:
            return
        self._total = value
        self.totalChanged.emit(self._total)

    @QtCore.pyqtProperty(int, notify=validChanged)
    def valid(self):
        return self._valid

    @valid.setter
    def valid(self, value):
        if self._valid == value:
            return
        self._valid = value
        self.validChanged.emit(self._valid)

    @QtCore.pyqtProperty(int, notify=invalidChanged)
    def invalid(self):
        return self._invalid

    @invalid.setter
    def invalid(self, value):
        if self._invalid == value:
            return
        self._invalid = value
        self.invalidChanged.emit(self._invalid)

    @QtCore.pyqtSlot()
    def check(self):
        async def worker1(s):
            if module.method.lower() == "get":
                r = await s.get(module.request(), params=module.settings())
            elif module.method.lower() == "post":
                r = await s.post(module.request(), data=module.settings())
            # if any(x in r.status_code for x in module.error):
            if str(r.status_code) in module.error:
                print("BAD -- " + module.request())
                self.total += 1
                self.invalid += 1
            else:
                print("GOOD -- " + module.request())
                self.total += 1
                self.valid += 1
                print(r.text)

        async def worker2(s):
            if module.method.lower() == "get":
                r = await s.get(module.request(), params=module.settings())
            elif module.method.lower() == "post":
                r = await s.post(module.request(), data=module.settings())
            # if any(x in r.status_code for x in module.error):
            if str(r.status_code) in module.error:
                print("BAD -- " + module.request())
                self.total += 1
                self.invalid += 1
            else:
                print("GOOD -- " + module.request())
                self.total += 1
                self.valid += 1
                print(r.text)

        async def example():
            s = Session(connections=module.connections)
            for i in range(40):
                async with trio.open_nursery() as nursery:
                    nursery.start_soon(worker1, s)
                    nursery.start_soon(worker2, s)

        multio.init("trio")
        trio.run(example)


class Menu(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Status Codes")
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        lay = QtWidgets.QVBoxLayout(self.central_widget)

        self.resize(500, 350)
        self.ok = QtWidgets.QLabel(
            "200: <font color='green'>0</font>",
            alignment=QtCore.Qt.AlignHCenter,
        )
        self.bad = QtWidgets.QLabel(
            "400: <font color='yellow'>0</font>",
            alignment=QtCore.Qt.AlignHCenter,
        )
        self.total = QtWidgets.QLabel(
            "Total: <font color='#00FF00'>0</font>",
            alignment=QtCore.Qt.AlignHCenter,
        )
        lay.addWidget(self.ok)
        lay.addWidget(self.bad)
        lay.addWidget(self.total)

        thread = QtCore.QThread(self)
        thread.start()

        self.worker = TaskWorker()
        self.worker.moveToThread(thread)
        self.worker.totalChanged.connect(self.updateTotal)
        self.worker.validChanged.connect(self.updateValid)
        self.worker.invalidChanged.connect(self.updateInvalid)
        QtCore.QTimer.singleShot(0, self.worker.check)

    @QtCore.pyqtSlot(int)
    def updateTotal(self, total):
        self.total.setText("Total: <font color='#F40D30'>%s</font>" % (total))

    @QtCore.pyqtSlot(int)
    def updateValid(self, valid):
        self.ok.setText("200: <font color='green'>%s</font>" % (valid))

    @QtCore.pyqtSlot(int)
    def updateInvalid(self, invalid):
        self.bad.setText("400: <font color='#00FF00'>%s</font>" % (invalid))


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    ex = Menu()
    ex.show()
    sys.exit(app.exec_())

这篇关于在发送 http 请求时让 PyQt5 标签动态更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆