Python PyQt:可以在非GUI程序中使用QThread吗?

1 投票
1 回答
3353 浏览
提问于 2025-04-18 17:50

我有一个用Python和PyQt做的应用程序,它显示了一个简单的用户界面。当用户在界面上点击一个按钮时,就会启动一个QThread(线程)。使用线程的好处是可以防止用户界面在线程运行时“卡住”。我通过发出信号来传递信息,从运行的线程回到用户界面,以便更新状态和表示完成。整个过程都很顺利,我还为我的用户界面创建了一个简单的类,这个类负责创建线程并运行我的通用处理程序。

不过,我还想做一个没有图形界面的命令行版本的程序,并且想使用同样的处理QThread类。但是,当我尝试连接我的信号时,出现了以下错误。看起来QThread似乎只适用于图形用户界面程序?

AttributeError: MyClass instance has no attribute 'connect'

在没有图形界面的程序中使用QThread是否可能?

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 个回答

7

问题不在于QThread,而是你在一个没有这个方法的类里调用了connect。你需要让MyClass继承自QObject。

在图形界面中,这样做是有效的,因为你使用的任何小部件(比如QDialog、QMainWindow、QWidget等)都是直接或间接继承自QObject的。

要让MyClass继承自QObject,你只需要:

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... 

我还建议你使用新风格的信号和槽支持

一切都正常,除了processdone信号被调用,但似乎从未触发对thread1done的调用。

我能发现的问题是你没有定义processdone信号。这个信号在Qt中并不存在。查看我给你的链接,了解如何创建自定义信号。与此同时,你可以添加:

class MyProcess(QThread):
    processdone = QtCore.pyqtSignal("QString")

在类的开头。

还有最后一件事,非常重要。你虽然不在处理图形界面,但仍然在使用QObject和Qt的信号机制,这些都依赖于Qt的主循环才能工作。因此,尽管你的应用程序是非图形界面的,你仍然需要一个QApplication对象。

这是你的代码,现在可以正常工作了:

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

撰写回答