How can I automatically update data in pyqtgraph?

只愿长相守 提交于 2019-12-11 17:20:05

问题


I'm trying to plot candlesticks. I referred to this question and answer( The fastest way to add a new data bar with pyqtgraph )

I want my program to update and plot new candlesticks by receiving new values from a server, through calling update().

A problem that I have is that this example doesn't work without using QtCore.QTimer(). For example, if I manually call update() on the prompt or by apscheduler, candlesticks show no difference, but once I select the plot window, it shows new candlesticks at the same time. I don't understand why this happens.

Could anyone try testing this and tell me how to fix this problem?

import pyqtgraph as pg
from pyqtgraph import QtCore, QtGui
import random
import numpy as np 
from apscheduler.schedulers.background import BackgroundScheduler

class CandlestickItem(pg.GraphicsObject):
    def __init__(self):
        pg.GraphicsObject.__init__(self)
        self.flagHasData = False

    def set_data(self, data):
        self.data = data 
        self.flagHasData = True
        self.generatePicture()
        self.informViewBoundsChanged()

    def generatePicture(self):
        self.picture = QtGui.QPicture()
        p = QtGui.QPainter(self.picture)
        p.setPen(pg.mkPen('w'))
        w = (self.data[1][0] - self.data[0][0]) / 3.
        for (t, open, close, min, max) in self.data:
            p.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max))
            if open > close:
                p.setBrush(pg.mkBrush('r'))
            else:
                p.setBrush(pg.mkBrush('g'))
            p.drawRect(QtCore.QRectF(t-w, open, w*2, close-open))
        p.end()

    def paint(self, p, *args):
        if self.flagHasData:
            p.drawPicture(0, 0, self.picture)

    def boundingRect(self):
        return QtCore.QRectF(self.picture.boundingRect())

app = QtGui.QApplication([])

data = [  
    [1., 10, 13, 5, 15],
    [2., 13, 17, 9, 20],
    [3., 17, 14, 11, 23],
    [4., 14, 15, 5, 19],
    [5., 15, 9, 8, 22],
    [6., 9, 15, 8, 16],
]
item = CandlestickItem()
item.set_data(data)

plt = pg.plot()
plt.addItem(item)
plt.setWindowTitle('pyqtgraph example: customGraphicsItem')


def update():
    global item, data
    data_len = len(data)
    rand = random.randint(0, len(data)-1)
    new_bar = data[rand][:]
    new_bar[0] = data_len
    data.append(new_bar)
    item.set_data(data)
    app.processEvents() 


## DOESN'T SHOW NEW CANDLESTICKS UNLESS YOU SELECT THE PLOT WINDOW
#sched = BackgroundScheduler()
#sched.start()
#sched.add_job(update, trigger='cron', second='*/1')

## WORKS FINE WITH THIS PARAGRAPH.
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(1000)


if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

回答1:


Many times non-native elements for Qt generate that behavior, Qt is a framework that has libraries for many tasks like the ones you want to do, a Qt-style solution would be to use QThreads and in that case we would use signals to update the data, but another solution but it is simple to use QRunnable and QThreadPool as I show below:

class PlotRunnable(QtCore.QRunnable):
    def __init__(self, it):
        QtCore.QRunnable.__init__(self)
        self.it = it

    def run(self):
        while True:
            data = self.it.data
            data_len = len(data)
            rand = random.randint(0, len(data)-1)
            new_bar = data[rand][:]
            new_bar[0] = data_len
            data.append(new_bar)

            QtCore.QMetaObject.invokeMethod(self.it, "set_data",
                                     QtCore.Qt.QueuedConnection,
                                     QtCore.Q_ARG(list, data))
            QtCore.QThread.msleep(1000)


class CandlestickItem(pg.GraphicsObject):
    def __init__(self):
        pg.GraphicsObject.__init__(self)
        self.flagHasData = False

    @QtCore.pyqtSlot(list)
    def set_data(self, data):
        self.data = data 
        self.flagHasData = True
        self.generatePicture()
        self.informViewBoundsChanged()

    def generatePicture(self):
        [...]

app = QtGui.QApplication([])

data = [  
    [1., 10, 13, 5, 15],
    [2., 13, 17, 9, 20],
    [3., 17, 14, 11, 23],
    [4., 14, 15, 5, 19],
    [5., 15, 9, 8, 22],
    [6., 9, 15, 8, 16],
]
item = CandlestickItem()
item.set_data(data)

plt = pg.plot()
plt.addItem(item)
plt.setWindowTitle('pyqtgraph example: customGraphicsItem')


runnable = PlotRunnable(item)
QtCore.QThreadPool.globalInstance().start(runnable)

if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()


来源:https://stackoverflow.com/questions/47256835/how-can-i-automatically-update-data-in-pyqtgraph

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