多次加载 pyqt 应用程序导致分段错误
Loading a pyqt application multiple times cause segmentation fault
我有一个文件 Foo.py
,其中包含以下代码。当我从命令行使用 运行 文件时,python Foo.py
一切正常。但是,如果我使用 python
的 CLI
python
import Foo
Foo.main()
Foo.main()
Foo.main()
第一个调用工作正常,第二个调用带来了所有地狱警告,第一个是
(python:5389): Gtk-CRITICAL **: IA__gtk_container_add: assertion 'GTK_IS_CONTAINER (container)' failed
最后一个导致分段错误。我的代码有什么问题?
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
from PyQt4 import Qt
from PyQt4 import QtGui,QtCore
class Foo (QtGui.QWidget):
def __init__(self,parent=None):
super(Foo,self).__init__()
self.setUI()
self.showMaximized()
def setUI(self):
self.setGeometry(100,100,1150,650)
self.grid = QtGui.QGridLayout()
self.setLayout(self.grid)
#For convininece, I set different ui "concepts" in their own function
self.setInterfaceLine()
self.setMainText()
def setMainText(self):
#the main box, where information is displayed
self.main_label = QtGui.QLabel('Details')
self.main_text = QtGui.QLabel()
self.main_text.setAlignment(QtCore.Qt.AlignTop)
#Reading the welcome message from file
self.main_text.setText('')
self.main_text.setWordWrap(True) #To handle long sentenses
self.grid.addWidget(self.main_text,1,1,25,8)
def setInterfaceLine(self):
#Create the interface section
self.msg_label = QtGui.QLabel('Now Reading:',self)
self.msg_line = QtGui.QLabel('commands',self) #the user message label
self.input_line = QtGui.QLineEdit('',self) #The command line
self.input_line.returnPressed.connect(self.executeCommand)
self.grid.addWidget(self.input_line,26,1,1,10)
self.grid.addWidget(self.msg_label,25,1,1,1)
self.grid.addWidget(self.msg_line,25,2,1,7)
def executeCommand(self):
fullcommand = self.input_line.text() #Get the command
args = fullcommand.split(' ')
if fullcommand =='exit':
self.exit()
def exit(self):
#Exit the program, for now, no confirmation
QtGui.QApplication.quit()
def main():
app = QtGui.QApplication(sys.argv)
foo = Foo(sys.argv)
app.exit(app.exec_())
if __name__ in ['__main__']:
main()
一个进程中必须只有 QApplication
个实例。 GUI 框架没有为多应用模式做好准备。
我实际上无法重现问题,这至少表明代码没有根本性错误。
问题可能是 main
函数 returns 时的一些垃圾回收问题引起的(删除顺序可能无法预测)。
尝试在事件循环退出后放置 del foo
。
我可以在 Python 3 中重现,但不能在 Python 2 中重现。
这是关于垃圾收集和多个 QApplications 的事情。 Qt 不希望在同一个进程中使用多个 QApplications,并且无论您是否每次都创建一个新的,旧的都存在于解释器的某个地方。在您的 main()
方法的第一个 运行 上,您需要创建一个 QApplication
并通过将其存储在某个地方(例如模块全局或 [=36 的属性)来防止它被垃圾收集=] 或在 main()
returns.
时不会被垃圾回收的全局范围内的实例
然后,在随后的 运行 中,您应该访问现有的 QApplication
而不是创建新的。如果您有多个模块可能需要 QApplication
但您不希望它们必须协调,您可以使用 QApplication.instance()
访问现有实例,然后仅在 none 时实例化一个存在。
因此,将您的 main()
方法更改为以下方法:
def main():
global app
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication(sys.argv)
foo = Foo(sys.argv)
app.exit(app.exec_())
奇怪的是,您必须保留引用以确保 QApplication
不会被垃圾回收。因为每个进程应该只有一个,所以我希望它能永远存在,即使你不保留对它的引用。这似乎是 Python 2 中发生的事情。所以理想情况下,上面不需要 global app
行,但它是为了防止这种垃圾收集业务。
关于这个 QApplication
对象的不朽程度,我们有点犹豫不决......它的寿命太长了,你不能每次都使用一个新的,但不是长寿的足以让您每次 运行 重复使用它,而不必通过保留引用来阻止其垃圾收集。这可能是 PyQt 中的错误,它可能应该为我们做参考。
我有一个文件 Foo.py
,其中包含以下代码。当我从命令行使用 运行 文件时,python Foo.py
一切正常。但是,如果我使用 python
python
import Foo
Foo.main()
Foo.main()
Foo.main()
第一个调用工作正常,第二个调用带来了所有地狱警告,第一个是
(python:5389): Gtk-CRITICAL **: IA__gtk_container_add: assertion 'GTK_IS_CONTAINER (container)' failed
最后一个导致分段错误。我的代码有什么问题?
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
from PyQt4 import Qt
from PyQt4 import QtGui,QtCore
class Foo (QtGui.QWidget):
def __init__(self,parent=None):
super(Foo,self).__init__()
self.setUI()
self.showMaximized()
def setUI(self):
self.setGeometry(100,100,1150,650)
self.grid = QtGui.QGridLayout()
self.setLayout(self.grid)
#For convininece, I set different ui "concepts" in their own function
self.setInterfaceLine()
self.setMainText()
def setMainText(self):
#the main box, where information is displayed
self.main_label = QtGui.QLabel('Details')
self.main_text = QtGui.QLabel()
self.main_text.setAlignment(QtCore.Qt.AlignTop)
#Reading the welcome message from file
self.main_text.setText('')
self.main_text.setWordWrap(True) #To handle long sentenses
self.grid.addWidget(self.main_text,1,1,25,8)
def setInterfaceLine(self):
#Create the interface section
self.msg_label = QtGui.QLabel('Now Reading:',self)
self.msg_line = QtGui.QLabel('commands',self) #the user message label
self.input_line = QtGui.QLineEdit('',self) #The command line
self.input_line.returnPressed.connect(self.executeCommand)
self.grid.addWidget(self.input_line,26,1,1,10)
self.grid.addWidget(self.msg_label,25,1,1,1)
self.grid.addWidget(self.msg_line,25,2,1,7)
def executeCommand(self):
fullcommand = self.input_line.text() #Get the command
args = fullcommand.split(' ')
if fullcommand =='exit':
self.exit()
def exit(self):
#Exit the program, for now, no confirmation
QtGui.QApplication.quit()
def main():
app = QtGui.QApplication(sys.argv)
foo = Foo(sys.argv)
app.exit(app.exec_())
if __name__ in ['__main__']:
main()
一个进程中必须只有 QApplication
个实例。 GUI 框架没有为多应用模式做好准备。
我实际上无法重现问题,这至少表明代码没有根本性错误。
问题可能是 main
函数 returns 时的一些垃圾回收问题引起的(删除顺序可能无法预测)。
尝试在事件循环退出后放置 del foo
。
我可以在 Python 3 中重现,但不能在 Python 2 中重现。
这是关于垃圾收集和多个 QApplications 的事情。 Qt 不希望在同一个进程中使用多个 QApplications,并且无论您是否每次都创建一个新的,旧的都存在于解释器的某个地方。在您的 main()
方法的第一个 运行 上,您需要创建一个 QApplication
并通过将其存储在某个地方(例如模块全局或 [=36 的属性)来防止它被垃圾收集=] 或在 main()
returns.
然后,在随后的 运行 中,您应该访问现有的 QApplication
而不是创建新的。如果您有多个模块可能需要 QApplication
但您不希望它们必须协调,您可以使用 QApplication.instance()
访问现有实例,然后仅在 none 时实例化一个存在。
因此,将您的 main()
方法更改为以下方法:
def main():
global app
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication(sys.argv)
foo = Foo(sys.argv)
app.exit(app.exec_())
奇怪的是,您必须保留引用以确保 QApplication
不会被垃圾回收。因为每个进程应该只有一个,所以我希望它能永远存在,即使你不保留对它的引用。这似乎是 Python 2 中发生的事情。所以理想情况下,上面不需要 global app
行,但它是为了防止这种垃圾收集业务。
关于这个 QApplication
对象的不朽程度,我们有点犹豫不决......它的寿命太长了,你不能每次都使用一个新的,但不是长寿的足以让您每次 运行 重复使用它,而不必通过保留引用来阻止其垃圾收集。这可能是 PyQt 中的错误,它可能应该为我们做参考。