I'm new to GUI-programming and need help with a QThread application. I have a gui program that makes a query and lists the results of the query on the screen. I want an information dialog to open when the listing on the screen is finished. I wrote a function for this and when the operation is successful, it works without any problems. But I couldn't figure out how to show an error dialog if an invalid link entry occurs. Thank you for helping.
-I have commented out some of the methods I tried and failed.
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.finished.connect(self.uyariBox) #---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText("Tüm Sonuçlar Getirildi")
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(ui.uyariBox) #---> When you activate this place, the warning appears instantly and disappears immediately.
#---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except:
print("link error")
self.finished.emit()
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())
You can pass the operation result as string to the uyariBox
method.
First you can create another signal in Bs4Worker
:
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
Change the previous worker.finished
connection to worker.result
:
def startBs4Worker(self):
else:
self.worker.result.connect(self.uyariBox)
#Previous was self.worker.finished.connect(self.uyariBox)
Add variable to function:
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
Then use signal to pass on the result of the operation:
def run(self):
try:
self.result.emit("Tüm Sonuçlar Getirildi")
except Exception as e:
self.result.emit(f"Error: {e}")
Full code:
import sys
import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox
from bs4 import BeautifulSoup
from time import sleep
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(476, 391)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(0, 270, 471, 81))
self.pushButton.setObjectName("pushButton")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(0, 0, 471, 31))
self.lineEdit.setObjectName("lineEdit")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(0, 50, 471, 192))
self.listWidget.setObjectName("listWidget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 476, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.running = False
self.pushButton.clicked.connect(self.startBs4Worker)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "GET"))
def reveiceData(self, data):
# This method is the method that will
# process the data sent by the thread.
if data == None:
return
_translate = QtCore.QCoreApplication.translate
item = QtWidgets.QListWidgetItem()
font = QtGui.QFont()
font.setFamily("Arial")
font.setPointSize(11)
item.setFont(font)
item.setCheckState(QtCore.Qt.Checked)
item.setText(_translate("MainWindow", data))
self.listWidget.addItem(item)
def setThreadStatus(self):
self.running = not self.running
def startBs4Worker(self):
if self.running:
print("Thread is still running ...")
return
else:
self.thread = QThread()
# We send the URL address that the thread will process as a parameter.
self.worker = Bs4Worker(url=self.lineEdit.text())
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.setThreadStatus)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.notifyProgress.connect(self.reveiceData)
self.worker.result.connect(
self.uyariBox) # ---> With this place active, I couldn't figure out how to activate the Error message when wrong connection was entered.
self.running = True
self.thread.start()
def uyariBox(self,message):
self.msg = QMessageBox()
self.msg.thread()
self.msg.setWindowTitle("Bilgi !")
self.msg.setText(message)
self.msg.setIcon(QMessageBox.Information)
self.msg.exec_()
class Bs4Worker(QThread):
result = QtCore.pyqtSignal(str)
notifyProgress = QtCore.pyqtSignal(str)
def __init__(self, url, parent=None):
QThread.__init__(self, parent)
self.url = url
def run(self):
try:
r = requests.get(self.url)
soup = BeautifulSoup(r.content)
linkler = soup.find_all("a")
for link in linkler:
baslik = link.get("title")
# Sending header information to master class
self.notifyProgress.emit(baslik)
self.finished.connect(
ui.uyariBox) # ---> When you activate this place, the warning appears instantly and disappears immediately.
# ---> and there is no click anywhere in the app. (Since completion is not pressed in the warning message)
self.finished.emit()
self.result.emit("Tüm Sonuçlar Getirildi")
# self.finished.connect(ui.uyariBox) #----> Warning does not appear when you activate this place.
except Exception as e:
print("link error")
self.finished.emit()
self.result.emit(f"Error: {e}")
# When I create a new QMessageBox to show the error message, the application crashes.
Uygulama = QApplication(sys.argv)
menu = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(menu)
menu.show()
sys.exit(Uygulama.exec_())