QThreading PyQtGraph PlotWidgets в PyQt4

В качестве продолжения вопроса я решил здесь несколько дней назад у меня появился графический интерфейс PyQt4, в который встроены два PyQtGraph PlotWidgets, каждый из которых устанавливает/обновляет данные из многопоточного массива со случайным добавлением, запускаемого кнопкой. Он прекрасно работает, а графический интерфейс отзывчив. Но наступает момент, когда графики перестают отображать обновления внутри соответствующих PlotWidget, и мне приходится сворачивать/разворачивать, чтобы увидеть обновления, нарисованные внутри PlotWidget. Код выглядит так:

import random
import sys

import pyqtgraph as pg
import time
from PyQt4 import QtGui, QtCore


class MyThread(QtCore.QThread):
    def __init__(self, curve, parent=None):
        super(MyThread, self).__init__(parent=parent)
        self.curve = curve
        self.data = [0]
        app.processEvents()#increases the time the drawings are shown

    def run(self):
        app.processEvents() #increases the time the drawings are shown
        while True:
            self.data.append(self.data[-1] + 0.2 * (0.5 - random.random()))
            self.curve.setData(self.data, downsample=10) #crashes without
            time.sleep(0.1)


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)
        self.button.clicked.connect(self.plotter)
        app.processEvents()

    def plotter(self):
        self.curve = self.plot.getPlotItem().plot()
        myThread = MyThread(self.curve, self)
        myThread.start()


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.centralwidget = QtGui.QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.login_widget_1 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_1)
        self.login_widget_2 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_2)
        self.setCentralWidget(self.centralwidget)


if __name__ == '__main__':
    global app
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()    
    sys.exit(app.exec_())

Любая подсказка или обходной путь для того, что я могу сделать, чтобы не максимизировать/минимизировать, чтобы получать обновления в графическом интерфейсе, сделают меня невероятно счастливым.

Следуя совету @ImportanceOfBeingErnest в ответе ниже, я попытался следовать Python Wiki связать GUI и поток с сигналами и слотами как:

import random
import sys
import pyqtgraph as pg
import time
from PyQt4 import QtGui, QtCore


class MyThread(QtCore.QThread):
    def __init__(self, parent=None):
        super(MyThread, self).__init__(parent=parent)
        self.data = [0]

    def run(self):
        while True:
            self.data.append(self.data[-1] + 0.2 * (0.5 - random.random()))
            self.emit(SIGNAL("output(data)"), self.data)
            time.sleep(0.1)


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        self.myThread = MyThread()
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)
        self.button.clicked.connect(self.plotter)
        self.connect(self.myThread, SIGNAL("output(data)"), self.plotter)

    def plotter(self, data):
        self.curve = self.plot.getPlotItem().plot()
        self.curve.setData(data, downsample=10)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.centralwidget = QtGui.QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.login_widget_1 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_1)
        self.login_widget_2 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_2)
        self.setCentralWidget(self.centralwidget)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

Но это дает мне ошибку:

"in self.connect(self.myThread, SIGNAL("output(data)"), self.plotter)
NameError: name 'SIGNAL' is not defined"

Прочитав вопрос четырехлетней давности, я заменил SIGNAL QtCore.SIGNAL, чтобы получить следующую ошибку:

"in self.connect(self.myThread, QtCore.SIGNAL("output(data)"), self.plotter)
TypeError: C++ type 'data' is not supported as a slot argument type"

Затем, игнорируя TypeError как:

    try:
    self.connect(self.myThread, QtCore.SIGNAL("output(data)"), self.plotter)
except TypeError:
    pass

Графический интерфейс запускается, но когда я, наконец, нажимаю кнопку и подключаюсь к функции self.plotter, я получаю следующую ошибку:

"in plotter
self.curve.setData(data, downsample=10)
File "C:\Users\iyv\AppData\Local\Programs\Python\Python34_64\lib\site-
packages\pyqtgraph\graphicsItems\PlotDataItem.py", line 381, in setData
raise Exception('Invalid data type %s' % type(data))
Exception: Invalid data type <class 'bool'>"

Я довольно смущен. Должен ли я связаться с PyQtGraph? Любая помощь с благодарностью приветствуется.


person Ivy    schedule 23.01.2017    source источник
comment
Я отредактировал свой ответ ниже, чтобы показать рабочий пример.   -  person ImportanceOfBeingErnest    schedule 24.01.2017


Ответы (1)


Вам нужно разделить графический интерфейс и поток. В этом случае они не должны совместно использовать какие-либо объекты графического интерфейса, такие как виджет графика!

Я не уверен, что вам нужна многопоточность, так как ваши данные не очень большие и обновляются только 10 раз в секунду. Но если вы решите его использовать, используйте только сигналы и слоты для связи между поток и графический интерфейс.


Ниже приведена модифицированная версия вашей программы, которая использует многопоточность и отлично работает. Он использует новый стиль сигналов и слотов. Надеюсь, это легче понять, потому что вы можете просто использовать object в качестве типа данных.

Две вещи, которые не имеют смысла в вашей программе:

  • вы переопределяете self.curve в графическом слоте.
  • вы вызываете слот построения при нажатии кнопки. Вместо этого нажатие кнопки должно запустить поток.

Я исправил их.

import random
import sys
import pyqtgraph as pg
import time
from PyQt4 import QtGui, QtCore


class MyThread(QtCore.QThread):
    signal = QtCore.pyqtSignal(object)
    def __init__(self, parent=None):
        super(MyThread, self).__init__(parent=parent)
        self.data = [0]

    def __del__(self):
        self.exiting = True
        self.wait()

    def run(self):
        while True:
            self.data.append(random.random())
            self.signal.emit(self.data)
            time.sleep(0.1)


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        self.myThread = MyThread()
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Start Plotting')
        layout.addWidget(self.button)
        self.plot = pg.PlotWidget()
        layout.addWidget(self.plot)
        self.setLayout(layout)
        self.curve = self.plot.getPlotItem().plot()
        self.button.clicked.connect(self.start)


    def plotter(self, data):
        self.curve.setData(data)

    def start(self):
        self.myThread.start()
        self.myThread.signal.connect(self.plotter)


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.centralwidget = QtGui.QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
        self.login_widget_1 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_1)
        self.login_widget_2 = LoginWidget(self)
        self.horizontalLayout.addWidget(self.login_widget_2)
        self.setCentralWidget(self.centralwidget)


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())
person ImportanceOfBeingErnest    schedule 23.01.2017