saving figure using matplotlib - python

I have multiple plots each displayed in each tab(each tab is a page in wx.Notebook). I have attached the code snippet used to plot the figure in each tab. Everything is fine till I try to save a figure using the option in navigation toolbar. When I try to save a figure, the figure in that plot gets messed up and the tab looks like the image attached. How can I fix this issue?
Any help is highly appreciated. Thanks.
self.fig = plt.figure(figsize=(hor_pix, ver_pix), dpi=self.dpi)
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.toolbar = NavigationToolbar(self.canvas)
line_ico = wx.Image('line.bmp', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.line_button = self.toolbar.AddSimpleTool(wx.ID_ANY, line_ico, "Line", "Extract using line Marker")
paste_ico = wx.Image('paste2.bmp', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
self.paste_button = self.toolbar.AddSimpleTool(wx.ID_ANY, paste_ico, "Paste gates", "Copy gates from other plots.")
self.toolbar.Realize()
self.SetSizer(self.vbox)
self.vbox.Fit(self)
self.axes.hist(datam3,bins,color='b',ec='b',fc='b')
self.canvas.draw()
self.Refresh()
self.Update()

Related

How to refresh wx.Panel correctly?

I am creating a image viewer using wxPython. I want to view multiple images individually, so I wrote a code below (partially).
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figs = []
self.axes = []
self.canvases = []
self.panelsizers = []
def draw(self, data):
"""data is 2D numpy array"""
fig = Figure()
self.figs.append(fig)
ax = fig.add_subplot(111)
ax.imshow(data, interpolation='none')
self.axes.append(ax)
canvas = FigureCanvas(self, wx.ID_ANY, fig)
self.canvases.append(canvas)
panelsizer = wx.BoxSizer()
panelsizer.Add(canvas, 1, wx.GROW)
self.panelsizers.append(panelsizer)
self.SetSizer(panelsizer)
This works almost perfectly except a slight problem.
When I ran the code and open a image, the window looks like below.
enter image description here
This window consists of three wx.Panels and the center one is CanvasPanel. You can see that the size of CanvasPanel is a bit small even though proportion=1 and style=wx.GROW. Moreover, when I resize this window by dragging the corner of window, it looks like below.
enter image description here
The size of CanvasPanel changes correctly! Why? And how can I revise my code to fit the CanvasPanel in the viewer without resizing.

Add a Grid in a matplotlib plot between different classes using a button

I want to connect a QPushButton in my QMainWindow with a class that i created using Matplotlib so i can show a grid when i push the button. This is a part of the code:
class Ventana(QMainWindow):
def __init__(self):
super(QMainWindow, self).__init__()
self.channel = ViewWidget()
#I add a toolbar and i put the button in here
self.toolbar2.addWidget(self.btn_showgrid)
self.btn_showgrid.setEnabled(True)
self.connect(self.btn_showgrid, SIGNAL("clicked()"), self.showGrid)
def showGrid(self):
self.btn_showgrid.setEnabled(False)
self.channel.axes.grid(True)
class ViewWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.mainWidget = QWidget()
self.setCentralWidget(self.mainWidget)
layout = QVBoxLayout()
self.mainWidget.setLayout(layout)
self.figure_canvas = FigureCanvas(Figure())
layout.addWidget(self.figure_canvas, 10)
self.axes = self.figure_canvas.figure.add_subplot(111)
x = np.arange(0,5,0.1)
y = np.sin(x)
self.axes.plot(x,y)
My method called showGrid set the button to "disable" (is what i want to when the button is pushed) but it does not shows the grid. What am i doing wrong?
Hope you can help me. Thanks for your answers
------------------------- EDIT --------------------------------
I´ve made a few changes. I created the QPushButton and i added it to the toolbar of the plot.
# create a simple widget to extend the navigation toolbar
self.btn_showgrid = QPushButton("Show Grid")
self.btn_showgrid.setEnabled(True)
self.btn_hidegrid = QPushButton("Hide Grid")
self.btn_hidegrid.setEnabled(False)
layout = QVBoxLayout()
self.mainWidget.setLayout(layout)
self.figure_canvas = FigureCanvas(Figure())
layout.addWidget(self.figure_canvas, 10)
self.axes = self.figure_canvas.figure.add_subplot(111)
self.axes.grid(False)
x = np.arange(0,5,0.1)
y = np.sin(x)
self.axes.plot(x,y)
I also put a line: self.axes.grid(False) as you can see above. And at last i created this method:
def showGrid(self):
self.btn_showgrid.setEnabled(False)
self.btn_hidegrid.setEnabled(True)
self.axes.grid(True)
self.axes.draw()
The problem now is that when i push the button, it only hides like it should, but the grid does no show. But if create a new plot in the same `QMainWindow, it works!!!!
I think i need to refresh the plot at the moment i make self.axes.grid(True), but the draw() does not work. How can i accomplish this? I mean, refresh the plot?
You need to tell the canvas to re-draw. Drawing can be expensive so updating the state of the artists does not trigger a re-draw (the pyplot API does do that but you should not use that here). I think
def showGrid(self):
self.btn_showgrid.setEnabled(False)
self.channel.axes.grid(True)
self.channel.canvas.draw_idle()
will do the trick. The call to draw_idle tells Qt to, the next time it re-paints window to also trigger an Agg redraw for mpl, and to please schedule a repaint 'soon'.

Dynamically update graphics in matplotlib

# when the code works but it's meaningless to include it
### When I can't get this part to work and I'd need your code
How do you hide or show an Axes object (subplot) in matplotlib so you can toggle between different Axes in the same figure?
I'm using matplotlib to display graphics in a Tkinter GUI and I'd like to use radiobuttons to switch between different axes in the same figure.
Basically I'll have some radiobuttons linked to a IntVar():
graphic_version = tk.IntVar()
tk.Radiobutton(root, text='Option1', variable=graphic_version, value=1).pack()
tk.Radiobutton(root, text='Option2', variable=graphic_version, value=2).pack()
Then I'd trace the IntVar() with a custom methods updating my figure with the requested graphic:
choice.trace("w", lambda choice: myGraphic.showGraphic(version))
so that everytime the user clicks a radiobutton the figure is updated with a different version of the plot. Now the problem is I have no idea how to do the showGraphic properly. Lets say I use this class system to get 2 different versions of plotting the same data:
class Version1():
def __init__(self, ax, data):
self.ax = ax #This is a Axes object
self.data = self._formatDataV1(data)
self._draw()
self._setOptions()
self.hide()
def _formatDataV1(self, data):
#Here I manipulate the raw data to extract the info I need for this version
#Just a bunch of math algorithms it works fine
def _setOptions(self):
#Here I can overwrite or change settings specific for this version
def _draw(self):
self.ax.bar(self.data[0], self.data[1], width=1, color='red')
self._setOptions()
def hide(self):
###How do I remove the ax without affecting the figure?
def show(self):
###If I want to see this version again I don't want the cost of redrawing
class Version2():
def __init__(self, ax, data):
self.ax = ax #This is a Axes object
self.data = self._formatDataV1(data)
self._draw()
self._setOptions()
self.hide()
def _formatDataV2(self, data):
#The data is manipulated differently here to extract new information
def _setOptions(self):
#These options are specific to the version2 to get the display right
def _draw(self): #Drawing a different version of the graphic with differently formated data
self.ax.plot(self.data[0], self.data[1])
self._setOptions()
def hide(self):
###How do I remove the ax without affecting the figure?
def show(self):
###If I want to see this version again I don't want the cost of redrawing
class MyGraphic(tk.LabelFrame):
def __init__(self, root, data, **options):
#I use the labelframe only as a container to make things pretty
tk.LabelFrame.__init__(self, root, text="My 1337 graphic : ", **options)
self.data = data
self.fig = mpl.figure.Figure()
self.ax = self.fig.add_subplot(111)
self._drawCanvas() #This is just for Tkinter compatibility
self.my_versions = {}
self.my_versions.update({'v1' : Version1(self.ax, self.data)})
self.my_versions.update({'v2' : Version2(self.ax, self.data)})
def _drawCanvas(self):
self.canvas = FigureCanvasTkAgg(self.figure, master=self)
self.canvas.show()
self.canvas.get_tk_widget().grid()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def _setOptions(self, **kwargs):
#Here I can set options common to both versions of the graphic
def showGraphic(self, graphic_version):
for i, item in enumerate(self.my_versions):
item.hide()
if graphic_version == 1:
self.my_versions['v1'].show()
elif graphic_version == 2:
self.my_versions['v2'].show()
self._setOptions()
Sorry for the lengthy post but I rather include too many details and edit out those who are not necessary when it's solved.
Basically I want to be able to hide and show different ax on the same figure depending on the choice made by my user. The missing parts of the puzzle are myGraphic.show() and myGraphic.hide().
I'm also a complete matplotlib newby I tried this design because it seemed clear and easy to implement additional versions when needed but design inputs are also really appreciated.
You can remove axes from a figure with figure.delaxes (and add with add_axes):
http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure.delaxes
I managed to solve it with figure.clear() and figure.add_axes(ax)
I'll try to edit a clean question-answer tomorrow when I have time with a minimal example of how to toggle different version of a plot on the same figure in Tkinter.

wxpython and updating matplotlib figure in panel

I am using a wx gui with basemap and matplotlib. I have two panels (left/right). I plot a map of the US in my right panel. This occurs no problem with the code below:
# Setup the Map for Display
self.figure = Figure(None,dpi=75)
self.canvas = FigureCanvas(self.MapPage, -1, self.figure)
self.axes = self.figure.add_axes([0,0,1,1],frameon=False)
self.SetColor( (255,255,255) )
#Sizer for Map Page Right Panel (Map Display)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL)
self.MapPage.SetSizer(sizer)
# Plot the Map
self.map = Basemap(llcrnrlon=-119, llcrnrlat=22, urcrnrlon=-64,
urcrnrlat=49, projection='lcc', lat_1=33, lat_2=45,
lon_0=-95, resolution='i', area_thresh=10000,ax=self.axes)
self.map.drawcoastlines()
self.map.drawcountries()
self.map.drawstates()
This works like a charm. However, eventually later in the code I would like to change the map to maybe only show a region. This is where I run into issues. Currently, I am doing this:
# Clear the previous figure
self.figure.clf()
self.figure.canvas.draw()
# Setup for the NEW Map to Display
self.figure = Figure(None,dpi=75)
self.canvas = FigureCanvas(self.MapPage, -1, self.figure)
self.axes = self.figure.add_axes([0,0,1,1],frameon=False)
self.SetColor( (255,255,255) )
#Sizer for NEW Map Page Right Panel (Map Display)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.EXPAND | wx.ALL)
self.MapPage.SetSizer(sizer)
# Plot the NEW Map
self.map = Basemap(llcrnrlon=newlong1, llcrnrlat=newlat1, urcrnrlon=newlong2,
urcrnrlat=newlat2, projection='lcc', lat_0=maplat,
lon_0=maplon, resolution='i', area_thresh=10000,ax=self.axes)
self.map.drawcoastlines()
self.map.drawcountries()
self.map.drawstates()
This code manages to clear my right panel and display the new map; HOWEVER, this new map is only in the top left corner of the panel. It no longer stretches across the entire panel. Any ideas on why this is? Or is there a better way for me to clear the Basemap figure to plot a new one in the same panel?
Any help would be appreciated! Thanks!

How do I draw a colorbar in a gtk/glade window embedded matplotlib figure?

I am currently working on a gtk/python front-end to a simulation tool. I've got everything working as planned, with one exception- I can't figure out how to draw the colorbar associated with the plot I am displaying. I can get the colorbar to display just fine if I don't embed my plot in a GTK window, but as soon as I embed it, the colorbar disappears.
The relevant rendering code (within my python gui object) is below (the init and render_domain class functions). I am trying to render the colorbar, 3-lines up from the bottom of the render_domain function. The colormap is working fine (my data in the mesh is properly colored), it is just getting the actual colorbar to appear that is the issue. Thoughts?
class AppGui(object):
def __init__(self):
#### --- SET UP GTK INTERFACE FROM GLADE FILE --- ####
gladefile = "py_FD2D.glade"
self.windowname = "main_window"
self.builder = gtk.Builder()
self.builder.add_from_file(gladefile)
#### --- SET UP GUI SIGNALS --- ####
self.window = self.builder.get_object(self.windowname)
dic = {
"on_main_window_destroy" : self.quit,
"on_run_simulation_button_clicked" : self.run_simulation,
"on_play_button_clicked" : self.play_animation,
"on_play_slider_value_changed" : self.slider_changed,
"on_cap_pres_yn_changed" : self.cap_pres_menu_changed,
}
self.builder.connect_signals(dic)
#### --- SET UP FIGURE IN GTK WINDOW --- ####
self.figure = matplotlib.figure.Figure(figsize=(6,4), dpi=72)
self.axis = self.figure.add_subplot(1,1,1)
self.canvas = FigureCanvas(self.figure)
self.canvas.show()
self.toolbar = NavigationToolbar(self.canvas, self.window)
# Place the figure in the plot_holder pane of the py_FD2D.glade file
self.graphview = self.builder.get_object("plot_holder")
self.graphview.pack_start(self.canvas, True, True)
self.graphview.pack_start(self.toolbar, False, False)
self.FD = gcsmt.FD2D() #instantiate the simulator
self.update_all() #update all simulation parameters from gtk GUI fields
self.render_domain() #render the initial state of the plot
self.builder.get_object(self.windowname).show()
self.stop_sim = False
def render_domain(self):
self.axis.clear()
self.axis.set_xlabel('Formation Width (meters)')
self.axis.set_ylabel('Formation Thickness (meters)')
self.axis.set_title('Formation Saturation')
self.msh = matplotlib.collections.QuadMesh(self.x_cells, self.y_cells, self.verts, True)
if self.FD.HasRun()==False:
self.msh.set_array(np.zeros(self.x_cells*self.y_cells))
else:
self.msh.set_array(np.array(self.FD.GetTimestepData(0)))
self.msh.set_clim(0.0, 1.0)
self.cax = self.axis.add_collection(self.msh)
self.axis.axis([0, self.x_max, 0, self.y_top])
plt.colorbar(self.cax) ###!!! I have tried self.cax and self.msh, and various combos
self.toolbar.show()
self.canvas.draw()

Categories