【问题标题】:How can I automatically update data in pyqtgraph?如何自动更新pyqtgraph中的数据?
【发布时间】:2018-04-25 16:37:46
【问题描述】:

我正在尝试绘制烛台图。我参考了这个问答(The fastest way to add a new data bar with pyqtgraph)

我希望我的程序通过调用 update() 从服务器接收新值来更新和绘制新烛台。

我遇到的一个问题是,如果不使用 QtCore.QTimer(),此示例将无法运行。例如,如果我在提示符或通过 apscheduler 手动调用 update(),烛台显示没有区别,但一旦我选择绘图窗口,它会同时显示新的烛台。我不明白为什么会这样。

谁能尝试测试一下并告诉我如何解决这个问题?

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

【问题讨论】:

  • 如果您还有其他问题,请创建一个新问题,因此:为每个问题创建一个帖子。
  • @eyllanesc 好的!我创建了一个更详细的新问题(stackoverflow.com/questions/47361222/…)。谢谢你的建议。

标签: python pyqtgraph


【解决方案1】:

很多时候,Qt 的非本地元素会产生这种行为,Qt 是一个框架,其中包含许多任务的库,例如您想要执行的任务,Qt 风格的解决方案是使用 QThreads,在这种情况下,我们会使用更新数据的信号,但是另一种解决方案,但使用 QRunnable 和 QThreadPool 很简单,如下所示:

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

【讨论】:

  • 谢谢你,我看到了希望!我想我需要更多地了解QT。你能告诉我如何手动更新烛台吗?例如,像这样:candleItem.set_date(new_array) 实际上,我的目标不是绘制随机值,而是在使用新的实际值调用 set_data() 时更新实际值
  • @maynull 它只是将生成随机值的逻辑替换为对你服务器的请求
  • 也许我现在不明白它是如何工作的。我尝试在删除while TrueQtCore.QThread.msleep(1000) 后执行runnable.run(),认为调用run() 将自行工作,但除非我单击窗口,否则图表仍然没有显示任何差异。你能告诉我如何通过每次调用run() 方法来更新图表吗?
  • QRunnable 是一个面向在线程上运行的类,所以直接调用它不是正确的选择,如果你想调用我想你只需要执行:QtCore.QThreadPool.globalInstance().start(runnable)(这个我没试过),但解释了你想怎么称呼它?
猜你喜欢
  • 2018-11-23
  • 2021-03-31
  • 2020-07-09
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多