Solved Easy to read 60 lines of code, minimal threading example, shows blank widget! 😊
-
from PyQt5.QtCore import QObject, QThread, pyqtSignal from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QPushButton, QPlainTextEdit, QApplication, QGridLayout) import sys class Thread1(QThread): statusMessage = pyqtSignal(str) def __init__(self): super().__init__() def run(self): while self._running: self.statusMessage.emit('Doing the work of thread 1.') self.sleep(1) def startRunning(self): self._running = True self.start() class Thread2(QThread): statusMessage = pyqtSignal(str) def __init__(self): super().__init__() def run(self): while self._running: self.statusMessage.emit('Doing the work of thread 2.') self.sleep(2) def startRunning(self): self._running = True self.start() class Widget(QWidget): def __int__(self): super().__init__() self.textEdit = QPlainTextEdit(parent=self) self.button = QPushButton('click me') lyt = QVBoxLayout(parent=self) self.setLayout(lyt) lyt.addWidget(self.button) lty.addWidget(self.textEdit) self.button.clicked.connect(self.runTwoThreads) def runTwoThreads(self): self.thread1 = Thread1() self.thread1.statusMessage.connect(self.showStatusMessage) self.thread2 = Thread2() self.thread2.statusMessage.connect(self.showStatusMessage) self.thread1.startRunning() self.thread2.startRunning() def showStatusMessage(self, msg): self.textEdit.appendPlainText(msg) if __name__ == '__main__': app = QApplication([]) window = Widget() window.show() sys.exit(app.exec_())
Thanks for showing me why the widget is blank. I've also tried a QMainWindow. I must be doing something really dumb....
-
Hi,
What are these startRunning methods ?
PyQt's QThread documentatoin doesn't show them at all. The start method should be used.
My guess is that your threads run method are never called. -
Sadly, that is not the issue. The way python works, is I won't encounter a semantic issue until that code gets run, but that code never sees the light of day because there's no button to click and run it.
I will update the code. Thank you!
-
Wait, are you running Python with a GIL (standard python)?
If this is CPython (standard python) you will never have true multithreading. Everything depends upon the GIL and everything has to take turns accessing it. There are versions of Python that have taken the GIL out. If I remember right IronPython did this.I have created multithreaded python apps and they run fine, but realize there are limitations.
https://realpython.com/python-gil/ -
Sorry, I misunderstood your question.
Anyway, it's because of typos. It's
__init__
, you wrote it wrong for your QWidget subclass. -
Knowing that, what do I do? The widget is blank for one. That's unrelated to the threading. But you've seen my next question.
So for that, why can't I have two running threads? I'm not doing anything high-frequency. It's all > 1ms loop sleeps.
I did it once for a "record mode" of my app, and that works fine. Then I do it with another button toggling the "playback" mode, and only one thread will run at a time:
I have 3 thread types A, B, C, all subclassed from D.
A,B work together 1 minute,
click another button and A, C don't run together, all in the same fashion and nature as the first pair!Thanks.
-
You may consider this library if you want true parallelism in Python:
https://docs.python.org/2/library/multiprocessing.html
Available for both 2.7 and 3.x -
Okay, here is the updated code, thanks @SGaist .
from PyQt5.QtCore import QObject, QThread, pyqtSignal from PyQt5.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QPushButton, QPlainTextEdit, QApplication, QGridLayout) import sys class Thread1(QThread): statusMessage = pyqtSignal(str) def __init__(self): super().__init__() def run(self): while self._running: self.statusMessage.emit('Doing the work of thread 1.') self.sleep(1) def startRunning(self): self._running = True self.start() class Thread2(QThread): statusMessage = pyqtSignal(str) def __init__(self): super().__init__() def run(self): while self._running: self.statusMessage.emit('Doing the work of thread 2.') self.sleep(2) def startRunning(self): self._running = True self.start() class Widget(QWidget): def __init__(self): super().__init__() self.textEdit = QPlainTextEdit(parent=self) self.button = QPushButton('click me') lyt = QVBoxLayout() self.setLayout(lyt) lyt.addWidget(self.button) lyt.addWidget(self.textEdit) self.button.clicked.connect(self.runTwoThreads) def runTwoThreads(self): self.thread1 = Thread1() self.thread1.statusMessage.connect(self.showStatusMessage) self.thread2 = Thread2() self.thread2.statusMessage.connect(self.showStatusMessage) self.thread1.startRunning() self.thread2.startRunning() def showStatusMessage(self, msg): self.textEdit.appendPlainText(msg) if __name__ == '__main__': app = QApplication([]) window = Widget() window.show() sys.exit(app.exec_())
And it demonstrates the bug I'm seeing at a higher level with my app. For me, when I click the button, only thread two is running, and thread 1 never emits a message, or if you put a breakpoint in thread1's run() method, it will not be hit.
Thanks all for your help.
-
What version of PyQt5 and Qt are you using ?
On what OS ?I have both thread outputs here.
-
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>>
Name: PyQt5 Version: 5.12.1 Summary: Python bindings for the Qt cross platform UI and application toolkit Home-page: https://www.riverbankcomputing.com/software/pyqt/ Author: Riverbank Computing Limited Author-email: info@riverbankcomputing.com License: GPL v3 Location: c:\python36-32\lib\site-packages Requires: PyQt5-sip Required-by: QScintilla, PyQtChart
-
I know I'm running 32-bit python because it's installed to C:\Python36-32 and that's my convention. I think I updated everything a few days ago in order to look at a pyinstaller issue.
-
What are your versions, maybe I should try those out?
-
Just tested with the latest version available on macOS through pip and it worked as expected.
-
This post is deleted! -
I'm trying out Python 3.7 now, but maybe PyQt5 won't even install, so... I'll try some things out. Any ideas? Should I just combine the work of the two threads into one thread, since I do have exactly the same loop sleep set of 1 ms (the practical min).
I'm testing 3.7.3 both 32 and 64-bit.
-
I tried 3.7.3 64-bit and the latest PyQt5 did install. However, the minimal app is having the exact same issue on my system.
So I will just combine the different threads all into one, and switch between the cases.
-
Might be a silly test, but did you try using the QThread method to start your thread ? As I wrote earlier, I don't know how startRunning is implemented.
-
@SGaist said in Easy to read 60 lines of code, minimal threading example, shows blank widget! 😊:
Might be a silly test, but did you try using the QThread method to start your thread ? As I wrote earlier, I don't know how startRunning is implemented.
Nope, that had no effect.