简体   繁体   English

如何以防止xdata变为None的方式更新matplotlib图形?

[英]How can I update a matplotlib figure in a way that prevents xdata from being None?

I'm writing an application that periodically (approximately once per second) collects some data from a hardware device and displays it in a graph using matplotlib. 我正在编写一个应用程序,该应用程序定期(大约每秒一次)从硬件设备收集一些数据,并使用matplotlib将其显示在图形中。 The user will periodically click on the graph, and when that happens I need to know the x and y coordinates of the click, as measured in the graph's coordinate system. 用户将定期单击图形,当发生这种情况时,我需要知道图形的坐标系中测量的单击的xy坐标。

This is easy enough to do: if canvas is a FigureCanvas instance, you can say 这很容易做到:如果canvasFigureCanvas实例,则可以说

canvas.mpl_connect('button_release_event', mouse_up_handler)

and define 并定义

def mouse_up_handler(event):
    print('The user clicked at ({}, {})'.format(event.xdata, event.ydata))

to show the click coordinates in the graph's coordinate system. 以在图形的坐标系中显示点击坐标。 Occasionally, though, the user will click right as the graph is updating, and then event.xdata and .ydata are None . 但是,有时用户会在图形更新时单击鼠标右键,然后event.xdata.ydataNone Right now I'm just discarding those clicks, but obviously it's pretty frustrating for the user when the application just ignores their clicks. 现在,我只是舍弃这些点击,但是当应用程序忽略他们的点击时,这显然让用户感到沮丧。

I've attached a full sample program so that you can see how I'm replacing the current graph with the new one. 我已经附上了完整的示例程序,以便您可以看到如何用新的图形替换当前的图形。 (If you run it and click on the graph repeatedly you'll see that the reported x coordinate is sometimes None .) Can I replace the figure's data in a way that never gives None for the coordinates? (如果运行它并重复单击图形,您有时会看到报告的x坐标有时为None 。)我可以用永远不会给出None的方式替换图形的数据吗? I only want one set of data to be shown at a time. 我只希望一次显示一组数据。

Example program 范例程序

from __future__ import print_function
from math import exp
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg \
    as FigureCanvas
from matplotlib.figure import Figure
from random import normalvariate
from threading import Timer
from time import sleep
import wx

class GraphView(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)

        self.figure = Figure()
        canvas = FigureCanvas(self, -1, self.figure)
        canvas.mpl_connect('button_release_event', self.mouse_up_handler)

        self.axes = None

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(canvas, 1, wx.GROW)
        self.SetSizer(sizer)

        self.generate_new_data()

    def generate_new_data(self):
        x_values = range(16384)
        y_values = [exp(normalvariate(0, 10)) for _ in range(16384)]

        if self.axes is not None:
            self.figure.delaxes(self.axes)

        sleep(0.4)  # simulate some additional work being done with the data

        self.axes = self.figure.add_subplot(1, 1, 1)
        self.axes.set_yscale('log')
        self.axes.autoscale(enable=True)
        self.axes.set_xlim(x_values[0], x_values[-1])
        self.axes.plot(x_values, y_values, antialiased=False)

        self.figure.canvas.draw()

        Timer(1.0, self.generate_new_data).start()

    def mouse_up_handler(self, event):
        print('Click at x = {}'.format(event.xdata))


class GraphWindow(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent)
        graph_view = GraphView(self)


if __name__ == '__main__':
    app = wx.App(False)
    GraphWindow(None).Show()
    app.MainLoop()

Something like: 就像是:

def __init__(self, parent):
    wx.Panel.__init__(self, parent)

    self.figure = Figure()
    canvas = FigureCanvas(self, -1, self.figure)
    canvas.mpl_connect('button_release_event', self.mouse_up_handler)

    # make axes
    self.ax = self.figure.add_subplot(1, 1, 1)
    # set up details
    self.ax.set_yscale('log')
    self.ax.autoscale(enable=True)
    self.ax.set_xlim(x_values[0], x_values[-1]) 
    # make the line
    self.ln, = self.ax.plot([], []) # add what ever parameters you want

    sizer = wx.BoxSizer(wx.VERTICAL)
    sizer.Add(canvas, 1, wx.GROW)
    self.SetSizer(sizer)

    self.generate_new_data()



def generate_new_data(self):
    x_values = range(16384)
    y_values = [exp(normalvariate(0, 10)) for _ in range(16384)]

    sleep(0.4)  # simulate some additional work being done with the data

    # update your existing line
    self.ln.set_data(x_values, y_values, antialiased=False)

    self.figure.canvas.draw()

    Timer(1.0, self.generate_new_data).start()

to re-use the axes and Line2D objects. 重用轴和Line2D对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM