GUI 在循环时变得无响应

GUI become unresponsive while looping

单击按钮后,表单变得无响应,直到解析函数完成其工作。

我想将 searchAll 函数移动到线程。看了几个类似问题的答案,但是没看懂。

class MyForm(QDialog):


    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.buttonOK.clicked.connect(self.searchAll)
        self.show()

    def searchAll(self):

        sID = self.ui.txtSellerID.text()
        sUrl = "https://removed.com/" + sID + "/p/?section=2&page=1"
        sr = requests.get(sUrl)
        soup1 = BeautifulSoup(sr.text, "html.parser")

        NumberOfPagesBlock = soup1.find_all("li", class_="text-gray")

        if not NumberOfPagesBlock:
            QMessageBox.about(self, "Warning", "Nothing Here")
        else:
            items = re.compile(r'[^\d.]+')
            PagesCount = -(-items // 60)

            for i in range(1, int(PagesCount + 1)):
                itemsIdDs = soup1.find_all("div", class_="large-single-item")

                for itemsIdD in itemsIdDs:
                    iUrl = ("https://removed.com/" + itemsIdDs.get('data-ean') + "/s")
                    r = requests.get(iUrl)
                    soup = BeautifulSoup(r.text, "html.parser")
                    seller = soup.find("div", id="productTrackingParams")
                    title = (str(ctr) + '- ' + "Title " + str(seller.get('data-title')))
                    self.ui.txtDetails.appendPlainText(title)


if __name__ == "__main__":

    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())

您必须在另一个线程中执行繁重的任务(请求 + BeautifulSoup),因为它们会阻塞 GUI 所在的主线程,从而阻止 GUI 正常工作,例如,通过冻结屏幕。在这种情况下,我将实施工作线程方法:

import re
import ssl
import sys
from functools import partial

import requests
from PyQt5.QtCore import QObject, QThread, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox
from bs4 import BeautifulSoup

from foo_module import Ui_Dialog


class ScrapeWorker(QObject):
    started = pyqtSignal()
    finished = pyqtSignal()
    resultsChanged = pyqtSignal(str)
    errorSignal = pyqtSignal(str)

    @pyqtSlot(str)
    def run(self, text):
        self.started.emit()
        sUrl = "https://removed.com/{}/p/?section=2&page=1".format(text)
        try:
            sr = requests.get(sUrl)
        except Exception as e:
            self.errorSignal.emit("error: {}".format(e))
            self.finished.emit()
            return
        soup1 = BeautifulSoup(sr.text, "html.parser")
        NumberOfPagesBlock = soup1.find_all("li", class_="text-gray")
        if not NumberOfPagesBlock:
            self.errorSignal.emit("Nothing Here")
        else:
            items = re.compile(r"[^\d.]+")
            PagesCount = -(-items // 60)
            for i in range(1, int(PagesCount + 1)):
                itemsIdDs = soup1.find_all("div", class_="large-single-item")

                for itemsIdD in itemsIdDs:
                    iUrl = "https://removed.com/{}/s".format(itemsIdDs.get("data-ean"))
                    r = requests.get(iUrl)
                    soup = BeautifulSoup(r.text, "html.parser")
                    seller = soup.find("div", id="productTrackingParams")
                    title = "{}- Title {}".format(ctr, seller.get("data-title"))
                    self.resultsChanged.emit(title)
        self.finished.emit()


class MyForm(QDialog):
    def __init__(self):
        super().__init__()
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.ui.buttonOK.clicked.connect(self.searchAll)

        thread = QThread(self)
        thread.start()

        self.m_worker = ScrapeWorker()
        self.m_worker.moveToThread(thread)
        self.m_worker.started.connect(self.onStarted)
        self.m_worker.finished.connect(self.onFinished)
        self.m_worker.resultsChanged.connect(self.onResultChanged)
        self.m_worker.errorSignal.connect(self.onErrorSignal)

    @pyqtSlot()
    def searchAll(self):
        sID = self.ui.txtSellerID.text()
        wrapper = partial(self.m_worker.run, sID)
        QTimer.singleShot(0, wrapper)

    @pyqtSlot(str)
    def onResultChanged(self, title):
        self.ui.txtDetails.appendPlainText(title)

    @pyqtSlot()
    def onStarted(self):
        self.ui.buttonOK.setEnabled(False)

    @pyqtSlot()
    def onFinished(self):
        self.ui.buttonOK.setEnabled(True)

    @pyqtSlot(str)
    def onErrorSignal(self, message):
        QMessageBox.about(self, "Warning", message)


if __name__ == "__main__":

    app = QApplication(sys.argv)
    w = MyForm()
    w.show()
    sys.exit(app.exec_())