Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating GUI elements in MultiThreaded PyQT

Tags:

I was researching for some time to find information how to do multithreaded program using PyQT, updating GUI to show the results.

I'm used to learning by example and i can't find (yes i was looking for weeks) any simple example of program using multithreading doing such simple task as for example connecting to list of www sites (5 threads) and just printing processed urls with response code.

Could anyone share code or send me to good tutorial where such program is explained ?

like image 577
Nuncjo Avatar asked Mar 31 '12 15:03

Nuncjo


2 Answers

Here some very basic examples.

You can pass references to GUI elements to threads, and update them in thread.

import sys import urllib2  from PyQt4 import QtCore, QtGui   class DownloadThread(QtCore.QThread):     def __init__(self, url, list_widget):         QtCore.QThread.__init__(self)         self.url = url         self.list_widget = list_widget      def run(self):         info = urllib2.urlopen(self.url).info()         self.list_widget.addItem('%s\n%s' % (self.url, info))   class MainWindow(QtGui.QWidget):     def __init__(self):         super(MainWindow, self).__init__()         self.list_widget = QtGui.QListWidget()         self.button = QtGui.QPushButton("Start")         self.button.clicked.connect(self.start_download)         layout = QtGui.QVBoxLayout()         layout.addWidget(self.button)         layout.addWidget(self.list_widget)         self.setLayout(layout)      def start_download(self):         urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',                 'http://stackoverflow.com/', 'http://www.youtube.com/']         self.threads = []         for url in urls:             downloader = DownloadThread(url, self.list_widget)             self.threads.append(downloader)             downloader.start()  if __name__ == "__main__":     app = QtGui.QApplication(sys.argv)     window = MainWindow()     window.resize(640, 480)     window.show()     sys.exit(app.exec_()) 

Editors Note: Qt widgets are not thread safe and should not be accessed from any thread but the main thread (see the Qt documentation for more details). The correct way to use threads is via signals/slots as the second part of this answer shows.


Also, you can use signals and slots, to separate gui and network logic.

import sys import urllib2  from PyQt4 import QtCore, QtGui   class DownloadThread(QtCore.QThread):      data_downloaded = QtCore.pyqtSignal(object)      def __init__(self, url):         QtCore.QThread.__init__(self)         self.url = url      def run(self):         info = urllib2.urlopen(self.url).info()         self.data_downloaded.emit('%s\n%s' % (self.url, info))   class MainWindow(QtGui.QWidget):     def __init__(self):         super(MainWindow, self).__init__()         self.list_widget = QtGui.QListWidget()         self.button = QtGui.QPushButton("Start")         self.button.clicked.connect(self.start_download)         layout = QtGui.QVBoxLayout()         layout.addWidget(self.button)         layout.addWidget(self.list_widget)         self.setLayout(layout)      def start_download(self):         urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',                 'http://stackoverflow.com/', 'http://www.youtube.com/']         self.threads = []         for url in urls:             downloader = DownloadThread(url)             downloader.data_downloaded.connect(self.on_data_ready)             self.threads.append(downloader)             downloader.start()      def on_data_ready(self, data):         print data         self.list_widget.addItem(unicode(data))   if __name__ == "__main__":     app = QtGui.QApplication(sys.argv)     window = MainWindow()     window.resize(640, 480)     window.show()     sys.exit(app.exec_()) 
like image 154
reclosedev Avatar answered Oct 09 '22 15:10

reclosedev


While reclosedev's answer worked perfectly for me, I was getting a QThread: Destroyed while thread is still running error. To correct this, I passed the parent class reference to the QThread constructor, as indicated in this question.

import sys import urllib2  from PyQt4 import QtCore, QtGui   class DownloadThread(QtCore.QThread):      data_downloaded = QtCore.pyqtSignal(object)      def __init__(self, parent, url):         QtCore.QThread.__init__(self, parent)         self.url = url      def run(self):         info = urllib2.urlopen(self.url).info()         self.data_downloaded.emit('%s\n%s' % (self.url, info))   class MainWindow(QtGui.QWidget):     def __init__(self):         super(MainWindow, self).__init__()         self.list_widget = QtGui.QListWidget()         self.button = QtGui.QPushButton("Start")         self.button.clicked.connect(self.start_download)         layout = QtGui.QVBoxLayout()         layout.addWidget(self.button)         layout.addWidget(self.list_widget)         self.setLayout(layout)      def start_download(self):         urls = ['http://google.com', 'http://twitter.com', 'http://yandex.ru',                 'http://stackoverflow.com/', 'http://www.youtube.com/']         self.threads = []         for url in urls:             downloader = DownloadThread(parent=self, url)             downloader.data_downloaded.connect(self.on_data_ready)             self.threads.append(downloader)             downloader.start()      def on_data_ready(self, data):         print data         self.list_widget.addItem(unicode(data))   if __name__ == "__main__":     app = QtGui.QApplication(sys.argv)     window = MainWindow()     window.resize(640, 480)     window.show()     sys.exit(app.exec_()) 
like image 39
Lucas Franceschi Avatar answered Oct 09 '22 15:10

Lucas Franceschi