Pyqt5中的Qthreading

2024-09-29 03:28:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个应用程序,你可以输入亚马逊产品的URL和电话号码,当该商品有库存时,它会给你发短信。我正在使用URL,我将使用它,这样当你按下开始按钮时,它会转到你输入的URL,并检查它是否缺货,但当我按下开始按钮时,程序崩溃,并说没有响应。这是我的密码

from PyQt5 import QtCore, QtGui, QtWidgets
import requests
from bs4 import BeautifulSoup
import time
import threading
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import *

class Ui_AmazonStockCheck(object):
        def setupUi(self, AmazonStockCheck):
            AmazonStockCheck.setObjectName("AmazonStockCheck")
            AmazonStockCheck.resize(804, 617)
            AmazonStockCheck.setWindowTitle('Stocker')
            self.centralwidget = QtWidgets.QWidget(AmazonStockCheck)
            self.centralwidget.setObjectName("centralwidget")
            self.urlBox = QtWidgets.QLineEdit(self.centralwidget)
            self.urlBox.setGeometry(QtCore.QRect(50, 90, 161, 51))
            self.urlBox.setObjectName("urlBox")
            font = QtGui.QFont()
            font.setFamily("Bebas Neue")
            font.setPointSize(20)
            self.phonenumberBox = QtWidgets.QLineEdit(self.centralwidget)
            self.phonenumberBox.setGeometry(QtCore.QRect(590, 90, 161, 51))
            self.phonenumberBox.setObjectName("phonenumberBox")
            self.producturl = QtWidgets.QLabel(self.centralwidget)
            self.producturl.setGeometry(QtCore.QRect(60, 50, 111, 31))
            font = QtGui.QFont()
            font.setFamily("Bebas Neue")
            font.setPointSize(20)
            self.producturl.setFont(font)
            self.producturl.setObjectName("producturl")
            self.phonenumber = QtWidgets.QLabel(self.centralwidget)
            self.phonenumber.setGeometry(QtCore.QRect(600, 60, 131, 31))
            font = QtGui.QFont()
            font.setFamily("Bebas Neue")
            font.setPointSize(20)
            self.phonenumber.setFont(font)
            self.phonenumber.setObjectName("phonenumber")
            self.startbutton = QtWidgets.QPushButton(self.centralwidget)
            self.startbutton.setGeometry(QtCore.QRect(320, 160, 141, 41))
            self.startbutton.clicked.connect(lambda: self.onclick(self.urlBox.text()))
            font = QtGui.QFont()
            font.setFamily("Bebas Neue")
            font.setPointSize(16)
            self.startbutton.setFont(font)
            self.startbutton.setObjectName("startbutton")
            self.abouttext = QtWidgets.QLabel(self.centralwidget)
            self.abouttext.setGeometry(QtCore.QRect(120, 240, 601, 231))
            font = QtGui.QFont()
            font.setFamily("Bebas Neue")
            font.setPointSize(28)
            self.abouttext.setFont(font)
            self.abouttext.setObjectName("abouttext")
            AmazonStockCheck.setCentralWidget(self.centralwidget)
            self.menubar = QtWidgets.QMenuBar(AmazonStockCheck)
            self.menubar.setGeometry(QtCore.QRect(0, 0, 804, 21))
            self.menubar.setObjectName("menubar")
            AmazonStockCheck.setMenuBar(self.menubar)
            self.statusbar = QtWidgets.QStatusBar(AmazonStockCheck)
            self.statusbar.setObjectName("statusbar")
            AmazonStockCheck.setStatusBar(self.statusbar)

            self.retranslateUi(AmazonStockCheck)
            QtCore.QMetaObject.connectSlotsByName(AmazonStockCheck)

        def retranslateUi(self, AmazonStockCheck):
                _translate = QtCore.QCoreApplication.translate
                AmazonStockCheck.setWindowTitle(_translate("AmazonStockCheck", "MainWindow"))
                self.producturl.setText(_translate("AmazonStockCheck", "Product Url"))
                self.phonenumber.setText(_translate("AmazonStockCheck", "Phone Number"))
                self.startbutton.setText(_translate("AmazonStockCheck", "Start"))
                self.abouttext.setText(_translate("AmazonStockCheck", "App developed by Gabriel Zebersky Beta Version"))


        def onclick(self, url):
            headers = {"User-Agent": 'Mozilla/5.0 (X11; Linux x86_64)', 'Cache-Control': 'no-cache', "Pragma": "no-cache"}
            page = requests.get(url, headers=headers)
            soup = BeautifulSoup(page.content, 'html.parser')
            while True:
                if soup.find(id='outOfStock'):
                    print('NOT IN STOCK')
                    time.sleep(2)
            else:
                print('IN STOCK')


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    AmazonStockCheck = QtWidgets.QMainWindow()
    ui = Ui_AmazonStockCheck()
    ui.setupUi(AmazonStockCheck)
    AmazonStockCheck.show()
    sys.exit(app.exec_())

我知道穿线可能有用。是否有任何方法可以使用Qthread或Qthreadpool来修复此问题


Tags: importselftranslatefontphonenumberqtguiqtcoreqtwidgets
1条回答
网友
1楼 · 发布于 2024-09-29 03:28:32

Windows会告诉您,您的应用程序(据说)崩溃是因为它挂起了。这意味着代码没有处理任何事件(因此应用程序被冻结),可能是因为它在做其他事情

在应用程序中,当您单击按钮时,将调用onclick方法。但此函数永远不会返回,因为它将无限期地检查项目可用性(由于while True而没有break)。
它所做的是,在点击按钮后,主线程(在PyQt中处理GUI)被卡在这个方法中

实际上,您应该为此使用一个线程:辅助线程将卡在一个循环中,但主线程仍然可以自由运行和处理事件

下面是一个不阻塞主线程的辅助线程的最小示例:

import random
import time
import typing

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_AmazonStockCheck(object):
        def setupUi(self, AmazonStockCheck):
            AmazonStockCheck.resize(804, 617)
            AmazonStockCheck.setWindowTitle('Stocker')
            self.centralwidget = QtWidgets.QWidget(AmazonStockCheck)
            self.urlBox = QtWidgets.QLineEdit(self.centralwidget)
            self.urlBox.setGeometry(QtCore.QRect(50, 90, 161, 51))
            self.startbutton = QtWidgets.QPushButton("Start", self.centralwidget)
            self.startbutton.setGeometry(QtCore.QRect(320, 160, 141, 41))
            self.startbutton.clicked.connect(lambda: self.onclick(self.urlBox.text()))
            QtCore.QMetaObject.connectSlotsByName(AmazonStockCheck)

            layout = QtWidgets.QVBoxLayout()
            layout.addWidget(self.urlBox)
            layout.addWidget(self.startbutton)
            self.centralwidget.setLayout(layout)
            AmazonStockCheck.setCentralWidget(self.centralwidget)

        def onclick(self, url):
            print(f"starting to check availability for {url!r}")
            self.checker = AvailabilityChecker(url, self.centralwidget)
            print("checker has started")
            self.checker.availability_update.connect(
                lambda is_available: print(f"received update: available={is_available}")
            )


class AvailabilityChecker(QtCore.QObject):
    def __init__(self, url, parent: QtCore.QObject):
        super().__init__(None)  # a QObject with a parent can not be moveToThread'd
        self.url = url

        self._thread = QtCore.QThread(parent)
        self.moveToThread(self._thread)
        assert QtCore.QThread.currentThread() != self._thread  # we are still running in the caller's thread
        self._thread.started.connect(self.check_indefinitely)
        self._thread.start()

    def check_indefinitely(self) -> typing.NoReturn:
        assert QtCore.QThread.currentThread() == self._thread  # we are now running in our dedicated thread
        while True:
            is_available = (random.randint(0, 10) == 9)
            if is_available:
                print("AVAILABLE")
            else:
                print("NOT AVAILABLE")
            self.availability_update.emit(is_available)
            time.sleep(2)

    availability_update = QtCore.pyqtSignal([bool])


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    main_window = QtWidgets.QMainWindow()
    ui = Ui_AmazonStockCheck()
    ui.setupUi(main_window)
    main_window.show()
    sys.exit(app.exec_())

您可以看到,您仍然可以使用GUI,而worker在其自己的线程中无休止地检查可用性(为了方便起见,我只是random

我只是将lambda连接到worker发出的信号,但您应该将其连接到GUI的方法以更新其状态(更改颜色、文本等)

此外,线程无休止地运行,因此应用程序不会干净地退出。您应该考虑在您的工作人员上设置一个时隙,以更改布尔^ ^ {CD5}},并将该循环更改为^ {CD6}},这样您就可以调用该时隙使您的线程不久之后退出。p>

但我希望你能理解我回答的要点。是的,在线程中使用对象(“工作线程”)。使用信号在工作人员和GUI之间进行通信。关于如何做到这一点,有很多例子和文档

如果适合的话,我发现让worker类也继承QRunnable(除了信号/插槽的QObject之外)可以将所有线程委托给QThreadPool,这简化了大部分代码

相关问题 更多 >