我正在开发一个应用程序,你可以在该应用程序中输入亚马逊产品的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来解决这个问题吗?
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(为了方便起见,我只是random
(。
我只是将lambda连接到工作者发出的信号,但您应该将其连接到GUI的一个方法,以更新其状态(更改颜色、文本,等等(。
此外,线程无休止地运行,因此应用程序不会干净地退出。您应该考虑在工作线程上设置一个插槽来更改布尔should_stop
,并将循环更改为while not should_stop
,这样您就可以调用该插槽使线程在不久后退出。
但我希望你能理解我回答的要点。是的,在线程中使用一个对象("worker"(。使用信号在您的工作人员和GUI之间进行通信。关于如何做到这一点,有很多例子和文档。
如果合适的话,我发现让worker类也继承QRunnable(除了信号/插槽的QObject(可以将所有线程委托给QThreadPool,这简化了大部分代码。