2010-02-09 38 views
2

我剛開始使用Matplotlib的動畫功能來製作Google財經圖表。像Matplotlib中的Google Finance圖表一樣動畫?

我在項目網站(Draggable rectangle exerciseapi example code: date_demo.py)上找到了兩個例子,並調整了一些代碼以提供底部列出的代碼。

雖然看起來不錯,但我希望頂部圖表(主)在底部圖表(從屬)選擇移動時動態更新,而不僅僅在底部選擇被釋放時。我怎樣才能做到這一點?我試圖將self.rect.figure.canvas.draw()位移動到on_motion方法,但它似乎會干擾blit,因爲底部選擇無法正確呈現。

所以我會假設解決方案是做底部圖表的智能動畫,即blit-ing位,而頂部圖表完全重新繪製。問題在於,我可以重繪任何東西的唯一方法是通過重新繪製整個畫布,這將包括底部圖表。我確實找到matplotlib.axesdraw()方法,但我無法使其工作。正如我上面所說的,最好我想重新繪製最上面的圖表,而最下面的圖表是聰明的方式。有誰知道如何做到這一點?

這是我的代碼到目前爲止。請原諒代碼,這有點不整潔。

import datetime 
import numpy as np 
import sys 
import time 
import wx 
import matplotlib 
from matplotlib.figure import Figure 
import matplotlib.dates as mdates 
import matplotlib.ticker as mtickers 
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 
import matplotlib.patches as mpatches 

class DraggableRectangle: 
    lock = None 
    def __init__(self, rect, master, xMin, xMax):  
     self.rect = rect   
     self.press = None 
     self.background = None 
     self.xMax = xMax 
     self.xMin = xMin 
     self.master = master 
    def connect(self):  
     self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press) 
     self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release) 
     self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) 
    def on_press(self, event):  
     if event.inaxes != self.rect.axes: return 
     if DraggableRectangle.lock is not None: return 
     contains, attrd = self.rect.contains(event) 
     if not contains: return  
     x0, y0 = self.rect.xy 
     self.press = x0, y0, event.xdata, event.ydata 
     DraggableRectangle.lock = self 
     canvas = self.rect.figure.canvas 
     axes = self.rect.axes 
     self.rect.set_animated(True) 
     canvas.draw() 
     self.background = canvas.copy_from_bbox(self.rect.axes.bbox) 
     axes.draw_artist(self.rect) 
     canvas.blit(axes.bbox) 
    def on_motion(self, event): 
     if DraggableRectangle.lock is not self: return 
     if event.inaxes != self.rect.axes: return 
     x0, y0, xpress, ypress = self.press 
     dx = event.xdata - xpress 
     dy = 0 
     if x0+dx > self.xMax: 
      self.rect.set_x(self.xMax) 
     elif x0+dx < self.xMin: 
      self.rect.set_x(self.xMin) 
     else: 
      self.rect.set_x(x0+dx) 
     self.rect.set_y(y0+dy) 
     canvas = self.rect.figure.canvas 
     axes = self.rect.axes 
     canvas.restore_region(self.background) 
     self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92) 
     axes.draw_artist(self.rect) 
     canvas.blit(axes.bbox) 
    def on_release(self, event):   
     if DraggableRectangle.lock is not self: return 
     self.press = None 
     DraggableRectangle.lock = None 
     self.rect.set_animated(False) 
     self.background = None 
     self.rect.figure.canvas.draw() 
    def disconnect(self): 
     self.rect.figure.canvas.mpl_disconnect(self.cidpress) 
     self.rect.figure.canvas.mpl_disconnect(self.cidrelease) 
     self.rect.figure.canvas.mpl_disconnect(self.cidmotion) 

class MplCanvasFrame(wx.Frame): 
    def __init__(self): 
     wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700)) 
     datafile = matplotlib.get_example_data('goog.npy') 
     r = np.load(datafile).view(np.recarray) 
     datesFloat = matplotlib.dates.date2num(r.date) 
     figure = Figure() 
     xMaxDatetime = r.date[len(r.date)-1] 
     xMinDatetime = r.date[0] 
     xMaxFloat = datesFloat[len(datesFloat)-1] 
     xMinFloat = datesFloat[0] 
     yMin = min(r.adj_close) // 5 * 5 
     yMax = (1 + max(r.adj_close) // 5) * 5  
     master = figure.add_subplot(211) 
     master.plot(datesFloat, r.adj_close) 
     master.xaxis.set_minor_locator(mdates.MonthLocator()) 
     master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10))) 
     master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y')) 
     master.set_xlim(datesFloat[120], datesFloat[120]+92) 
     master.yaxis.set_minor_locator(mtickers.MultipleLocator(50)) 
     master.yaxis.set_major_locator(mtickers.MultipleLocator(100)) 
     master.set_ylim(yMin, yMax) 
     master.set_position([0.05,0.20,0.92,0.75]) 
     master.xaxis.grid(True, which='minor') 
     master.yaxis.grid(True, which='minor') 
     slave = figure.add_subplot(212, yticks=[]) 
     slave.plot(datesFloat, r.adj_close) 
     slave.xaxis.set_minor_locator(mdates.MonthLocator()) 
     slave.xaxis.set_major_locator(mdates.YearLocator()) 
     slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y')) 
     slave.set_xlim(xMinDatetime, xMaxDatetime) 
     slave.set_ylim(yMin, yMax) 
     slave.set_position([0.05,0.05,0.92,0.10]) 
     rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)  
     slave.add_patch(rectangle) 
     canvas = FigureCanvas(self, -1, figure) 
     drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92) 
     drag.connect() 

app = wx.PySimpleApp() 
frame = MplCanvasFrame() 
frame.Show(True) 
app.MainLoop() 
+0

我沒有一個答案,但涼爽的情節! – Mark 2010-02-09 14:55:34

+0

是的,整個動畫的東西在Matplotlib中出人意料的出色。如果我能把這最後一部分弄清楚,我更想要它) – c00kiemonster 2010-02-09 15:20:27

回答

1

我今天早上有機會在這工作(過去3天我們有第二場暴風雪)。你是對的,如果你試圖重新繪製on_motion中的整個圖形,它會弄亂黃色矩形的動畫。關鍵是還要在主子​​圖上劃線。

試試這個代碼了:

import datetime 
import numpy as np 
import sys 
import time 
import wx 
import matplotlib 
from matplotlib.figure import Figure 
import matplotlib.dates as mdates 
import matplotlib.ticker as mtickers 
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas 
import matplotlib.patches as mpatches 

class DraggableRectangle: 
    lock = None 
    def __init__(self, rect, master, xMin, xMax):  
     self.rect = rect   
     self.press = None 
     self.slave_background = None 
     self.master_background = None 
     self.xMax = xMax 
     self.xMin = xMin 
     self.master = master 
     self.master_line, = self.master.get_lines() 

    def connect(self):  
     self.cidpress = self.rect.figure.canvas.mpl_connect('button_press_event', self.on_press) 
     self.cidrelease = self.rect.figure.canvas.mpl_connect('button_release_event', self.on_release) 
     self.cidmotion = self.rect.figure.canvas.mpl_connect('motion_notify_event', self.on_motion) 

    def on_press(self, event):  
     if event.inaxes != self.rect.axes: return 
     if DraggableRectangle.lock is not None: return 
     contains, attrd = self.rect.contains(event) 
     if not contains: return  
     x0, y0 = self.rect.xy 
     self.press = x0, y0, event.xdata, event.ydata 
     DraggableRectangle.lock = self 
     canvas = self.rect.figure.canvas 
     axes = self.rect.axes 

     # set up our animated elements 
     self.rect.set_animated(True) 
     self.master_line.set_animated(True) 
     self.master.xaxis.set_visible(False) #we are not animating this 

     canvas.draw() 

     # backgrounds for restoring on animation 
     self.slave_background = canvas.copy_from_bbox(self.rect.axes.bbox) 
     self.master_background = canvas.copy_from_bbox(self.master.axes.bbox) 

     axes.draw_artist(self.rect) 
     canvas.blit(axes.bbox) 

    def on_motion(self, event): 
     if DraggableRectangle.lock is not self: return 
     if event.inaxes != self.rect.axes: return 
     x0, y0, xpress, ypress = self.press 
     dx = event.xdata - xpress 
     dy = 0 
     if x0+dx > self.xMax: 
      self.rect.set_x(self.xMax) 
     elif x0+dx < self.xMin: 
      self.rect.set_x(self.xMin) 
     else: 
      self.rect.set_x(x0+dx) 
     self.rect.set_y(y0+dy) 
     canvas = self.rect.figure.canvas 
     axes = self.rect.axes 

     # restore backgrounds 
     canvas.restore_region(self.slave_background) 
     canvas.restore_region(self.master_background) 

     # set our limits for animated line 
     self.master.set_xlim(self.rect.get_x(), self.rect.get_x() + 92) 

     # draw yellow box 
     axes.draw_artist(self.rect) 
     canvas.blit(axes.bbox) 

     #draw line 
     self.master.axes.draw_artist(self.master_line) 
     canvas.blit(self.master.axes.bbox) 

    def on_release(self, event):   
     if DraggableRectangle.lock is not self: return 
     self.press = None 
     DraggableRectangle.lock = None 

     # unanimate rect and lines 
     self.rect.set_animated(False) 
     self.master_line.set_animated(False) 

     self.slave_background = None 
     self.master_background = None 

     # redraw whole figure 
     self.master.xaxis.set_visible(True) 
     self.rect.figure.canvas.draw() 

    def disconnect(self): 
     self.rect.figure.canvas.mpl_disconnect(self.cidpress) 
     self.rect.figure.canvas.mpl_disconnect(self.cidrelease) 
     self.rect.figure.canvas.mpl_disconnect(self.cidmotion) 

class MplCanvasFrame(wx.Frame): 
    def __init__(self): 
     wx.Frame.__init__(self, None, wx.ID_ANY, title='First Chart', size=(800, 700)) 
     datafile = matplotlib.get_example_data('goog.npy') 
     r = np.load(datafile).view(np.recarray) 
     datesFloat = matplotlib.dates.date2num(r.date) 
     figure = Figure() 
     xMaxDatetime = r.date[len(r.date)-1] 
     xMinDatetime = r.date[0] 
     xMaxFloat = datesFloat[len(datesFloat)-1] 
     xMinFloat = datesFloat[0] 
     yMin = min(r.adj_close) // 5 * 5 
     yMax = (1 + max(r.adj_close) // 5) * 5  
     master = figure.add_subplot(211) 
     master.plot(datesFloat, r.adj_close) 
     master.xaxis.set_minor_locator(mdates.MonthLocator()) 
     master.xaxis.set_major_locator(mdates.MonthLocator(bymonth=(1,4,7,10))) 
     master.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y')) 
     master.set_xlim(datesFloat[120], datesFloat[120]+92) 
     master.yaxis.set_minor_locator(mtickers.MultipleLocator(50)) 
     master.yaxis.set_major_locator(mtickers.MultipleLocator(100)) 
     master.set_ylim(yMin, yMax) 
     master.set_position([0.05,0.20,0.92,0.75]) 
     master.xaxis.grid(True, which='minor') 
     master.yaxis.grid(True, which='minor') 
     slave = figure.add_subplot(212, yticks=[]) 
     slave.plot(datesFloat, r.adj_close) 
     slave.xaxis.set_minor_locator(mdates.MonthLocator()) 
     slave.xaxis.set_major_locator(mdates.YearLocator()) 
     slave.xaxis.set_major_formatter(mdates.DateFormatter('%b-%y')) 
     slave.set_xlim(xMinDatetime, xMaxDatetime) 
     slave.set_ylim(yMin, yMax) 
     slave.set_position([0.05,0.05,0.92,0.10]) 
     rectangle = mpatches.Rectangle((datesFloat[120], yMin), 92, yMax-yMin, facecolor='yellow', alpha = 0.4)  
     slave.add_patch(rectangle) 
     canvas = FigureCanvas(self, -1, figure) 
     drag = DraggableRectangle(rectangle, master, xMinFloat, xMaxFloat - 92) 
     drag.connect() 

app = wx.PySimpleApp() 
frame = MplCanvasFrame() 
frame.Show(True) 
app.MainLoop() 
+0

有趣。我希望今天晚些時候會看看這個。我試着用master.get_lines()方法修改,但是我不太清楚。看起來你確實是對的。 – c00kiemonster 2010-02-11 07:53:27

+0

我設法blax xaxis以獲得網格線動畫也。是否有可能使xticklabels動畫?我嘗試通過get_majorticklables()方法獲取它們,但它並不完美。 – c00kiemonster 2010-02-12 05:22:03