I'm new to PyQT4. I am developing a program with it and I am web scraping to get data into my program. While the information is downloading my GUI locks up. I would like to call this function in a separate background thread, perhaps using QThread, but I am having a tough time wrapping my head around QThread, Qt in general, and the slot/signal way of communication.
I have read about making a generic worker thread that will call any function passed to it. I don't know how to implement it in my main file so that I can run my functions as a background process. If any example code could be shown, please explain each line in detail as I am not understanding the process.
My ui is loaded in from an external file created by Qt 4 Designer.
Full files on Github
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName(_fromUtf8("MainWindow"))
main.py (Main file)
def connections():
# If button is clicked, call summary(), which web scrapes
# for data. This could take 5-30 seconds, this freezes UI.
ui.btnRefreshSummary.clicked.connect(lambda: summary())
# Refresh items in gui
def refresh_ui():
if summary_data != []:
ui.valWatching.setText(summary_data[0])
ui.valBidding.setText(summary_data[1])
ui.valWon.setText(summary_data[2])
ui.valNotWon.setText(summary_data[3])
ui.valPurchases.setText(summary_data[4])
ui.valInvoices.setText(summary_data[5])
def login():
# Scrape website and login while in background;
# This locks up GUI until it completes.
# Pretend this sleep command is the time it takes to login
time.sleep(5) # <-This would lock it up for 5 seconds
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
connections()
# Load credentials from file.
with open('login.txt') as f:
credentials = f.readline().strip().split(':')
f.closed
# Login, download summary, then refresh the UI.
b = Biddergy()
b.login(credentials[0],credentials[1])
summary_data = b.summary()
b.logout()
refresh_ui()
sys.exit(app.exec_())
It's not clear from the example code why connections()
would block (given the code it contains), or why login()
shouldn't block (given that that is what login dialogs normally do). But anyway, the worker class in your example can be converted to a QThread
like this:
class Worker(QThread):
intReady = pyqtSignal(int)
def run(self):
for i in range(1, 10):
time.sleep(1)
self.intReady.emit(i)
and then it can be used like this:
# connections()
# login()
def slot(arg='finished'): print(arg)
thread = Worker()
thread.intReady.connect(slot)
thread.finished.connect(slot)
thread.start()
There are many other ways of achieving the same thing - but which one is most appropriate, and how it would actually be implemented, depends on the details of how your application is intended to work.
With this updated code, I am finally able to start my functions in the background. Now off to learning how to make my background thread communicate with the main UI thread. Much thanks to @ekhumoro for being extremely patient with me.
#!/usr/bin/env python3
from PySide import QtGui, QtCore
from PySide.QtCore import QThread, QObject, Signal, Slot
from main_gui import Ui_MainWindow # my UI from Qt4 Designer(pyside-uic)
from Scrapers import Biddergy # My own class
import sys
import queue
class BiddergyWrapper(QThread):
def __init__(self, q, loop_time=1.0/60):
self.q = q
self.timeout = loop_time
super(BiddergyWrapper, self).__init__()
def onThread(self, function, *args, **kwargs):
self.q.put((function, args, kwargs))
def run(self):
while True:
try:
function, args, kwargs = self.q.get(timeout=self.timeout)
function(*args, **kwargs)
except queue.Empty:
self.idle()
def idle(self):
pass
def _summary(self):
b.summary()
def summary(self):
self.onThread(self._summary)
def _login(self):
b.login()
def login(self):
self.onThread(self._login())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
ui.btnRefreshSummary.clicked.connect(lambda: bw.summary())
# Load credentials from file.
with open('login.txt') as f:
credentials = f.readline().strip().split(':')
# Login, download summary, then refresh the UI.
b = Biddergy(credentials[0], credentials[1])
request_queue = queue.Queue()
bw = BiddergyWrapper(request_queue)
bw.start()
# Run QApplication
app.exec_()
# Begin "Graceful stop?"
bw.quit()
b.logout()
sys.exit()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With