2014-11-25 4 views
4

Я знаю, что это было задано много раз раньше. Я прочитал все эти темы, и мое дело кажется другим. Все остальные, кто имеет это проблемы имеет несколько прямолинейные причины, которые я думаю, я исключил, такие как:Ошибка «QObject :: startTimer: QTimer может использоваться только с потоками, запущенными с QThread» много раз при закрытии приложения

  • Начало таймер с не цикл обработки событий не работает
  • Запуск/остановка таймера из потока, отличного тот, который создал таймер
  • Если не установить родительское свойство виджета, что приводит к проблемам с порядком уничтожения

Ниже у меня есть минимальный пример кода, который демонстрирует проблему. Обратите внимание, что я не начал ни потоков, ни таймеров. Я также установил родительский элемент каждого виджета. Если я удалю виджеты графа, проблема исчезнет, ​​поэтому возникает соблазн обвинять pyQtGraph, однако, если я включаю виджеты сюжета, но исключаю все пустые вкладки (т.е. каждую вкладку, кроме tabCatchaTiger), проблема также исчезает, и это кажется, оправдывает pyQtGraph.

Версии:

  • Windows 7
  • Python 2.7.8
  • Wing IDE 5.0.9-1
  • PyQt 4.11.1
  • PyQwt 5.2.1
  • PyQtGraph 0,9 .8

Испытание ca себе:

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() 
+0

Я не совсем понимаю ваше заключение. Проблема возникает, только если вкладка pyqtgraph присутствует, но не показана. Таким образом, похоже, что есть ошибка в инициализации виджета сюжета (я не буду говорить об ошибке, потому что это только извергает предупреждающие сообщения). – ekhumoro

ответ

0

Лично я не ставлю никаких усилий в погоне за аварии выхода больше - просто использовать pg.exit() и сделать с ней.

(но если случится найти ошибку в pyqtgraph, не стесняйтесь открыть вопрос на GitHub)

+0

Это действительно хромой API. Если эти предполагаемые ошибки в PySide/PyQt реальны, почему их не сообщают? – ekhumoro

+0

Некоторые из них были сообщены. Другие были просто сброшены, потому что они требуют слишком много усилий для отслеживания (например, перенаправление приложения на 100 тыс. Строк вплоть до минимальных строк, необходимых для воспроизведения краха выхода, не требует больших усилий). – Luke

+0

Хорошо, но тогда какое оправдание для обвинения PySide/PyQt в документации публичного API? Несомненно, бремя ответственности за авторов pyqtgraph заключается в том, чтобы исключить ошибки в их собственном коде, прежде чем они начнут обвинять других? – ekhumoro

6

Вот лучший ответ:

Вы позволяете QApplication быть собраны до выходы python. Это вызывает два различных вопроса:

  1. Сообщения об ошибках QTimer вызваны pyqtgraph пытается проследить его ViewBoxes после QApplication был разрушен.

  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 на уровне модуля.

+0

@ekhumoro is 2 a (Py) Ошибка Qt или ошибка пользователя? Лично я наклоняюсь к первому .. – Luke

+1

В этом конкретном случае это из-за ошибки в вашем примере. Сцена должна иметь представление, установленное в качестве родителя. – ekhumoro

+2

Я не думаю, что исправление «глобального приложения» также может быть надежным. Когда я пытаюсь использовать его с оригинальным примером OP, он все равно выгружает ядро ​​при выходе. Решение, которое работает для меня, заключается в том, чтобы добавить 'self.setAttribute (QtCore.Qt.WA_DeleteOnClose)' в '__init__' главного окна, хотя я не знаю, является ли это более надежным в целом. Следует, однако, отметить, что эти «исправления» нужны только тогда, когда виджеты сюжета созданы, иначе исходный пример отлично подходит для меня. – ekhumoro

10

В этом примере, по-видимому, есть две тесно связанные проблемы.

Первый вызывает 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 начнет работать.

Однако для того, чтобы это было полностью успешным, все соответствующие объекты должны быть связаны друг с другом в иерархии parent-child. Код примера делает это там, где он может, но, кажется, есть некоторые критические места в инициализации класса 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 сообщений и уходит.

+1

Использование 'WA_DeleteOnClose' работает в моем случае. Благодаря :) –