簡體   English   中英

關閉應用程序時多次出現錯誤“QObject::startTimer: QTimer can only be used with threads started with QThread”

[英]Error “QObject::startTimer: QTimer can only be used with threads started with QThread” many times when closing application

我知道這已經被問過很多次了。 我閱讀了所有這些主題,我的情況似乎有所不同。 其他遇到此問題的人都有一些我認為已排除的直接原因,例如:

  • 在沒有事件循環運行的情況下啟動計時器
  • 從創建計時器的線程以外的線程啟動/停止計時器
  • 未設置widget的parent屬性,導致銷毀順序問題

下面我有一個演示問題的最小代碼示例。 請注意,我沒有啟動任何線程或計時器。 我還設置了每個小部件的父級。 如果我刪除圖形小部件,問題就會消失,所以人們很想責怪 pyQtGraph,但是,如果我包含繪圖小部件但排除所有空白選項卡(即除 tabCatchaTiger 之外的每個選項卡),問題也會消失,並且似乎證明 pyQtGraph 是正確的。

版本:

  • Windows 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在退出時QObject::startTimer: QTimer can only be used with threads started with QThread消息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的中心項在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 這會導致兩個不同的問題:

  1. QTimer 錯誤消息是由 pyqtgraph 在 QApplication 被銷毀后嘗試跟蹤其 ViewBoxes 引起的。

  2. 崩潰似乎是 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()

您可以通過將global app添加到主函數或通過在模塊級別創建QApplication來修復它。

嘗試在 __init__ 塊中寫入:

self.setAttribute(Qt.WA_DeleteOnClose)

就個人而言,我不再努力追趕退出崩潰——只需使用pg.exit()並完成它。

(但如果你碰巧在 pyqtgraph 中發現了一個 bug,請不要猶豫,在 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_())

擺脫整個窗口上的 deleteLater 似乎解決了它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM