Python PyQt: Is it possible to use QThread with a

2019-06-05 06:16发布

问题:

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()

回答1:

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())