QThreading PyQtGraph PlotWidgets in PyQt4

北城余情 提交于 2019-12-11 01:53:28

问题


As a continuation of the question I got solved here some days ago, I've got a PyQt4 GUI which embeds two PyQtGraph's PlotWidgets, which each set/update data from an threaded randomly-appending array triggered by a button. It works beautifully, and the GUI is responsive. But there comes a point, where the graphs stop showing the updates inside their respective PlotWidgets, and I have to minimize/maximize to see the updates drawn inside the PlotWidgets. The code looks like:

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_())

Any clue or workaround for what I can do for not having to maximize/minimize in order to get the updates in the GUI will make me incredibly glad.

Following the advice from @ImportanceOfBeingErnest in the answer below, I attempted following the Python Wiki to connect the GUI and the thread with signals and slots as:

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_())

But that gives me the error:

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

By reading a question from 4 years ago, I substitute SIGNAL by QtCore.SIGNAL to get the following error:

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

Then by ingnoring the TypeError as:

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

The GUI starts, but when I finally click the button and connect to the self.plotter function I get the following error:

"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'>"

I am pretty confused. Should I contact PyQtGraph? Any help gratefully welcomed.


回答1:


You need to keep the GUI and the thread separate. They must not share any GUI object like the plot widget in this case!

I'm not sure if you need threading since your data is not very big and is only updated 10 times a second. But if you decide to use it, only use signals and slots to communicate between thread and GUI.


Below is a modified version of your program which uses threading and works fine. It uses the new style signals and slots. This is hopefully easier to understand, because you can just use object as datatype.

Two things hat make no sense in your program are:

  • you redefine self.curve in the plotting slot.
  • you call the plotting slot when pressing the button. Instead a button press should start the thread.

I have corrected them.

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_())


来源:https://stackoverflow.com/questions/41808667/qthreading-pyqtgraph-plotwidgets-in-pyqt4

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!