Updating GUI elements in MultiThreaded PyQT
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_())
Update PyQt GUI from a Python thread
Define a custom signal that sends updates to the progress-bar:
class Form(QMainWindow):
finished = pyqtSignal()
updateProgress = pyqtSignal(int)
def __init__(self, parent=None):
super(Form, self).__init__(parent)
...
self.updateProgress.connect(self.ui.progressBar.setValue)
def run_test(self):
for i in range(100):
per = i + 1
self.updateProgress.emit(per)
...
Updating Python GUI element from Qthread
There are a few things wrong with your code, however you were pretty close.
The first obvious one is that you have a loop with a time.sleep()
in your main thread. The Ui_Dialog.get_time()
method runs in the main thread (as it should). You should not have any long running code there. However, the loop with the time.sleep(2)
in it is long running code. As it stands now, your GUI locks up because control it not returned to the GUI event loop for 2*countto
seconds. Just remove the whole while loop. I don't really know why it is there.
Delete this:
q = 0
while q < 5:
print(countto+2)
time.sleep(2)
q += 1
The next issue comes because you recreate the QThread
each time you click the button. As such, for each new object, you need to connect the updateProgress
signal in that object to the setProgress
slot. So change the code there to:
self.wt = WorkerThread(countto)
self.wt.updateProgress.connect(self.setProgress)
self.wt.start()
At this point you will see the progress bar update correctly. However, the maximum value of the progress bar is set to 100 by default. So you might want to set the maximum value to countto
just before you create the thread. For example:
self.progressBar.setMaximum(countto)
self.wt = WorkerThread(countto)
Hope that explains things!
Easy Multi-threading with PyQt5, for updating QTextBrowser contents
You do not have to update the GUI from an external thread. There are several options like signals, QMetaObject::invokeMethod(...)
, QEvent and QTimer::singleShot(0, ...)
with pyqtSlot.
Using the last method the solution is as follows:
from functools import partial
from PyQt5.QtCore import pyqtSlot
class MyApp(QMainWindow):
# ...
@pyqtSlot()
def stop_loader(self):
self.ui.loader.hide()
self.loading_animate.stop()
def get_response(self, text):
plain_text, speech = get_Wresponse(text)
QtCore.QTimer.singleShot(0, self.stop_loader)
wrapper = partial(self.ui.textDisplay.setText, plain_text)
QtCore.QTimer.singleShot(0, wrapper)
if speech == '':
say("Here you GO!")
else:
say(speech)
def load_response(self):
self.start_loader()
text = self.ui.queryBox.displayText()
_thread.start_new_thread(self.get_response, (text,))
Multithreading updating with multiple windows in pyqt5
Why is it necessary to use multi-threading? The threads are not the magic solution, and on the contrary if their disadvantages are not known then they bring more problems than solutions. In your case it is unnecessary as you don't have any time consuming task that blocks the GUI. Bottom line: Use tools when necessary, not when you want.
On the other hand you are creating GUI from another thread which Qt prohibits since it is not thread-safe.
class AnotherWindow(QWidget):
def __init__(self):
super().__init__()
self.x = 0
layout = QVBoxLayout()
self.label = QLabel("Another Window")
layout.addWidget(self.label)
self.line = QLineEdit(self)
layout.addWidget(self.line)
self.setLayout(layout)
timer = QTimer(self)
timer.timeout.connect(self.update_line)
timer.start()
def update_line(self):
self.x += 1
self.line.setText(str(self.x))
class AnotherWindow2(QWidget):
def __init__(self):
super().__init__()
self.x = 0
layout = QVBoxLayout()
self.label = QLabel("Another Window2")
layout.addWidget(self.label)
self.line = QLineEdit(self)
layout.addWidget(self.line)
self.setLayout(layout)
timer = QTimer(self)
timer.timeout.connect(self.update_line)
timer.start()
def update_line(self):
self.x += 3
self.line.setText(str(self.x))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
l = QVBoxLayout()
button1 = QPushButton("Push for Window 1")
button1.clicked.connect(self.show_new_window)
l.addWidget(button1)
button2 = QPushButton("Push for Window 2")
button2.clicked.connect(self.show_new_window2)
l.addWidget(button2)
w = QWidget()
w.setLayout(l)
self.setCentralWidget(w)
def show_new_window(self):
self.w = AnotherWindow()
self.w.show()
def show_new_window2(self):
self.w2 = AnotherWindow2()
self.w2.show()
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
PyQt update gui
self.trigger.connect(Output().main())
This line is problematic. You are instantiating a class in the thread which looks like a widget. This is wrong. You shouldn't use GUI elements in a different thread. All GUI related code should run in the same thread with the event loop.
The above line is also wrong in terms of design. You emit a custom signal from your thread and this is a good way. But the object to process this signal should be the one that owns/creates the thread, namely your MainWindow
You also don't keep a reference to your thread instance. You create it in a method, but it is local. So it'll be garbage collected, you probably would see a warning that it is deleted before it is finished.
Here is a minimal working example:
import sys
from PyQt4 import QtGui, QtCore
import time
import random
class MyThread(QtCore.QThread):
trigger = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyThread, self).__init__(parent)
def setup(self, thread_no):
self.thread_no = thread_no
def run(self):
time.sleep(random.random()*5) # random sleep to imitate working
self.trigger.emit(self.thread_no)
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.text_area = QtGui.QTextBrowser()
self.thread_button = QtGui.QPushButton('Start threads')
self.thread_button.clicked.connect(self.start_threads)
central_widget = QtGui.QWidget()
central_layout = QtGui.QHBoxLayout()
central_layout.addWidget(self.text_area)
central_layout.addWidget(self.thread_button)
central_widget.setLayout(central_layout)
self.setCentralWidget(central_widget)
def start_threads(self):
self.threads = [] # this will keep a reference to threads
for i in range(10):
thread = MyThread(self) # create a thread
thread.trigger.connect(self.update_text) # connect to it's signal
thread.setup(i) # just setting up a parameter
thread.start() # start the thread
self.threads.append(thread) # keep a reference
def update_text(self, thread_no):
self.text_area.append('thread # %d finished' % thread_no)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainwindow = Main()
mainwindow.show()
sys.exit(app.exec_())
PyQt: How to update progress without freezing the GUI?
If you want to use signals to indicate progress to the main thread then you should really be using PyQt's QThread class instead of the Thread class from Python's threading module.
A simple example which uses QThread, signals and slots can be found on the PyQt Wiki:
https://wiki.python.org/moin/PyQt/Threading,_Signals_and_Slots
Related Topics
How to Download Image Using Requests
What Is Memoization and How to Use It in Python
How to Merge Lists into a List of Tuples
How to Write Json Data to a File
Why Is "Except: Pass" a Bad Programming Practice
Why Do Python Classes Inherit Object
How to Convert a .Py to .Exe For Python
Open Web in New Tab Selenium + Python
How to Sort a List of Objects Based on an Attribute of the Objects
All Combinations of a List of Lists
C Function Called from Python Via Ctypes Returns Incorrect Value
Why Is _Init_() Always Called After _New_()
Generating a Png With Matplotlib When Display Is Undefined
Is There a Numpy Function to Return the First Index of Something in an Array
How to Input a Regex in String.Replace
How to Programmatically Set an Attribute
Why Does This Iterative List-Growing Code Give Indexerror: List Assignment Index Out of Range