I have a Python PyQt application that displays a simple UI.
When the user clicks a button in the UI it triggers a QThread.
The use of a thread prevents the UI from "freezing" while the thread runs.
I emit signals to pass information from the run thread back to the UI for status updates and to indicate completion. Everything works fine as described and I've created a simple class for my UI to call which creates the thread and runs my generic processing.
However, I would also like to create a command line version (No GUI) of my program and use the same processing QThread class. However, I get the following error when I try to connect my signals. It would seem that QThread was only meant for GUI programs?
AttributeError: MyClass instance has no attribute 'connect'
Is it possible to use QThread with a non-GUI program?
from PyQt4 import QtCore
from PyQt4.QtCore import *
#======================================
class MyProcess(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.quit()
self.wait()
def run(self):
print "do time intensive process here"
self.emit( SIGNAL('processdone'), "emitting signal processdone")
return
#======================================
class MyClass(QObject):
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
thread1 = MyProcess() # uses QThread and emits signal 'processdone'
self.connect( thread1, SIGNAL("processdone"), self.thread1done)
thread1.start()
def thread1done(self):
print "done"
#======================================
if __name__ == "__main__":
MyClass()
The problem isn't QThread, the problem is that you're calling the connect
method from a class which doesn't have it. You need to make MyClass
inherits from QObject.
In the GUI this works because whatever widget you're working with (QDialog, QMainWindow, QWidget ...) it inherits (directly or indirectly) from QObject.
To make MyClass
inherit from QObject
you only have to:
class MyClass(QObject): # Specify the class your are specializing.
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
# And countinue your code here...
I also recommend you to use the New-style Signal and Slot Support.
Everything works except the processdone
signal is called but apparently it never triggers call to thread1done.
The problem I can spot is you don't have defined a processdone signal. That signal does not exist for Qt. Check the link I left you to learn about custom signals. Meanwhile you can add:
class MyProcess(QThread):
processdone = QtCore.pyqtSignal("QString")
at the beginning of the class.
And one last thing, but very important. You aren't working with GUI, but you are still using QObjects and the Qt singal mechanisms, which depends on the main Qt loop to work. Hence, you still need a QApplication
object despite of your application is a non-gui program.
Here is your code, now working:
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtCore import *
class MyProcess(QThread):
processdone = QtCore.pyqtSignal("QString") # Define custom signal.
def __init__(self, parent = None):
QThread.__init__(self, parent)
def run(self):
print("do time intensive process here")
self.emit( SIGNAL('processdone'), "emitting signal processdone")
return
class MyClass(QObject):
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
thread1 = MyProcess(self)
self.connect( thread1, SIGNAL("processdone"), self.thread1done)
thread1.start()
@QtCore.pyqtSlot("QString") # Tell Python this is a QTSLOT an receives a string
def thread1done(self, text):
print(text) # Print the text from the signal.
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv) # You still need a QApplication object.
a = MyClass()
sys.exit(app.exec())