PyQt崩溃和线程安全 [英] PyQt crashes and thread safety
问题描述
您好StackExchange社区,
Hello StackExchange community,
首先,非常感谢你们对我的帮助.第一次问题:
first off, you have all been a great help for me, thanks so much. First time question:
我目前正在编写一个PyQt GUI应用程序,我发现它在Windows系统上崩溃,同时它还给我在家中的机器上带来了段错误,而它却可以在工作中的机器上使用(均为Linux Mint 17).经过一番研究,我意识到我可能已经创建了一个线程不安全的GUI,因为我有几个对象互相调用方法.
I am currently writing a PyQt GUI application and I see that it crashes on windows systems, also it gives me a segfault on my machine at home while it works on the one at work (both linux mint 17). After some research, I realize that I have probably created a thread-unsafe GUI because I have several objects that call each others methods.
来自另一个stackoverflow问题: GUI窗口小部件只能从主线程访问,这意味着调用的线程QApplication.exec().从任何其他线程访问GUI窗口小部件(您对self.parent()的调用所做的工作)是未定义的行为,在您的情况下,这意味着崩溃.
来自Qt文档:尽管QObject是可重入的,GUI类(尤其是QWidget及其所有子类)不是可重入的.它们只能在主线程中使用.如前所述,还必须从该线程中调用QCoreApplication :: exec().
最后,我认为我应该只使用信号插槽系统.
So in the end, I figure that I should only use the signal-slot system for doing so.
- 这正确吗?
- 这仅是函数调用所需要的,还是可以在运行时以线程安全的方式操纵其他对象中某些对象的字段?例如,我有一个options对象,可以从多个其他对象访问该对象,并且经常从不同的源那里更改参数.线程安全还是不安全?
接下来的事情是,我在示例代码中重新创建此线程不安全行为时遇到了问题. Qt文档说QObjects存在于不同的线程中.这意味着,以下Qt应用程序应该是线程不安全的(如果我正确理解的话).
The next thing, is that I have a problem recreating this thread-unsafe behaviour in example code. Qt documentation says that QObjects live in different threads. This means, the following Qt application should be thread unsafe (if I got it correctly).
from PyQt4 import QtGui
import sys
class TestWidget(QtGui.QWidget):
def __init__(self,string):
super(TestWidget,self).__init__()
self.button = QtGui.QPushButton(string,parent=self)
self.button.clicked.connect(self.buttonClicked)
# just to check, and yes, lives in it's own thread
print self.thread()
def buttonClicked(self):
# the seemingly problematic line
self.parent().parent().statusBar().showMessage(self.button.text())
pass
pass
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
Layout = QtGui.QHBoxLayout()
for string in ['foo','bar']:
Layout.addWidget(TestWidget(string))
CentralWidget = QtGui.QWidget(self)
CentralWidget.setLayout(Layout)
self.setCentralWidget(CentralWidget)
self.statusBar()
self.show()
pass
pass
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
M = MainWindow()
sys.exit(app.exec_())
但是它也可以在我的我的Windows计算机上运行.
but it runs on my mine as well on windows machines fine.
- 为什么?这实际上是线程不安全的并且可能崩溃,但事实并非如此吗?
感谢您帮助我解决此问题...
Thanks for helping me to sort this out ...
推荐答案
这正确吗?
是的,您应该仅将信号插槽系统用于q对象之间的交互. 这就是它的本意.
Yes, you should only use the signal-slot system for interactions ammong q-objects. This is how it's meant to be.
这仅是函数调用所需要的,还是可以操纵某些对象的字段? 在运行时以线程安全的方式从其他对象访问?
Is this only needed for function calls, or can I manipulate the fields of some objects from other objects at runtime in a thread safe manner?
我有一个options对象,可以从其他多个对象中访问...
如果此处所说的对象是指Q对象:
If by objects here you meant Q-objects:
您的options
对象应该具有对信号插槽机制的支持,您可以实现此目标
从QObject
导出options
.
Your options
object should have support for the signal-slot mechanism, you can achieve this
deriving options
from QObject
.
class Options(QtCore.QObject):
optionUpdated = QtCore.pyqtSignal(object)
def __init__(self):
self.__options = {
'option_1': None
}
def get_option(self, option):
return self.__options.get(option)
def set_option(self, option, value):
self.__options[option] = value
self.optionUpdated.emit(self)
,然后所有使用此选项的小部件/对象都应具有一个插槽,用于连接到此信号.
and then all widgets/objects using this options should have a slot that connect to this singal.
一个简单的例子:
options = Options()
some_widget = SomeWidget()
options.optionUpdated.connect(some_widget.options_updated) // Is like you implement the observer pattern, right?
为什么?这实际上是线程不安全的并且可能崩溃,但事实并非如此吗?
thread-unsafe
并非意味着崩溃已得到保证",而是可能崩溃"或很有可能崩溃".
thread-unsafe
doesn't mean "the crash is guaranteed" but "this might crash" or "there is a high probability for this to crash".
来自pyqt API文档 QObject.thread :
From pyqt API doc QObject.thread :
返回对象所在的线程.
Returns the thread in which the object lives.
勘误表
正如ekumoro所指出的,我已经重新检查了关于每个对象留在不同线程中的先前位置,并且...我错了!
As pointed by ekumoro, I've re-checked my previous position about each object leaving in a different thread, and ... I was wrong!
QObject.thread
将为每个对象返回不同的 QThread实例,但是QThread
实际上不是线程仅仅是操作系统提供的那些线程的包装.
QObject.thread
will return a different QThread instance for each object but QThread
is not actually a thread is just a wrapper for those threads provided by the OS.
因此,在不同线程中划分多个对象时,代码确实没有问题.
So the code don't really have the problem af several object sliving in different threads.
为简单起见,我对用于演示的代码做了一些修改:
from PyQt4 import QtGui
import sys
class TestWidget(QtGui.QWidget):
def __init__(self,string):
super(TestWidget,self).__init__()
# just to check, and yes, lives in it's own thread
print("TestWidget thread: {}".format(self.thread()))
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow,self).__init__()
print("Window thread: {}".format(self.thread()))
Layout = QtGui.QHBoxLayout()
for string in ['foo','bar']:
Layout.addWidget(TestWidget(string))
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
M = MainWindow()
sys.exit(app.exec_())
是的,它会打印:
Window thread: <PyQt4.QtCore.QThread object at 0x00000000025C1048>
TestWidget thread: <PyQt4.QtCore.QThread object at 0x00000000025C4168>
TestWidget thread: <PyQt4.QtCore.QThread object at 0x00000000025C41F8>
降低每个控件的寿命,使其驻留在自己的线程中.
现在,您已经有了信号插槽机制来安全地"处理该线程,任何其他方法都不是线程安全的.
这篇关于PyQt崩溃和线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!