Python / Matplotlib - Quickly update axis text

I have a matplotlib shape / canvas in a wxpython window. I want to update some plot information when the mouse moves. I contacted 'motion_notify_event' to get this information.

In the code below, a lot of random data is superimposed, and then the x, y position of the cursor appears in the window status bar. It is very smooth and works well. However, I really want to display this information at the top of the plot. The behavior I want is shown if you uncomment the last two lines of cbUpdateCursor. However, when this is done, the response time to moving the cursor is terribly slow (because a draw is called and there is a lot of data, but you need to draw, or the text is not updated).

How can I speed it up so that the cursor position appears on the chart but doesnโ€™t slow it down? I think I might need to do something using bbox?

code:

import wx import numpy as np import matplotlib matplotlib.use('WXAgg') from matplotlib.figure import Figure from matplotlib.widgets import Cursor from matplotlib.backends.backend_wxagg import \ FigureCanvasWxAgg as FigCanvas, \ NavigationToolbar2WxAgg as NavigationToolbar class wxPlotting(wx.Frame): title = 'Test' def __init__(self): wx.Frame.__init__(self, None, -1, self.title) self.time = np.arange(10000) self.data = np.random.random(10000) self.sb = self.CreateStatusBar() self.create_main_panel() self.axes.plot(self.time, self.data) self.canvas.draw() def create_main_panel(self): self.panel = wx.Panel(self) self.fig = Figure((5.0, 4.0), dpi=100) self.canvas = FigCanvas(self.panel, -1, self.fig) self.axes = self.fig.add_subplot(111) self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes) self.cursor = Cursor(self.axes, useblit=True, color='red') self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) def cbUpdateCursor(self, event): if event.inaxes: text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata) self.sb.SetStatusText(text) #self.text.set_text(text) #self.canvas.draw() if __name__ == '__main__': app = wx.PySimpleApp() app.frame = wxPlotting() app.frame.Show() app.MainLoop() 

Basically, I want something similar to the text that is displayed with pyplot, i.e. bottom right corner when the following code is executed:

code:

 import matplotlib.pyplot as plt plt.plot(range(10000), range(10000)) plt.show() 

EDIT

In my real program, I want the static text to be within the matplotlib axes, and not above it. So I donโ€™t think I can just use wxpython static text to display it.

+5
source share
2 answers

You can use blitting, similar to the animation examples here .

In this case, there is a very big difference in performance, since only a small part of the window needs to be redrawn.

Unfortunately, I canโ€™t figure out how to get a gray background behind the text when it is redrawn to fit the default background behind it ... However, the performance is great.

As a separate example based on your code above:

 import wx import numpy as np import matplotlib matplotlib.use('WXAgg') from matplotlib.figure import Figure from matplotlib.widgets import Cursor from matplotlib.backends.backend_wxagg import \ FigureCanvasWxAgg as FigCanvas, \ NavigationToolbar2WxAgg as NavigationToolbar class wxPlotting(wx.Frame): title = 'Test' def __init__(self): wx.Frame.__init__(self, None, -1, self.title) self.time = np.arange(10000) self.data = np.random.random(10000) self.sb = self.CreateStatusBar() self.create_main_panel() self.axes.plot(self.time, self.data) self.background = self.canvas.copy_from_bbox(self.fig.bbox) self.canvas.draw() def create_main_panel(self): self.panel = wx.Panel(self) self.fig = Figure((5.0, 4.0), dpi=100) self.canvas = FigCanvas(self.panel, -1, self.fig) self.axes = self.fig.add_subplot(111) self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes, animated=True) self.cursor = Cursor(self.axes, useblit=True, color='red') self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) def cbUpdateCursor(self, event): if event.inaxes: text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata) self.sb.SetStatusText(text) self.canvas.restore_region(self.background) self.text.set_text(text) self.axes.draw_artist(self.text) self.canvas.blit(self.text.get_window_extent()) if __name__ == '__main__': app = wx.PySimpleApp() app.frame = wxPlotting() app.frame.Show() app.MainLoop() 
+5
source

You can add a static text box at the top and just update it:

 import wx import numpy as np import matplotlib matplotlib.use('WXAgg') from matplotlib.figure import Figure from matplotlib.widgets import Cursor from matplotlib.backends.backend_wxagg import \ FigureCanvasWxAgg as FigCanvas, \ NavigationToolbar2WxAgg as NavigationToolbar class wxPlotting(wx.Frame): title = 'Test' def __init__(self): wx.Frame.__init__(self, None, -1, self.title) self.time = np.arange(10000) self.data = np.random.random(10000) self.sb = self.CreateStatusBar() self.create_main_panel() self.axes.plot(self.time, self.data) self.canvas.draw() def create_main_panel(self): self.panel = wx.Panel(self) self.fig = Figure((5.0, 4.0), dpi=100) self.canvas = FigCanvas(self.panel, -1, self.fig) self.axes = self.fig.add_subplot(111) self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes) self.cursor = Cursor(self.axes, useblit=True, color='red') self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor) self.vbox = wx.BoxSizer(wx.VERTICAL) self.cursor_pos = wx.StaticText(self.panel,-1, label="") self.vbox.Add(self.cursor_pos, 0, wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) def cbUpdateCursor(self, event): if event.inaxes: text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata) self.sb.SetStatusText(text) self.cursor_pos.SetLabel(text) #self.text.set_text(text) #self.canvas.draw() if __name__ == '__main__': app = wx.PySimpleApp() app.frame = wxPlotting() app.frame.Show() app.MainLoop() 
0
source

Source: https://habr.com/ru/post/949063/


All Articles