Issue
I'm trying to learn Python and PyQt and, while it's mostly going great, I'm having a HUGE issue with PyQt threading. I've gotten this to work nicely using the Python "threading" module, though I get Seg Fault errors (with no additional info) on occassion.
I want to learn how to properly do threading with PyQt. I've read "Your doing it wrong", "You weren't doing it so wrong", Maya Posch's post, the (lame) PyQt4 and (slightly less lame) PyQt5 documentation, and tons of tutorials and examples on how to do it (including lots from Stack-O) and I've only gotten more and more confused.
I've been able to get threads to start a Worker and do its thing and return data. I can exit, emit, etc., with no problems, but I can't pass any args into the Worker. I'm a fairly intelligent guy, but this makes me feel like a complete idiot! Each time I read something new and try it, I feel like I'm trying to force a square peg into a round hole.
I am using the "moveToThread" feature and not sub-classing the Thread, which, according to most posts I've read, is the correct way to do it (though most examples show it as sub-classed).
If you can help me with this then I'll need more than "just send it when you do blah" - I'll need a real example or something explicit. I HAVE seen someone come close to answering this question, but they were incredibly vague and Stack-O requires that I have 22 more reputation points in order for me to comment or ask a question on someone else's post ... ~ sigh
I have a stub script that mimics my larger script. Here is the relevant code from the stub:
def pre_Flight_Check():
#count = 0 # I want to pass THIS to the Worker at Worker's start
fire_Up_Thread(count)
#=====================
def butStop_click():
# Stop Loop/Thread:
flagQuit = flagQuit + 1
#=====================
# Worker:
class Worker(QObject):
finished = pyqtSignal()
intCount = pyqtSignal(int)
@pyqtSlot()
def looper(self):
flagQuit = 0
count = 0 # I'd like to pass this into the Worker, not define it here
while (count < 5):
self.intCount.emit(count)
time.sleep(1)
count = count + 1
if flagQuit != 0:
break
self.finished.emit()
#========================
objWorker = Worker()
thrWorker = QThread()
#========================
# Threading stuffs:
def fire_Up_Thread(count):
# NOTE: I CAN pass the "count" var into here, just not to the Worker
objWorker.moveToThread(thrWorker)
thrWorker.started.connect(objWorker.looper)
objWorker.intCount.connect(updateTextBox)
objWorker.finished.connect(thrWorker.quit)
objWorker.finished.connect(work_finished)
# Start the thread:
thrWorker.start()
FYI: This is the implementation using Python Threads - it's very simple and straight-forward:
# Loop
def looper(count):
flagQuit = 0
while (count < 5):
print(count)
txtBox1.setText(str(count))
time.sleep(1)
count = count + 1
# User clicked "Stop" button
if flagQuit != 0:
break
work_finished()
#========================
def pre_Flight_Check():
# Var(s) to send to thread:
count = 0
# Start Loop/Thread
loopTest = threading.Thread(target=looper, args=(count,))
loopTest.setDaemon(True)
loopTest.start()
Solution
Using "partial" from the "functools" module allows a variable to be seen as a callable, which can then be used to pass the args on to the Worker. Here's the original line:
thrWorker.started.connect(objWorker.looper)
and the modified line:
thrWorker.started.connect(partial(objWorker.looper,count,strIs))
And now for the entire stub re-written to incorporate the change:
def pre_Flight_Check():
count = 0
strIs = "is: " # NOTE: this is only here to show that more than 1 variable can be included
fire_Up_Thread(count,strIs) # 2 vars to pass
#=====================
def butStop_click():
flagQuit = flagQuit + 1
#=====================
# Worker:
class Worker(QObject):
finished = pyqtSignal()
intCount = pyqtSignal(int)
@pyqtSlot()
def looper(self,count,strIs): # Both vars - "count" and "strIs"
flagQuit = 0
while (count < 5):
strCount = "The count " + strIs + str(count)
self.intCount.emit(count)
time.sleep(1)
count = count + 1
# User clicked "Stop" button
if flagQuit != 0:
break
self.finished.emit()
#========================
objWorker = Worker()
thrWorker = QThread()
#========================
# Threading stuffs:
def fire_Up_Thread(count,strIs): # Passed in from "pre_Flight_Check"
from functools import partial
objWorker.moveToThread(thrWorker)
thrWorker.started.connect(partial(objWorker.looper,count,strIs)) # All vars are appended to connected target
objWorker.intCount.connect(updateTextBox)
objWorker.finished.connect(thrWorker.quit)
objWorker.finished.connect(work_finished)
# Start the thread:
thrWorker.start()
This works, it's thread-safe, it's quick and costs very little in terms of processing.
Answered By - user7207540
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.