Matplotlib: scrolling plot
Question: I'm new to Python and I want to implement a scrolling plot for a very long
time series data. I've found an example from Matplotlib as follows.

<http://scipy-cookbook.readthedocs.io/items/Matplotlib_ScrollingPlot.html>

When I run the example from the link, I found every time I scroll the plot and
release the scrollbar, the scrollbar returns to the beginning. Want to scroll
to the next position? I need to start to scroll from the beginning again.

I want to understand why it happens and how to fix it.


Answer: Here's an improved version of the example. (Disclaimer: I started digging into
it half an hour ago, never before used wx/matplotlib scrollbars so there might
be a much better solution.)

The path I took: first I checked the [wx scroll
events](http://docs.wxwidgets.org/trunk/classwx_scroll_win_event.html "scroll
events"), then found out that the canvas is
[FigureCanvasWxAgg](http://matplotlib.org/api/backend_wxagg_api.html
"FigureCanvasWxAgg") derived from wxPanel, inheriting
[wxWindow](http://docs.wxwidgets.org/trunk/classwx_window.html "wxWindow")
methods. There you may find the scroll position handling methods
`GetScrollPos` and `SetScrollPos`.

    
    
    from numpy import arange, sin, pi, float, size
    
    import matplotlib
    matplotlib.use('WXAgg')
    from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
    from matplotlib.figure import Figure
    
    import wx
    
    class MyFrame(wx.Frame):
        def __init__(self, parent, id):
            wx.Frame.__init__(self,parent, id, 'scrollable plot',
                    style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER,
                    size=(800, 400))
            self.panel = wx.Panel(self, -1)
    
            self.fig = Figure((5, 4), 75)
            self.canvas = FigureCanvasWxAgg(self.panel, -1, self.fig)
            self.scroll_range = 400
            self.canvas.SetScrollbar(wx.HORIZONTAL, 0, 5,
                                     self.scroll_range)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(self.canvas, -1, wx.EXPAND)
    
            self.panel.SetSizer(sizer)
            self.panel.Fit()
    
            self.init_data()
            self.init_plot()
    
            self.canvas.Bind(wx.EVT_SCROLLWIN, self.OnScrollEvt)
    
        def init_data(self):
    
            # Generate some data to plot:
            self.dt = 0.01
            self.t = arange(0,5,self.dt)
            self.x = sin(2*pi*self.t)
    
            # Extents of data sequence:
            self.i_min = 0
            self.i_max = len(self.t)
    
            # Size of plot window:
            self.i_window = 100
    
            # Indices of data interval to be plotted:
            self.i_start = 0
            self.i_end = self.i_start + self.i_window
    
        def init_plot(self):
            self.axes = self.fig.add_subplot(111)
            self.plot_data = \
                      self.axes.plot(self.t[self.i_start:self.i_end],
                                     self.x[self.i_start:self.i_end])[0]
    
        def draw_plot(self):
    
            # Update data in plot:
            self.plot_data.set_xdata(self.t[self.i_start:self.i_end])
            self.plot_data.set_ydata(self.x[self.i_start:self.i_end])
    
            # Adjust plot limits:
            self.axes.set_xlim((min(self.t[self.i_start:self.i_end]),
                               max(self.t[self.i_start:self.i_end])))
            self.axes.set_ylim((min(self.x[self.i_start:self.i_end]),
                                max(self.x[self.i_start:self.i_end])))
    
            # Redraw:
            self.canvas.draw()
    
        def update_scrollpos(self, new_pos):
            self.i_start = self.i_min + new_pos
            self.i_end = self.i_min + self.i_window + new_pos
            self.canvas.SetScrollPos(wx.HORIZONTAL, new_pos)
            self.draw_plot()
    
        def OnScrollEvt(self, event):
            evtype = event.GetEventType()
    
            if evtype == wx.EVT_SCROLLWIN_THUMBTRACK.typeId:
                pos = event.GetPosition()
                self.update_scrollpos(pos)
            elif evtype == wx.EVT_SCROLLWIN_LINEDOWN.typeId:
                pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
                self.update_scrollpos(pos + 1)
            elif evtype == wx.EVT_SCROLLWIN_LINEUP.typeId:
                pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
                self.update_scrollpos(pos - 1)
            elif evtype == wx.EVT_SCROLLWIN_PAGEUP.typeId:
                pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
                self.update_scrollpos(pos - 10)
            elif evtype == wx.EVT_SCROLLWIN_PAGEDOWN.typeId:
                pos = self.canvas.GetScrollPos(wx.HORIZONTAL)
                self.update_scrollpos(pos + 10)
            else:
                print "unhandled scroll event, type id:", evtype
    
    class MyApp(wx.App):
        def OnInit(self):
            self.frame = MyFrame(parent=None,id=-1)
            self.frame.Show()
            self.SetTopWindow(self.frame)
            return True
    
    if __name__ == '__main__':
        app = MyApp()
        app.MainLoop()
    

You may adjust e.g. the increments for PAGEUP/PAGEDOWN if you feel it too
slow.

Also if you wish, the events can be handled separately setting up the specific
event handlers instead of their collection `EVT_SCROLLWIN`, then instead of
if/elifs there will be OnScrollPageUpEvt etc.

