#python #text #matplotlib
#python #текст #matplotlib
Вопрос:
У меня есть рисунок / холст matplotlib в окне wxpython. Я хочу обновлять некоторую информацию о графике по мере перемещения мыши. Я подключился к ‘motion_notify_event’, чтобы получить эту информацию.
В приведенном ниже коде выводится множество случайных данных, а затем в строке состояния окна отображается местоположение курсора по x, y. Это очень плавно и хорошо работает. Однако я действительно хочу отобразить эту информацию в верхней части графика. Поведение, которое я хочу, будет показано, если вы раскомментируете последние две строки cbUpdateCursor. Однако, когда это сделано, время отклика на перемещение курсора ужасно медленно (потому что вызывается draw и имеется много данных, но draw должен быть вызван, иначе текст не обновляется).
Как я могу ускорить это, чтобы положение курсора отображалось на графике, но не сильно замедляло его? Я думаю, мне может понадобиться что-то сделать с 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 для его отображения.
Ответ №1:
Вы могли бы использовать блиттинг, аналогичный примерам анимации здесь.
В данном случае это приводит к очень большой разнице в производительности, поскольку требуется перерисовать лишь небольшую часть окна.
К сожалению, я не могу понять, как получить серый фон позади текста при его перерисовке, чтобы соответствовать фону рисунка по умолчанию позади него… Тем не менее, производительность отличная.
В качестве отдельного примера, основанного на вашем коде выше:
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()
Комментарии:
1. @Joe Kington: Интересно… это работает так, как описано в Linux, но на моем рабочем компьютере (Windows xp) это работает совершенно по-другому. В Windows он заменяется серым фоном по умолчанию, но, похоже, он заменяется не в каждом цикле. Числа в основном нечитабельны, поскольку они накладываются друг на друга.
2. На самом деле, если вы переключите последние две строки init (вызовите self.canvas. рисуйте перед self.background = …), тогда он будет корректно работать в Windows с правильным цветом фона. Мне придется попробовать это позже, чтобы посмотреть, исправит ли это фоновую проблему и в Linux.
3. @Scott B — Ага! Я тестировал это только на Linux. Если у вас есть время, вероятно, стоит отправить отчет об ошибке и / или запросить в списке рассылки. Я не очень хорошо знаком с wx (мне нужно его изучить. Это действительно выглядит как хороший инструментарий GUI.), но могут быть некоторые хорошо известные обходные пути. Кроме того, вполне возможно, что я делаю что-то менее эффективным способом. Возможно, есть лучший способ сделать это.
4. Да, я продолжу изучать это. Я также заметил, что он запутывается (неправильная информация в self.background) при изменении размера (опять же, в любом случае, в Windows).
5. @Scott — Ах! Это также устраняет фоновую проблему в Linux! Тогда, вероятно, это тот случай, когда я не очень хорошо знаком с вещами, а не ошибка. Это имеет смысл, поскольку matplotlib не инициализирует средство визуализации для canvas до тех пор, пока оно не будет отрисовано в первый раз.
Ответ №2:
Вы могли бы добавить статическое текстовое поле сверху и просто обновить его метку:
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()
Комментарии:
1. Да, я действительно рассматривал это, но я действительно хочу сделать это с matplotlib. Потому что ФАКТИЧЕСКОЕ положение, которое я буду использовать в своей программе, будет находиться внутри самих осей matplotlib.