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

Python/Matplotlib-快速更新轴上的文本,python,text,matplotlib,Python,Text,Matplotlib,我在wxpython窗口中有一个matplotlib图形/画布。我想在鼠标移动时更新一些绘图信息。我已连接到“运动通知事件”以获取此信息 在下面的代码中,绘制了大量随机数据,然后光标的x、y位置显示在窗口的状态栏中。这是非常顺利,工作得很好。但是,我确实希望在绘图顶部显示此信息。如果取消对cbUpdateCursor最后两行的注释,将显示我想要的行为。但是,完成此操作后,移动光标的响应时间非常慢(因为会调用draw,并且有很多数据,但是必须调用draw,否则文本不会更新) 我怎样才能加快速度,使

我在wxpython窗口中有一个matplotlib图形/画布。我想在鼠标移动时更新一些绘图信息。我已连接到“运动通知事件”以获取此信息

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

我怎样才能加快速度,使光标位置可以显示在绘图上,但又不会使其速度降低太多?我想我可能需要用bbox做点什么

代码:

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

代码:

编辑


在我的实际程序中,我希望静态文本在matplotlib轴内,而不是在它上面。因此,我不认为我可以只使用wxpython静态文本来显示它。

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

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

您可以使用blitting,类似于动画示例

在这种情况下,这会造成很大的性能差异,因为只需要重新绘制窗口的一小部分

不幸的是,我不知道如何在文本重画时获得灰色背景,以匹配文本后面的默认数字背景。。。不过,表演非常出色

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

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

是的,我确实考虑过了,但我真的很想和MatpTLIB一起做。因为我将在我的程序中使用的实际位置将在matplotlib轴本身内。@Joe Kington:有趣的是……这在Linux上可以正常工作,但在我的工作计算机(windows xp)上,工作方式完全不同。在windows上,它确实会被默认的灰色背景替换,但似乎并不是每个循环都会被替换。这些数字通常无法读取,因为它们叠在一起。实际上,如果您切换init的最后两行(调用self.canvas.draw before self.background=…),那么它在Windows上可以正常工作,背景颜色正确。我将不得不稍后试用,看看这是否也能修复Linux上的背景问题。@Scott B-Huh!我只在linux上测试过它。如果你有时间,可能值得提交一份bug报告和/或在邮件列表上询问。我对wx不是很熟悉(我需要学习它。它看起来真的像一个很好的GUI工具包),但可能有一些众所周知的解决方法。而且,我完全有可能以一种效率较低的方式做事。也许有更好的办法。是的,我会继续调查。我还注意到,当它重新调整大小时(还是在Windows上),它会变得一团糟(self.background中的信息错误)。@Scott-Ah!这也解决了linux上的后台问题!这可能是因为我对事物不太熟悉,而不是一个bug。这很有意义,因为matplotlib在第一次绘制画布之前不会初始化画布的渲染器。
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()
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()