我知道这之前已经被问过很多次了。我阅读了所有这些线程,我的情况似乎有所不同。其他遇到这种麻烦的人都有一些我认为我已经排除的简单原因,例如:
- 启动未运行事件循环的计时器
- 从创建计时器的线程以外的线程启动/停止计时器
- 无法设置小部件的父属性,导致销毁顺序出现问题
下面我有一个演示问题的最小代码示例。请注意,我没有启动任何线程或计时器。我还设置了每个小部件的父级。如果我删除图形小部件,问题就会消失,所以人们很想责怪pyQtGraph,但是,如果我包含绘图小部件但排除所有空白选项卡(即除了tabCatchaTiger之外的每个选项卡),问题也会消失,这似乎证明了pyQtGraph的正确性。
版本:
- 视窗 7
- 蟒蛇 2.7.8
- 翼 IDE 5.0.9-1
- PyQt 4.11.1
- PyQwt 5.2.1
- PyQtGraph 0.9.8
测试用例:
from PyQt4 import Qt, QtGui, QtCore
import PyQt4.Qwt5 as Qwt
import pyqtgraph as pg
pg.functions.USE_WEAVE = False # Lets pyqtgraph plot without gcc
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
# GUI for visualizing data from database
class crashyGUI(QtGui.QWidget) :
def __init__(self) :
# Make the window
QtGui.QWidget.__init__(self)
self.resize(700, QtGui.QDesktopWidget().screenGeometry(self).height()*.85)
self.setWindowTitle('Data Visualization')
# Create tab interface
tabWidget = QtGui.QTabWidget(self)
# define the tab objects
self.tabEeny = QtGui.QWidget(tabWidget)
self.tabMeeny = QtGui.QWidget(tabWidget)
self.tabMiney = QtGui.QWidget(tabWidget)
self.tabMoe = QtGui.QWidget(tabWidget)
self.tabCatchaTiger = QtGui.QWidget(tabWidget)
self.tabByThe = QtGui.QWidget(tabWidget)
self.tabToe = QtGui.QWidget(tabWidget)
# Initialize the tab objects
self.initTabCatchaTiger()
###########################################
############### Main Layout ###############
###########################################
tabWidget.addTab(self.tabEeny, 'Eeny')
tabWidget.addTab(self.tabMeeny, 'Meeny')
tabWidget.addTab(self.tabMiney, 'Miney')
tabWidget.addTab(self.tabMoe, 'Moe')
tabWidget.addTab(self.tabCatchaTiger, 'Catch a Tiger')
tabWidget.addTab(self.tabByThe, 'By The')
tabWidget.addTab(self.tabToe, 'Toe')
self.mainLayout = QtGui.QVBoxLayout(self)
self.mainLayout.addWidget(tabWidget)
self.setLayout(self.mainLayout)
def initTabCatchaTiger(self):
###########################################
############# ADC Capture Tab #############
###########################################
# define tab layout
grid = QtGui.QGridLayout(self.tabCatchaTiger)
# create copy of adc plot and add to row 3 of the grid
self.catchaTigerPlot1 = pg.PlotWidget(name = 'Catch a Tiger 1', parent = self.tabCatchaTiger)
self.catchaTigerPlot1.setTitle('Catch a Tiger 1')
grid.addWidget(self.catchaTigerPlot1, 2, 0, 1, 8)
self.catchaTigerPlot2 = pg.PlotWidget(name = 'Catch a Tiger 2', parent = self.tabCatchaTiger)
self.catchaTigerPlot2.setTitle('Catch a Tiger 2')
grid.addWidget(self.catchaTigerPlot2, 3, 0, 1, 8)
# set layout for tab
self.tabCatchaTiger.setLayout(grid)
def closeEvent(self, event) :
pass
def main() :
# open a QApplication and dialog() GUI
app = QtGui.QApplication([])
windowCrashy = crashyGUI()
windowCrashy.show()
app.exec_()
main()
示例中似乎有两个密切相关的问题。
第一个导致Qt在退出时打印QObject::startTimer: QTimer can only be used with threads started with QThread
消息。
第二个(可能不会影响所有用户)导致Qt打印QPixmap: Must construct a QApplication before a QPaintDevice
,然后在退出时转储核心。
这两个问题都是由 python 在退出时以不可预测的顺序删除对象引起的。
在此示例中,可以通过将以下行添加到顶级窗口的__init__
来修复第二个问题:
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
除非QApplication.setQuitOnLastWindowClosed已更改为False
,否则这将确保应用程序在正确的时间退出,并且Qt有机会在python垃圾收集器开始工作之前自动删除顶级窗口的所有子窗口。
但是,要使此操作完全成功,必须在父子层次结构中将所有相关对象链接在一起。示例代码在可能的情况下执行此操作,但在 PlotWidget
类的初始化中似乎有一些关键位置没有完成。
特别是,没有什么可以确保PlotWidget
的中心项在创建时具有父集。如果代码的相关部分更改为:
class PlotWidget(GraphicsView):
...
def __init__(self, parent=None, background='default', **kargs):
GraphicsView.__init__(self, parent, background=background)
...
self.plotItem = PlotItem(**kargs)
# make sure the item gets a parent
self.plotItem.setParent(self)
self.setCentralItem(self.plotItem)
然后QTimer
消息的第一个问题也消失了。
这是一个更好的答案:
您允许在 python 退出之前收集QApplication
。这会导致两个不同的问题:
-
QTimer 错误消息是由 pyqtgraph 在 QApplication 被销毁后试图跟踪其 ViewBox 引起的。
-
崩溃似乎是Qt/PyQt固有的。以下以相同的方式崩溃:
from PyQt4 import Qt, QtGui, QtCore def main() : app = QtGui.QApplication([]) x = QtGui.QGraphicsView() s = QtGui.QGraphicsScene() x.setScene(s) x.show() app.exec_() main()
您可以通过向 main 函数添加global app
或在模块级别创建QApplication
来修复它。
尝试在__init__块中编写以下内容:
self.setAttribute(Qt.WA_DeleteOnClose)
就我个人而言,我不再花任何精力去追逐退出崩溃——只需使用pg.exit()
并完成它。
(但是如果你碰巧在 Pyqtgraph 中发现了一个错误,请不要犹豫,在 GitHub 上打开一个问题)
我也发生了这种情况,就我而言,这是由对应用程序的aboutToQuit
-Signal deleteLater()
调用引起的,如下所示:
def closeEvent(self, *args, **kwargs):
self.deleteLater()
if __name__ == "__main__":
application = QtWidgets.QApplication(sys.argv)
window = testApplication()
# Handle application exit
application.aboutToQuit.connect(window.closeEvent)
# System exit
sys.exit(application.exec_())
摆脱删除后来整个窗口似乎解决了它。