""" This demo demonstrates how to embed a matplotlib (mpl) plot into a wxPython GUI application, including: * Using the navigation toolbar * Adding data to the plot * Dynamically modifying the plot's properties * Processing mpl events * Saving the plot to a file from a menu The main goal is to serve as a basis for developing rich wx GUI applications featuring mpl plots (using the mpl OO API). Eli Bendersky (eliben@gmail.com) License: this code is in the public domain Last modified: 30.07.2008 """ import os import pprint import random import wx # The recommended way to use wx with mpl is with the WXAgg # backend. # import matplotlib matplotlib.use('WXAgg') from matplotlib.figure import Figure from matplotlib.backends.backend_wxagg import \ FigureCanvasWxAgg as FigCanvas, \ NavigationToolbar2WxAgg as NavigationToolbar class BarsFrame(wx.Frame): """ The main frame of the application """ title = 'Demo: wxPython with matplotlib' def __init__(self): wx.Frame.__init__(self, None, -1, self.title) self.data = [5, 6, 9, 14] self.create_menu() self.create_status_bar() self.create_main_panel() self.textbox.SetValue(' '.join(map(str, self.data))) self.draw_figure() 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) menu_help = wx.Menu() m_about = menu_help.Append(-1, "&About\tF1", "About the demo") self.Bind(wx.EVT_MENU, self.on_about, m_about) self.menubar.Append(menu_file, "&File") self.menubar.Append(menu_help, "&Help") self.SetMenuBar(self.menubar) def create_main_panel(self): """ Creates the main panel with all the controls on it: * mpl canvas * mpl navigation toolbar * Control panel for interaction """ self.panel = wx.Panel(self) # Create the mpl Figure and FigCanvas objects. # 5x4 inches, 100 dots-per-inch # self.dpi = 100 self.fig = Figure((5.0, 4.0), dpi=self.dpi) self.canvas = FigCanvas(self.panel, -1, self.fig) # Since we have only one plot, we can use add_axes # instead of add_subplot, but then the subplot # configuration tool in the navigation toolbar wouldn't # work. # self.axes = self.fig.add_subplot(111) # Bind the 'pick' event for clicking on one of the bars # self.canvas.mpl_connect('pick_event', self.on_pick) self.textbox = wx.TextCtrl( self.panel, size=(200,-1), style=wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter, self.textbox) self.drawbutton = wx.Button(self.panel, -1, "Draw!") self.Bind(wx.EVT_BUTTON, self.on_draw_button, self.drawbutton) self.cb_grid = wx.CheckBox(self.panel, -1, "Show Grid", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid) self.slider_label = wx.StaticText(self.panel, -1, "Bar width (%): ") self.slider_width = wx.Slider(self.panel, -1, value=20, minValue=1, maxValue=100, style=wx.SL_AUTOTICKS | wx.SL_LABELS) self.slider_width.SetTickFreq(10, 1) self.Bind(wx.EVT_COMMAND_SCROLL_THUMBTRACK, self.on_slider_width, self.slider_width) # Create the navigation toolbar, tied to the canvas # self.toolbar = NavigationToolbar(self.canvas) # # Layout with box sizers # self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.toolbar, 0, wx.EXPAND) self.vbox.AddSpacer(10) self.hbox = wx.BoxSizer(wx.HORIZONTAL) flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL self.hbox.Add(self.textbox, 0, border=3, flag=flags) self.hbox.Add(self.drawbutton, 0, border=3, flag=flags) self.hbox.Add(self.cb_grid, 0, border=3, flag=flags) self.hbox.AddSpacer(30) self.hbox.Add(self.slider_label, 0, flag=flags) self.hbox.Add(self.slider_width, 0, border=3, flag=flags) self.vbox.Add(self.hbox, 0, flag = wx.ALIGN_LEFT | wx.TOP) self.panel.SetSizer(self.vbox) self.vbox.Fit(self) def create_status_bar(self): self.statusbar = self.CreateStatusBar() def draw_figure(self): """ Redraws the figure """ str = self.textbox.GetValue() self.data = map(int, str.split()) x = range(len(self.data)) # clear the axes and redraw the plot anew # self.axes.clear() self.axes.grid(self.cb_grid.IsChecked()) self.axes.bar( left=x, height=self.data, width=self.slider_width.GetValue() / 100.0, align='center', alpha=0.44, picker=5) self.canvas.draw() def on_cb_grid(self, event): self.draw_figure() def on_slider_width(self, event): self.draw_figure() def on_draw_button(self, event): self.draw_figure() def on_pick(self, event): # The event received here is of the type # matplotlib.backend_bases.PickEvent # # It carries lots of information, of which we're using # only a small amount here. # box_points = event.artist.get_bbox().get_points() msg = "You've clicked on a bar with coords:\n %s" % box_points dlg = wx.MessageDialog( self, msg, "Click!", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def on_text_enter(self, event): self.draw_figure() 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() def on_about(self, event): msg = """ A demo using wxPython with matplotlib: * Use the matplotlib navigation bar * Add values to the text box and press Enter (or click "Draw!") * Show or hide the grid * Drag the slider to modify the width of the bars * Save the plot to a file using the File menu * Click on a bar to receive an informative message """ dlg = wx.MessageDialog(self, msg, "About", wx.OK) dlg.ShowModal() dlg.Destroy() def flash_status_message(self, msg, flash_len_ms=1500): self.statusbar.SetStatusText(msg) self.timeroff = wx.Timer(self) self.Bind( wx.EVT_TIMER, self.on_flash_status_off, self.timeroff) self.timeroff.Start(flash_len_ms, oneShot=True) def on_flash_status_off(self, event): self.statusbar.SetStatusText('') if __name__ == '__main__': app = wx.PySimpleApp() app.frame = BarsFrame() app.frame.Show() app.MainLoop()