PyQtGraph PlotWidget在关闭窗口崩溃应用程序

问题描述 投票:3回答:1

我正在尝试构建一个桌面应用程序,它会在到达时绘制一些流数据。我希望能够打开多个窗口来监控不同的流。

在我开始关闭图表窗口之前,它工作得非常好。

我遇到的问题是关闭我的一个图表窗口经常导致所有窗口关闭,应用程序退出。终端中不显示任何错误消息,所有窗口都关闭,程序终止。关闭图表窗口应该只关闭图表窗口。

我在Windows 10上使用PyQt5,PyQtGraph 0.10.0和Python 3.6.1。

下面的代码显示了我的应用程序的结构。

  • App类跟踪打开的窗口并存储图表数据。 (实际上,这个类收集流数据并调用相关打开窗口的更新例程。)
  • ButtonWindow类可用于创建新的图表窗口。 (实际上,有许多按钮取决于用户希望绘制的图表。)
  • ChartWindow类显示数据。 import PyQt5.QtWidgets as qt import pyqtgraph as pg class App(qt.QApplication): def __init__(self,args): qt.QApplication.__init__(self,args) #window tracking self.last_idx = 0 self.windows = {} #chart data self.x = [1,2,3,4,5] self.y = [1,2,3,4,5] #create button window self.button_window = ButtonWindow(self) #enter event loop self.exec_() def new_window(cls): cls.windows[cls.last_idx] = ChartWindow(cls, cls.last_idx) cls.last_idx += 1 def close_window(cls, window_id): cls.windows[window_id].destroy() del cls.windows[window_id] class ButtonWindow(qt.QWidget): def __init__(self, app): qt.QWidget.__init__(self) self.grid = qt.QGridLayout() self.app = app #add a button self.btn = qt.QPushButton('+1 Chart Window') self.btn.clicked.connect(self.app.new_window) self.grid.addWidget(self.btn,0,0) self.setLayout(self.grid) #show window self.show() class ChartWindow(qt.QWidget): def __init__(self, app, window_id): qt.QWidget.__init__(self) self.grid = qt.QGridLayout() self.app = app self.window_id = window_id self.setWindowTitle('Chart Window '+str(self.window_id)) #add a chart self.chart = pg.PlotWidget() self.chart.plot(app.x,app.y) self.grid.addWidget(self.chart,0,0) self.setLayout(self.grid) #show window self.show() def closeEvent(cls,event): #cls.chart.close() cls.app.close_window(cls.window_id) def main(): app = App([]) if __name__ == '__main__': main()

阅读后我认为这是由于Python垃圾收集器与对底层c ++对象的幸存引用之间的一些冲突。

  1. https://github.com/pyqtgraph/pyqtgraph/issues/55
  2. parent for widgets - nescessary?

问题肯定与PlotWidget有关。如果为按钮交换绘图窗口小部件,则不会观察到崩溃。

我有想法在1的closeEvent覆盖中添加'cls.chart.close()'行。这有帮助。崩溃变得不那么频繁,但是在20左右的图表窗口关闭后,它仍然会发生。

我也尝试将所有小部件都放在它们所在窗口的子窗口中,但这没有任何效果。

有任何想法吗?我不敢相信一些简单的东西,比如打开和关闭一个包含情节的窗口就足以炸掉PyQt所以我假设我在我的结构中做了一些愚蠢的事情。

python pyqt pyqt5 pyqtgraph
1个回答
1
投票

而不是从closeEvent调用一个自我消除的close_window,换句话说,你试图消除同一窗口内的窗口,这就是问题,在Qt中你必须使用信号来通知变化,在这种情况下,实现带有已关闭指数的已结算信号。

from PyQt5 import QtCore, QtWidgets
import pyqtgraph as pg

class App(QtWidgets.QApplication):
    def __init__(self, args):
        super(App, self).__init__(args)
        #window tracking
        self.last_idx = 0
        self.windows = {}

        #chart data
        self.x = [1,2,3,4,5]
        self.y = [1,2,3,4,5]

        #create button window
        self.button_window = ButtonWindow()

        #enter event loop
        self.exec_()

    @QtCore.pyqtSlot()
    def new_window(self):
        window = ChartWindow(self, self.last_idx)
        window.closed.connect(self.remove_window)
        self.windows[self.last_idx] = window
        self.last_idx += 1

    @QtCore.pyqtSlot(int)
    def remove_window(self, idx):
        w = self.windows[idx]
        w.deleteLater()
        del self.windows[idx]
        print(self.windows)

class ButtonWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ButtonWindow, self).__init__(parent)
        grid = QtWidgets.QGridLayout(self)
        self.btn = QtWidgets.QPushButton('+1 Chart Window')
        self.btn.clicked.connect(QtWidgets.QApplication.instance().new_window)
        grid.addWidget(self.btn, 0, 0)
        self.show()

class ChartWindow(QtWidgets.QWidget):
    closed = QtCore.pyqtSignal(int)

    def __init__(self, app, window_id):
        super(ChartWindow, self).__init__()
        grid = QtWidgets.QGridLayout(self)
        self.window_id = window_id
        self.setWindowTitle('Chart Window '+str(self.window_id))
        #add a chart
        self.chart = pg.PlotWidget()
        self.chart.plot(app.x,app.y)
        grid.addWidget(self.chart,0,0)
        #show window
        self.show()

    def closeEvent(self, event):
        self.closed.emit(self.window_id)
        super(ChartWindow, self).closeEvent(event)

def main():
    app = App([])
if __name__ == '__main__':
    main()
© www.soinside.com 2019 - 2024. All rights reserved.