PySide - QThread running but not entering slot connected to signal 'started' - python

I thought I've read it all about threading in Qt, but apparently I'm getting it wrong. I'm stuck with this stuff for some time now, so I would really appreciate your help a lot.
So, I created a class that starts a thread in its __init__ method:
class MyClass(object):
def __init__(self):
(...)
self.thread = QtCore.QThread(parent)
worker = Worker()
QtCore.QObject.connect(self.thread, QtCore.SIGNAL('started()'), worker,
QtCore.SLOT('doWork()'))
QtCore.QObject.connect(worker, QtCore.SIGNAL('finished()'), self.thread,
QtCore.SLOT('quit()'))
QtCore.QObject.connect(worker, QtCore.SIGNAL('finished()'), worker,
QtCore.SLOT('deleteLater()'))
QtCore.QObject.connect(self.thread, QtCore.SIGNAL('finished()'),
self.thread, QtCore.SLOT('deleteLater()'))
worker.moveToThread(self.thread)
self.thread.start()
Worker class looks like that:
class Worker(QtCore.QObject):
(some signals)
def doWork(self):
print "doing my work"
The problem is that my doWork slot is never executed, although the thread is running.
After creating instance of MyClass, let's say:
obj = MyClass()
I can call:
obj.thread.isRunning()
which returns True.
I assume that signal 'started' is not emitted, because of instantly exiting the method where thread was created (when I add sleep after starting thread, it enters doWork slot). I wonder how such situation should be handled properly.
Please let me know if I did not explain my problem clearly.
Thank you all for help in advance.

Your worker is being garbage collected after the MyClass constructor returns. Make worker a member of MyClass so that it persists beyond the constructor.
self.worker = Worker()

Related

Slot method called in the wrong thread (pyqt)

This class suppose to handle a long task in a separate thread.
Despite moving my object to a new thread, and using queued connection, the signal is handled at the main gui thread instead of self.thread. The main gui thread is the thread that did called startit of course..
This is the code:
class DoLongProcess(QObject):
def __init__(self,task):
QObject.__init__(self)
self._task=task
self._realtask=None
self.thread = QThread()
self.moveToThread(self.thread)
self.thread.started.connect(self.run,Qt.QueuedConnection)
#Slot()
def run(self):
self._realtask()
def startit(self,*params):
self._realtask= partial(self._task,*params)
self.thread.start()
I use pyside6. I tried to to split __init__ into two methods and moving the thread from outside. Also tried without the QueuedConnection attribute (which suppose to do this exactly). None of this had an effect.
Do you know why it doesn't work as expected?

How come the main thread is blocked if .connect takes a lambda function?

I'm trying to let a button spin up a new thread that does nothing but sleep for 30 seconds. However, the main thread is blocked if the slot is a lambda function. Does anyone know why this is the case and not behaving what I was expecting it to be? Here's my code:
# ...
def setup(self):
# ...
self.pushButton_TestConnection.clicked.connect(self.process)
def process(self):
self.worker_thread = QtCore.QThread()
self.worker = Worker()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.started.connect(lambda: self.worker.sleep(30))
self.worker_thread.start()
class Worker(QtCore.QObject):
def sleep(self, secs):
time.sleep(secs)
It works fine with the following
self.worker_thread.started.connect(self.worker.sleep)
self.worker_thread.start()
class Worker(QtCore.QObject):
def sleep(self):
time.sleep(30)
Thanks
In Qt the thread in which code is executed is determined by the thread affinity of the object receiving a signal. If you call a objects method directly from a different thread, it will be executed in the calling thread, no matter the thread affinity of the called object.
Lambdas and other python callables do not have a thread affinity (they're not QObjects after all, it's just a nice feature of PyQt allowing you to connect a signal to any python callable), so they will always be executed in the main (GUI) thread.
So in this case the lambda is executed in the GUI thread, so the worker.sleep call will also be executed there and will block it until the call returns.
To make this work, you need to connect the started signal directly to a slot of the Worker object, or communicate with the Worker using a signal you emit from the lambda.

Concurrent execution of QThread or QRunnable objects in Python

I have the following code:
from PySide.QtCore import *
import time
class GUI(object):
IDLIST = [i for i in xrange(20)]
UNUSEDIDS = [i for i in xrange(20)]
def __init__(self):
print "GUI CLASS INITIALIZED!"
worker = Worker()
worker2 = Worker2()
threadpool = QThreadPool()
threadpool.setMaxThreadCount(10)
for i in xrange(5):
#Alternate between the two
#threadpool.start(worker)
#worker2.start()
#classmethod
def delegator(self):
"""Irrelevant to the question, I need this method for something else"""
USEDIDS = []
toUse = self.UNUSEDIDS[0]
USEDIDS.append(toUse)
self.UNUSEDIDS.pop(0)
return toUse
class Worker(QRunnable):
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
def run(self):
#idInUse = getattr(GUI, "delegator")
idInUse = GUI.delegator()
print "Hello world from QRunnable", idInUse
#time.sleep(5)
class Worker2(QThread):
def __init__(self, parent=None):
super(Worker2, self).__init__(parent)
def run(self):
idInUse = GUI.delegator()
print "Hello world from QThread", idInUse
s = time.time()
GUI()
print "Done in %s" % ((time.time()-s) * 1000)
I think the desired effect is obvious from the code. I want the "Hello world from QThread/QRunnable " to be shown. Since I am writing a multi-threaded application, in my GUI __init__ part I have the loop that starts concurrent threads.
The thing is that, with QRunnable it works just fine. All the 5 threads I specified get executed at once, concurrently. With QThread, however, that is not the case. Instead, I get the following error:
QThread: Destroyed while thread is still running
And it is not executed at all.
Normally I would not at all mind using the QRunnable, however, it does not derive from QObject (so I can't directly emit signals though I can construct a QObject() within it) and also it does not have the .stop() method which I badly need. Googling revealed that there is no way to stop a QRunnable from executing? On the other hand, QThread has both of these methods that I need.
So I guess my question is either how to make multiple same QThreads run concurrently, or how to terminate an execution of a QRunnable?
(also please bear in mind that the python's built-in threading module is out of the question)
The QThread: Destroyed while thread is still running-exception happens because you never wait for your threads to finish, and you don't keep any reference to them (neither to worker, worker2 or threadpool, so when your __init__ finishes it gets destroyed.
if you keep a reference to this objects, then is should work:
def __init__(self):
print "GUI CLASS INITIALIZED!"
self.worker = Worker()
self.worker2 = Worker2()
self.threadpool = QThreadPool()
self.threadpool.setMaxThreadCount(10)
for i in xrange(5):
#Alternate between the two
self.threadpool.start(worker)
# this is wrong, by the way!
# you should create 5 workers, not call start 5 times...
self.worker2.start()
and calling the wait/waitForDone methods on the thread/pool is even better.
For a QThreadPool this implicily happens when it's (C++) destructor is called. If that wasn't the case, then your program wouldn't have worked with QRunnables in the first place eiter. For the QThread nothing like this happens and it's even mentioned that it will probably result in a crash. So it's better to explicitly wait for the threads to finish...
also, i hope you already know this

Deleting a threaded timer class?

Imagine I have this code (not written the actual timer yet):
class Timer(threading.Thread):
def __init__(self, seconds):
self.runTime = seconds
threading.Thread.__init__(self)
def run(self):
time.sleep(self.runTime)
#do some other function
print 'Finished'
t = Timer(60)
t.start()
Once the run() method has finished running, is there some way to stop the thread, and delete the class instance?
The run() method is everything that runs in a thread, so when it has finished nothing is running anymore. Then t.is_alive() will return False. You can then delete the instance using del t, but that will just remove your reference to it. The actual deleting will be done by garbage collector some time later.

Python: Explanation of example... Why does this work?

I've been mucking around with python for a little while and I have recently come up with something involving multithreading... without further ado... heres what I have...
import pythoncom
import wmi
import threading
class Info(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
pythoncom.CoInitialize()
c = wmi.WMI()
detect = c.Win32_ComputerShutdownEvent.watch_for()
detect()
return
if __name__ == '__main__':
Info().start()
for process in c.Win32_Process(Name="something.exe"):
result = process.Terminate()
So my question is... Why does this work? It may be an overall question regarding the process of the inheritence of threading.Thread... but there is no start() def in the class Info() so why does the run def begin?
This is actually a pretty handy application I needed to use to stop an application that always seems to hang when windows shuts down... finding when the windows shutdown event happens was a bit of a headache but luckily tim golden's masterpiece saves the day!
Because it's defined in the parent. Parent classes are checked for attributes if they're not found (or handled) in the child class.
Subclasses of Thread automatically call their run(), when you call start() on them. Start is defined in Thread.
From the docs
There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass.
and from docs on start()
It arranges for the object’s run() method to be invoked in a separate thread of control.
Don't you intent to wait the thread has ended before killing process ?
If so:
if __name__ == '__main__':
info = Info()
info.start()
info.join()
for process in c.Win32_Process(Name="something.exe"):
result = process.Terminate()

Categories

Resources