Matplotlib: scrolling plot - python

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.

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, then found out that the canvas is FigureCanvasWxAgg derived from wxPanel, inheriting 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.

Related

I want to add an existing graph from matplotlib to my wxpython GUI

i wrote a function, that draws a graph from given values with matplotlib.
i wanted to add the graph to my GUI, that is in another class, just by calling the graph and placing it the top left. I have searched for a while, but havent found a good solution yet.
How could i achieve this?
The code of the graph:
def draw_graph(y_money, x_date):
counter = -1
now_money = []
for value in y_money:
if counter >= 0:
before_value = now_money[-1]
now_value = before_value + y_money[counter+1]
now_money.append(now_value)
counter += 1
else:
now_money.append(y_money[0])
counter += 1
plt.rcParams['toolbar'] = 'None'
plt.plot(now_money, "lime", label="Money")
before = 0
counter_2 = 0
# Show if before value was lower than now value(red/green)
for element in now_money:
if element >= before:
plt.scatter(x_date[counter_2], element, c="green", s=50)
counter_2 += 1
before = element
else:
plt.scatter(x_date[counter_2], element, c="red", s=50)
counter_2 += 1
before = element
x_tick = []
for item in x_date:
try:
x_tick.append(item[:3])
except IndexError:
x_tick.append("")
plt.xticks(np.arange(7), x_tick)
plt.legend()
plt.show()
Since you have not specified where (in terms of wxWidgets) you are trying to add your plot on your gui, I will try to give a general answer. In my applications I use wx.Panel as the basis or canvas of the matplotlib figures.
Inside the panel you can create a figure and plot with your own function. As you can see from the code, this is a class based implementation.
import wx
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
class PlotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
# self.fig = Figure((10, 4), 75)
sizer = wx.BoxSizer()
sizer.Add(self, 1, wx.EXPAND)
self.Parent.SetSizer(sizer)
# self.fig = Figure(facecolor='blue')
self.fig = Figure()
self.canvas = FigureCanvasWxAgg(self, -1, self.fig)
# Create toolbar for the figure
self.toolbar = Toolbar(self.canvas) # matplotlib toolbar
self.toolbar.Realize()
# Now put all into a sizer
sizer = wx.BoxSizer(wx.VERTICAL)
# This way of adding to sizer allows resizing
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL | wx.CENTER | wx.CENTER | wx.GROW)
# Best to allow the toolbar to resize!
sizer.Add(self.toolbar, 0, wx.EXPAND | wx.ALL | wx.GROW)
self.SetSizer(sizer)
self.Fit()
def GetToolBar(self):
# You will need to override GetToolBar if you are using an
# unmanaged toolbar in your frame
return self.toolbar
def onEraseBackground(self, evt):
# this is supposed to prevent redraw flicker on some X servers...
pass
def draw_graph(self, y_money, x_date):
""" You can access above parameters and plot
your graph """
# Your own plotting function definition
This is partly my implementation using the source from this old scipy wiki link. Some of the stuff inside the init function might be useless for your application like toolbar etc.

Relating a canvas to a figure wxPython

I would like to know how to perform the following pseudocode in python when embedding a matplotlib figure inside of a wxPython FigureCanvasWxAgg instance:
the following items need to be used:
---- IMPORTS THAT CAN BE USED ----
import wx
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
-------------------------------------------------------
main_canvas;
shadow_canvas;
big_plot [a matplotlib figure instance with one big plot in it -- like the one you would make with figure.add_subplots(1,1,1)];
small_subplots [a matplotlib figure instance with, say, 2 subplots in it -- you would make with figure.add_subplots(2,1,i), where 1<=i<=2]
a function called SwapView(main_canvas,shadow_canvas,big_plot,small_subplots) that essentially swaps the figure that is currently in shadow_canvas with the one in main_canvas (so keep switching between the one with a big plot and the one with many small plots)
a function UpdateDisplay() that dynamically updates the display every time you call SwapView()
******* PSEUDOCODE *******
main_canvas.show()
shadow_canvas.hide()
main_canvas has big_plot initially
shadow_canvas has small_subplots initially
if big_plot in main_canvas:
SwapView(...) ---> should put big_plot in shadow_canvas and small_subplots in the main_canvas
else:
SwapView(...) ---> should put the small_subplots in shadow_canvas and the big_plot in main_canvas
UpdateDisplay()
******* END OF CODE *******
Here is my initial attempt at this code and unfortunately I can't find a way to find which figure is the one currently displayed.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import wx
import time
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
class myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,parent = None, id = -1, title = 'LoadFigure()', size = (800,800))
self.figurePanel = FigurePanel(parent = self)
canvas1 = self.figurePanel.canvas
canvas2 = self.figurePanel.enlarged_canvas
fig1 = self.figurePanel.enlarged_figure
fig2 = self.figurePanel.figure
fig1.set_canvas(canvas1) #enlarged_fig resides in canvas1
fig2.set_canvas(canvas2) #fig resides in canvas2
#Show both canvases ---> canvas2 will override canvas1, but when canvas2 hides canvas1 should show
canvas2.Show()
canvas1.Show()
self.Show()
print "Starting to swap displays!"
time.sleep(1)
for i in range(10):
print "run: %d"%i
self.SwapView(big_plot = fig1,small_plots = fig2,main_canvas = canvas1,shadow_canvas = canvas2)
time.sleep(1)
def SwapView(self,big_plot,small_plots,main_canvas,shadow_canvas):
'''
Keep swapping the main_canvas with the shadow_canvas to show either fig1 or fig2.
Initially, big_plot has main_canvas and small_plots have shadow_canvas
'''
wx.Yield()
print list(main_canvas)
print list(big_plot.get_children())
time.sleep(2)
for child in big_plot.get_children():
if child == main_canvas:
print 'big_plot has main_canvas'
big_plot.set_canvas(shadow_canvas)
small_plots.set_canvas(main_canvas)
main_canvas.draw()
wx.Yield()
main_canvas.Show()
else:
print 'big_plot has shadow_canvas'
for child in small_plots.get_children():
if child == main_canvas:
print 'small_plots has main_canvas'
small_plots.set_canvas(shadow_canvas)
big_plot.set_canvas(main_canvas)
main_canvas.draw()
wx.Yield()
main_canvas.Show()
else:
print 'small_plots has shadow_canvas'
class FigurePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figPanel = self
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.figure = Figure(figsize = (8,6.1), dpi =60)
self.ax = self.figure.add_subplot(1,1,1)
self.ax.plot([1,2,3],[1,2,3])
self.enlarged_figure = Figure(figsize = (8,6.1), dpi = 60)
self.ax1 = self.enlarged_figure.add_subplot(2,1,1)
self.ax2 = self.enlarged_figure.add_subplot(2,1,2)
self.ax1.plot([1,2,3],[1,4,9])
self.ax2.plot([1,2,3],[1,4,9])
self.canvas = FigureCanvas(self, -1, self.figure)
self.enlarged_canvas = FigureCanvas(self,-1,self.enlarged_figure)
self.Layout()
self.Fit()
if __name__ == "__main__":
app = wx.App(False)
fr = myframe()
app.MainLoop()
For anyone that might need it, here's the solution that I came up with:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import wx
import time
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
class myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,parent = None, id = -1, title = 'SWAP!', size = (480,390))
self.figurePanel = FigurePanel(parent = self)
self.canvas1 = self.figurePanel.canvas
self.canvas2 = self.figurePanel.enlarged_canvas
self.fig1 = self.figurePanel.enlarged_figure
self.fig2 = self.figurePanel.figure
self.fig1.set_canvas(self.canvas1) #enlarged_fig resides in canvas1
self.canvas1.Show()
self.Show()
self.canvas2.mpl_connect("button_release_event",self.OnLoadFigure) #Enable the detection of mouseclicks for the plots in the plotting window
print "Click anywhere on the figure to swap the plots!"
self.display = 1
def OnLoadFigure(self,event = None):
print "Tried to load figure"
if event != None:
self.display = self.SwapView(big_plot = self.fig1 ,small_plots = self.fig2 , display = self.display, main_canvas = self.canvas1 , shadow_canvas = 0)
def SwapView(self,big_plot = None,display = -1, small_plots = None,main_canvas = None,shadow_canvas = None):
'''
Keep swapping the main_canvas with the shadow_canvas to show either fig1 or fig2.
Initially, big_plot has main_canvas and small_plots have shadow_canvas
'''
wx.Yield()
print display
if display == 1: #Show the big plot
print 'big_plot showing'
big_plot.set_canvas(main_canvas)
main_canvas.Show()
time.sleep(0.01) #Fastest time you can pick
wx.Yield()
else:
print 'small_plots showing'
main_canvas.Hide()
wx.Yield()
self.Refresh(canvas = main_canvas)
display = not(display)
return display
def Refresh(self,canvas = None,figure = None):
wx.Yield()
if canvas != None:
print "draw"
canvas.draw()
self.Update()
self.figurePanel.Update()
wx.Yield()
class FigurePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figPanel = self
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.figure = Figure(figsize = (8,6.1), dpi =60)
self.ax = self.figure.add_subplot(1,1,1)
self.ax.plot([1,2,3],[1,2,3])
self.enlarged_figure = Figure(figsize = (8,6.1), dpi = 60)
self.ax1 = self.enlarged_figure.add_subplot(2,1,1)
self.ax2 = self.enlarged_figure.add_subplot(2,1,2)
self.ax1.plot([1,2,3],[1,4,9])
self.ax2.plot([1,2,3],[1,4,9])
self.canvas = FigureCanvas(self, -1, self.figure)
self.enlarged_canvas = FigureCanvas(self,-1,self.enlarged_figure)
self.Layout()
self.Fit()
if __name__ == "__main__":
app = wx.App(False)
fr = myframe()
app.MainLoop()
To make the display change, click on the figure.

Matplotlib Drag and Drop File

I'd like to be able to drag and drop a file on my Matplotlib plot, and do something with said file (like open and plot it!). Unfortunately, while my script handles a few event using:
fig.canvas.mpl_connect('button_release_event', self.btn_release)
and the like, I can't find anything that will return the path of a file dropped on it.
I'm not quite at the level where I want to create GUIs with embedded plots, so if there is a solution without doing that, I would prefer it.
You seem to wish a drop_file_event was handled by mpl_connect ; unfortunately this is not the case according to the documentation (and it is a case a bit specific for a generalist plotting library anyway).
However, it is not so difficult to implement your own GUI handling drop event. Here below an example mostly based on embedding_in_wx2 and adding a wx.FileDropTarget.
# Used to guarantee to use at least Wx2.8
import wxversion
wxversion.ensureMinimal('2.8')
import numpy as np
import wx
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
class MyFileDropTarget(wx.FileDropTarget):
def __init__(self, window):
wx.FileDropTarget.__init__(self)
self.window = window
def OnDropFiles(self, x, y, filenames):
fig = self.window.figure
inaxes = fig.get_axes()[0]
h_pix = int(fig.get_figheight() * fig.get_dpi()) # fig height in pixels
message = "%d file(s) dropped at (%d,%d):\n" % (len(filenames), x, y)
for file in filenames:
message += file + "\n"
inaxes.annotate(message, (x, h_pix-y), xycoords='figure pixels')
self.window.draw()
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.add_toolbar()
self.Fit()
win_target = self.canvas
dt = MyFileDropTarget(win_target)
win_target.SetDropTarget(dt)
def draw(self):
t = np.linspace(0.0, 2., 100)
s = np.sin(2 * np.pi * t)
self.axes.plot(t, s)
def add_toolbar(self):
self.toolbar = NavigationToolbar2Wx(self.canvas)
self.toolbar.Realize()
if wx.Platform == '__WXMAC__':
# Mac platform (OSX 10.3, MacPython) does not seem to cope with
# having a toolbar in a sizer. This work-around gets the buttons
# back, but at the expense of having the toolbar at the top
self.SetToolBar(self.toolbar)
else:
# On Windows platform, default window size is incorrect, so set
# toolbar width to figure width.
tw, th = self.toolbar.GetSizeTuple()
fw, fh = self.canvas.GetSizeTuple()
# By adding toolbar in sizer, we are able to put it at the bottom
# of the frame - so appearance is closer to GTK version.
# As noted above, doesn't work for Mac.
self.toolbar.SetSize(wx.Size(fw, th))
self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
# update the axes menu on the toolbar
self.toolbar.update()
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = wx.Frame(None, title='File drop test')
panel = CanvasPanel(frame)
panel.draw()
frame.Show()
app.MainLoop()

wxPython - graphing data

I have an issue with graphing data within wxPython - this code below works but it's not exactly right: For now I plot random numbers which are multiples of an entry box, this is done every 100ms.
My issue is that the entire history of numbers is shown as a pose is a (say) running window of 25 samples. I initially tried redrawing the graph on each collection of 100 samples, something like, if( length(data)%100 ): drawGraph but again this didn't look correct.
Thoughts and suggestions welcome.
My code:
print( "\n- Please Wait -- Importing Matplotlib and Related Modules...\n" )
import random
import matplotlib
import numpy
import wx
import u3
import numpy as np
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
from matplotlib.figure import Figure
class TemperaturePanel( wx.Panel ) :
def __init__( self, parent, position ) :
wx.Panel.__init__( self, parent, pos=position, size=(800,320) )
# initialize matplotlib
self.figure = matplotlib.figure.Figure( None, facecolor="white" )
self.canvas = matplotlib.backends.backend_wxagg.FigureCanvasWxAgg( self, -1, self.figure )
self.axes = self.figure.add_subplot(111)
self.axes.grid(True, color="gray")
self.axes.set_xbound( (0,5) )
self.axes.set_ybound( (3,80) )
self.axes.set_xlabel( "Minutes" )
self.axes.set_ylabel( "Temperature ($^\circ$C)" )
self.axes = self.figure.add_subplot(111)
self.axes.grid(True, color="gray")
self._SetSize()
self.Bind( wx.EVT_SIZE, self._SetSize )
self.TemperatureData = []
def updateTemperature(self, value):
self.TemperatureData.append( value )
length = len(self.TemperatureData)
x = np.arange( length )
y = np.array(self.TemperatureData)
yMin = round(min(y)) - 2
yMax = round(max(y)) + 2
self.axes.plot(x,y, "-k")
self.axes.set_ybound( (yMin,yMax) )
self.canvas = FigureCanvas(self, -1, self.figure)
#-----------------------------------------------------------------------------------
def _SetSize( self, event=None ):
pixels = self.GetSize()
self.SetSize( pixels )
self.canvas.SetSize( pixels )
dpi = self.figure.get_dpi()
self.figure.set_size_inches( float( pixels[0] ) / dpi,float( pixels[1] ) / dpi )
#------------------------------------------------------------------------------------
class MainWindow(wx.Frame):
def __init__(self, parent):
#wx.Frame.__init__(self, *args, **kwargs)
wx.Frame.__init__(self, parent, title="Graph Issue", size=(1000,600))
self.panel = wx.Panel(self)
self.spin = wx.SpinCtrl(self.panel)
self.button = wx.Button(self.panel, label="Update")
self.stop = wx.Button(self.panel, label="Stop")
self.sizer = wx.BoxSizer()
self.sizer.Add(self.spin)
self.sizer.Add(self.button)
self.sizer.Add(self.stop)
self.TemperatureGraph = TemperaturePanel( self, position=(20, 50) )
self.panel.SetSizerAndFit(self.sizer)
self.Show()
# Use EVT_CHAR_HOOK on Frame insted of wx.EVT_KEY_UP on SpinCtrl
# to disable "on Enter go to next widget" functionality
self.Bind(wx.EVT_CHAR_HOOK, self.OnKey)
self.button.Bind(wx.EVT_BUTTON, self.OnUpdate)
self.stop.Bind(wx.EVT_BUTTON, self.OnStop)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
self.Bind(wx.EVT_TIMER, self.updateTemperature, self.timer)
self.timer.Start(100)
self.value = 0
def OnKey(self, e):
if e.GetKeyCode() == wx.WXK_RETURN: # Is the key ENTER?
self.value = self.spin.GetValue() # Read SpinCtrl and set internal value
else: # Else let the event out of the handler
e.Skip()
def OnUpdate(self, e):
self.value = self.spin.GetValue() # Read SpinCtrl and set internal value
def OnTimer(self, e):
# Show internal value
print(self.value)
def updateTemperature(self, e):
Temperature = self.value*random.uniform(-1,1) # obtain currnt temperature
self.TemperatureGraph.updateTemperature(Temperature) # add temperature to graph
def OnStop(self, e):
self.timer.Stop()
self.Destroy()
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()
If I understood the question correctly, you need 25 last temperature values only to be shown in your graph instead of all history of values. If that's what you want, then in the updateTemperature subroutine only the last 25 values should be plotted:
if length < 25:
x = np.arange(length)
y = np.array(self.TemperatureData)
else:
x = np.arange(length-25, length)
y = np.array(self.TemperatureData)[-25:]
To make the plot look better, x axis can be adjusted the same way you do it with y axis:
xMin = 0 if length < 25 else length-25
xMax = 25 if length < 25 else length
self.axes.set_xbound( (xMin,xMax) )
If the plot looks OK to you, and the issue is about the memory leak that causes the graph to freeze after ~200 iterations, that's due to the creation of FigureCanvas on every temperature update. Instead, you can re-use your existing FigureCanvas, changing the last line of updateTemperature to
self.canvas.draw()

Adding a gridded subplot to WxPython GUI (Matplotlib)

I'm developing a simple demonstration GUI for a multi-lateral ultrasonic rangefinder. A device captures ultrasonic signals from several different sensors, performs time of flight processing on these signals, then uses the range information of detected targets to localize an object in 2D cartesian coordinates.
I'd like to create a wxPython GUI, which uses matplotlib to display each of the sensors signals and plot the target locations. I've found some example code which has roughly the same layout that I'd like to have, and have removed some of the code's unnecessary components. The python code is below:
import sys,os,csv
import numpy as N
import wx
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
class FinalProject(wx.Frame):
title = ' Ultrasound Demo '
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title)
self.create_menu()
self.create_status_bar()
self.create_main_panel()
def create_menu(self):
self.menubar = wx.MenuBar()
menu_file = wx.Menu()
m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file")
self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt)
menu_file.AppendSeparator()
m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit")
self.Bind(wx.EVT_MENU, self.on_exit, m_exit)
self.menubar.Append(menu_file, "&File")
self.SetMenuBar(self.menubar)
def create_main_panel(self):
self.panel = wx.Panel(self)
self.dpi = 100
self.fig = Figure((9.5, 5.0), dpi=self.dpi)
self.canvas = FigCanvas(self.panel, -1, self.fig)
# self.axes1 = self.fig.add_subplot2grid((2,2), (0,0))
self.axes1 = self.fig.add_subplot(2,1,1)
# self.axes2 = self.fig.add_subplot2grid((2,2), (1,0))
self.axes2 = self.fig.add_subplot(2,1,2)
self.toolbar = NavigationToolbar(self.canvas)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.vbox.AddSpacer(10)
self.hbox = wx.BoxSizer(wx.HORIZONTAL)
flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def create_status_bar(self):
self.statusbar = self.CreateStatusBar()
def on_draw_button1(self, event):
self.axes1.clear()
self.axes2.clear()
i = N.arange(0,4,1)
q = i
w = N.arange(-4,0,1)
self.axes1.plot(q,i,'red')
self.axes2.plot(w,i,'yellow')
self.canvas.draw()
def on_draw_button2(self, event):
self.axes1.clear()
self.axes2.clear()
a = [0,1,2,3,4,]
b = [5.5,4.5,3.5,2.5,1.5]
c = [7.5,2.5,4,6.8,10.6]
self.axes1.plot(b,a,'purple')
self.axes2.plot(c,a,'black')
self.canvas.draw()
def on_save_plot(self, event):
file_choices = "PNG (*.png)|*.png"
dlg = wx.FileDialog(
self,
message="Save plot as...",
defaultDir=os.getcwd(),
defaultFile="plot.png",
wildcard=file_choices,
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.canvas.print_figure(path, dpi=self.dpi)
self.flash_status_message("Saved to %s" % path)
def on_exit(self, event):
self.Destroy()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = FinalProject()
app.frame.Show()
app.MainLoop()
del app
So, this code would be useful for displaying two captured signals, and it's relatively easy to add additional subplots, provided they exist on a symmetrical grid, but what I would like to do is add a third subplot to the right of these two plots which spans the height of both.
To be clear what I would like to do is add a third subplot to the right of the existing plots the height of which is equal to the current canvas height.
Reading through the matplotlib documentation it looks like the appropriate function to do this would be plt.subplot2grid(). Unfortunately I get errors when I attempt to use this function to define the current subplots (the code I attempted to use is commented out below:
self.axes1 = self.fig.add_subplot2grid((2,2), (0,0)))
Has anyone else attempted this? Any ideas on where I might be going wrong?
Thanks!
Sounds like you have already figured out something to do what you wanted.
To be explicit, there is nothing specific about the fact that you are using WX python in this example - essentially the question boils down to how one would go about adding an axes to a figure which has the same height as two other axes.
This can be done very simply with:
import matplotlib.pyplot as plt
ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 3)
ax3 = plt.subplot(1, 2, 2)
plt.show()
HTH

Categories

Resources