Python/Matplotlib - 快速更新轴上的文本

发布于 2024-11-08 14:09:39 字数 2221 浏览 0 评论 0原文

我在 wxpython 窗口中有一个 matplotlib 图/画布。我想在鼠标移动时更新绘图上的一些信息。我已连接到“motion_notify_event”来获取此信息。

在下面的代码中,绘制了大量随机数据,然后在窗口的状态栏中显示光标的 x,y 位置。这非常顺利并且效果很好。然而,我真的想在图的顶部显示这些信息。如果取消注释 cbUpdateCursor 的最后两行,就会显示我想要的行为。但是,完成此操作后,移动光标的响应时间非常慢(因为调用了绘制并且存在大量数据,但必须调用绘制否则文本不会更新)。

如何加快速度,以便光标位置可以显示在绘图上,但又不会减慢太多?我想我可能需要用 bbox 做点什么?

代码:

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

基本上我想要类似于使用 pyplot 显示的文本,即运行下面的代码时的右下角:

代码:

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

编辑

在我的实际程序中,我希望静态文本位于 matplotlib 轴内,而不是真正位于其上方。所以我不认为我可以只使用 wxpython statictext 来显示它。

I have a matplotlib figure/canvas in a wxpython window. I want to update some information on the plot as the mouse moves around. I've connected to 'motion_notify_event' to get this information.

In the code below, a lot of random data is plotted and then the x,y location of the cursor is displayed in the statusbar of the window. This 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 draw gets called and there is a lot of data, but draw must be called or the text doesn't get updated).

How can I speed this up so the cursor position can be displayed on the plot, but not slow it down so much? I think I might need to do something with 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 gets displayed using pyplot, i.e. the bottom right corner when the code below is run:

Code:

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

EDIT:

In my actual program, I want the static text to be within the matplotlib axes, not really above it. So I don't think I can just use a wxpython statictext to display it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陌生 2024-11-15 14:09:39

您可以使用位块传输,类似于此处的动画示例。

在这种情况下,这会产生非常大的性能差异,因为只需要重绘窗口的一小部分。

不幸的是,我不知道如何在重绘时在文本后面获得灰色背景,以匹配其后面的默认图形背景......不过,性能非常出色。

作为基于上面代码的独立示例:

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

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

This make a very large performance difference in this case, as only a small portion of the window needs to be redrawn.

Unfortunately, I can't figure out how to get a gray background behind the text when it's redrawn, to match the default figure background behind it... The performance is excellent, though.

As a stand-alone 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()
你怎么敢 2024-11-15 14:09:39

您可以在顶部添加一个静态文本框,然后更新它的标签:

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

You could add a static text box on top, and just update it's label:

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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文