Seaborn disable plot - python

I have a seaborn barplot embedded in a WxPython panel, like this:
The bar plot is drawn when the (big) button is clicked. This is what I made to accomplish it:
class SamplePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = Figure()
self.ax = self.figure.add_subplot(111)
self.x = np.array(list('XYZV'))
self.y = np.array([200,400,300,20])
self.ax.set_ylabel("Sample numbers")
self.canvas = FigureCanvas(self, -1, self.figure)
self.button = wx.Button(self, label="Plot data", pos=(100,15))
self.button.Bind(wx.EVT_BUTTON, self.OnButtonClick)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.sizer.Add(self.button, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
def OnButtonClick(self,event):
sns.barplot(self.x, self.y, palette="BuGn_d", ax=self.ax)
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = wx.Frame(None, title='Sample bar plot')
panel = SamplePanel(frame)
frame.Show()
app.MainLoop()
I have two questions:
How can I disable/draw the plot when I click the button? That is, if I click the button then the plot appears. If I click again the plot disappears and I get back to an empty original view, like this:
Also, the plot only changes when I maximize the window, how can I change it to be immediate, as soon as I click the button?
Any suggestions? Thanks in advance

It would have helped a lot if you had posted a small runnable example. Fortunately, Google helped me figure out what all was needed. Basically you need to set some kind of variable to keep track of if you've clicked the button or not. Or you could use a wx.ToggleButton instead of a regular wx.Button.
To get the graph to display without resizing the frame, you just need to call self.Layout().
To clear the figure, you'll need to do something like self.ax.cla() or self.ax.clear(). Here's a full example that worked for me:
import numpy as np
import seaborn as sns
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class SamplePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.toggled = False
self.figure = Figure()
self.ax = self.figure.add_subplot(111)
self.x = np.array(list('XYZV'))
self.y = np.array([200,400,300,20])
self.ax.set_ylabel("Sample numbers")
self.canvas = FigureCanvas(self, -1, self.figure)
self.button = wx.Button(self, label="Plot data", pos=(100,15))
self.button.Bind(wx.EVT_BUTTON, self.OnButtonClick)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.sizer.Add(self.button, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
def OnButtonClick(self, event):
if not self.toggled:
sns.barplot(self.x, self.y, palette="BuGn_d", ax=self.ax)
self.toggled = True
else:
self.ax.cla()
self.toggled = False
self.Layout()
if __name__ == "__main__":
app = wx.App(False)
frame = wx.Frame(None, title='Sample bar plot', size=(800,600))
panel = SamplePanel(frame)
frame.Show()
app.MainLoop()
Also note that wx.PySimpleApp is deprecated. I swapped it out for the recommended method of creating an app object.

Related

matplotlib Embedded in wxPython with Navigation Toolbar Coordinates

I've come across and implemented a few scripts with matplotlib figures embedded in a wxPython panel. The embedding of the actual plot is fine but when I add a navigation toolbar NavigationToolbar2WxAgg much of the toolbar functionality is lost. I can pan and zoom, but there are no coordinates displayed, and the default shortcut keys do not work. The same behavior occurs in embedding_in_wx4_sgskip.py from the example/user_interfaces folder for matplotlib:
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
import matplotlib as mpl
import wx
import wx.lib.mixins.inspection as WIT
class CanvasFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1,
'CanvasFrame', size=(550, 350))
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
t = np.arange(0.0, 3.0, 0.01)
s = np.sin(2 * np.pi * t)
self.axes.fmt_xdata = lambda x: "{0:f}".format(x)
self.axes.fmt_ydata = lambda x: "{0:f}".format(x)
self.axes.plot(t, s)
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.EXPAND)
self.SetSizer(self.sizer)
self.Fit()
self.add_toolbar() # comment this out for no toolbar
def add_toolbar(self):
self.toolbar = NavigationToolbar(self.canvas)
self.toolbar.Realize()
# By adding toolbar in sizer, we are able to put it at the bottom
# of the frame - so appearance is closer to GTK version.
self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND)
# update the axes menu on the toolbar
self.toolbar.update()
# alternatively you could use
#class App(wx.App):
class App(WIT.InspectableApp):
def OnInit(self):
'Create the main window and insert the custom frame'
self.Init()
frame = CanvasFrame()
frame.Show(True)
return True
app = App(0)
app.MainLoop()
How do I restore or add this functionality to my navigation bar?
You can put this code here:
#Create 'Position Display'
self.Text = wx.StaticText( self, wx.ID_ANY, u" Available Channels ", wx.DefaultPosition, wx.DefaultSize, 0 )
self.Text.Wrap( -1 )
mouseMoveID = self.canvas.mpl_connect('motion_notify_event',
self.onMotion)
Before you create your sizer. Then add this to the end of your init definition:
self.sizer.Add(self.Text,0, wx.LEFT | wx.EXPAND)
Finally, add this function to capture mouse movement on the frame:
def onMotion(self, evt):
"""This is a bind event for the mouse moving on the MatPlotLib graph
screen. It will give the x,y coordinates of the mouse pointer.
"""
xdata = evt.xdata
ydata = evt.ydata
try:
x = round(xdata,4)
y = round(ydata,4)
except:
x = ""
y = ""
self.Text.SetLabelText("%s (s), %s" % (x,y))
This is what worked for me, good luck!

Closing wxpython panel with Seaborn plot embedded

I have a seaborn plot embbeded in a wxPython panel, like this:
This is the code I made to accomplish this:
import numpy as np
import seaborn as sns
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import wx
class SimplePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
sns.set(style="whitegrid", palette="pastel", color_codes=True)
self.figure = Figure()
self.ax = self.figure.add_subplot(111)
self.planets = sns.load_dataset("planets")
self.years = np.arange(2010, 2014)
sns.factorplot(x="year", ax= self.ax,data=self.planets, kind="count",palette="BuPu", size=6, aspect=1.5, order=self.years)
sns.despine(left=True)
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.Fit()
if __name__ == "__main__":
app = wx.App(False)
fr = wx.Frame(None, title='test', size=(800,600))
panel = SimplePanel(fr)
fr.Show()
app.MainLoop()
Problem: it works fine, except when I close the window the program doesn't terminate. I think it has to do with the seaborn plot because I've run the program without it and it closes normally. But I don't know how to fix it. I've tried also to had a close functionality to the window (below self.Fit() line) like this:
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnCloseWindow(self,event):
self.Destroy()
But it doesn't work either.
Any suggestions?
Erase the seaborn plot object before destroying the window.

Matplotlib/wxpython: co-ordinate values not showing up in status bar

I have a simple matplotlib plot integrated with wxPython, and for some reason the x/y co-ordinates are not showing up under the status bar (the co-ordinates that show when you move the mouse), they used to appear but cannot get it to happen anymore. Any ideas why this may be possible? (I've also tried setting the format_coord attribute manually but that doesn't seem to work).
I think it's wx doing something funny, since if I just create a matplotlib plot in a new Python shell it works fine. Example code is below:
#Subclassing WX Panel Class to be able to integrate matplotlib into it
class p1(wx.Panel):
def __init__(self, parent, frame):
#Initialize WX Panel
wx.Panel.__init__(self, parent, -1, size=(50,50))
#Set up Figure/Canvas
self.frame = frame
self.figure = Figure()
self.canvas = FigureCanvas(self, -1, self.figure)
#Set up Matplotlib Toolbar
self.chart_toolbar = NavigationToolbar2Wx(self.canvas)
tw,th = self.chart_toolbar.GetSizeTuple()
fw,fh = self.canvas.GetSizeTuple()
self.chart_toolbar.SetSize(wx.Size(fw, th))
self.chart_toolbar.Realize()
graphs_sizer = wx.BoxSizer(wx.VERTICAL)
graphs_sizer.Add(self.canvas, 20, flag=wx.EXPAND, border=5)
graphs_sizer.Add(self.chart_toolbar, 1, flag=wx.ALIGN_CENTER, border=5)
self.SetSizer(graphs_sizer)
def plot(self):
self.axs1 = self.figure.add_subplot(1,1,1)
self.axs1.plot([1,2,3,4,5],color='blue')
class TestFrame(wx.Frame):
def __init__(self, parent, title):
#Initialize WX Frame
wx.Frame.__init__(self, parent, title=title, size=(1000,800))
#Create Splitter Window and Add Left/Right Panels
self.splitterWindow = wx.SplitterWindow(self)
self.panel1 = p1(self.splitterWindow, self)
self.panel2 = wx.Panel(self.splitterWindow)
self.splitterWindow.SplitVertically(self.panel1, self.panel2, 700)
#Create Status Bar
self.statusbar = self.CreateStatusBar()
#Plot
self.panel1.plot()
app = wx.App(redirect=False)
frame = TestFrame(None, "Test")
frame.Show(True)
app.MainLoop()
whereas something as simple as this works fine:
plt.plot([1,2,3,4,5])
plt.show()
It is easier for people to help if you make the code complete, i.e. with the imports in your case.
Note that you don't need the "SetSize" stuff for the toolbar, IIRC that was only needed with some wxPython versions on MAC.
To get the mouse coordinates you need to connect an event, that might be done out of the box with plt.plot.
Doc for the events is here: http://matplotlib.org/users/event_handling.html
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
#Subclassing WX Panel Class to be able to integrate matplotlib into it
class p1(wx.Panel):
def __init__(self, parent, frame):
#Initialize WX Panel
wx.Panel.__init__(self, parent, -1, size=(50,50))
#Set up Figure/Canvas
self.frame = frame
self.figure = Figure()
self.canvas = FigureCanvas(self, -1, self.figure)
#Set up Matplotlib Toolbar
self.chart_toolbar = NavigationToolbar2Wx(self.canvas)
self.chart_toolbar.Realize()
graphs_sizer = wx.BoxSizer(wx.VERTICAL)
graphs_sizer.Add(self.canvas, 20, flag=wx.EXPAND, border=5)
graphs_sizer.Add(self.chart_toolbar, 1, flag=wx.ALIGN_CENTER, border=5)
self.SetSizer(graphs_sizer)
def plot(self):
self.axs1 = self.figure.add_subplot(1,1,1)
self.axs1.plot([1,2,3,4,5],color='blue')
class TestFrame(wx.Frame):
def __init__(self, parent, title):
#Initialize WX Frame
wx.Frame.__init__(self, parent, title=title, size=(1000,800))
#Create Splitter Window and Add Left/Right Panels
self.splitterWindow = wx.SplitterWindow(self)
self.panel1 = p1(self.splitterWindow, self)
self.panel2 = wx.Panel(self.splitterWindow)
self.splitterWindow.SplitVertically(self.panel1, self.panel2, 700)
#Create Status Bar
self.statusbar = self.CreateStatusBar()
#Plot
self.panel1.plot()
mouseMoveID = self.panel1.canvas.mpl_connect('motion_notify_event',
self.onMotion)
def onMotion(self, evt):
x = evt.x
y = evt.y
inaxes = evt.inaxes
xdata = evt.xdata
ydata = evt.ydata
self.statusbar.SetStatusText("%s, %s, %s, %s, %s" % (
x, y, inaxes, xdata, ydata))
app = wx.App(redirect=False)
frame = TestFrame(None, "Test")
frame.Show(True)
app.MainLoop()

Python/Matplotlib - Quickly Updating Text on Axes

I have a matplotlib figure/canvas in a wxpython window. I want to update some information on the plot as the mouse moves around. I've connected to 'motion_notify_event' to get this information.
In the code below, a lot of random data is plotted and then the x,y location of the cursor is displayed in the statusbar of the window. This is very smooth and works well. However, I really want to display this information at the top of the plot. The behavior I want is shown if you uncomment the last two lines of cbUpdateCursor. However, when this is done, the response time to moving the cursor is terribly slow (because draw gets called and there is a lot of data, but draw must be called or the text doesn't get updated).
How can I speed this up so the cursor position can be displayed on the plot, but not slow it down so much? I think I might need to do something with bbox?
Code:
import wx
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.widgets import Cursor
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
class wxPlotting(wx.Frame):
title = 'Test'
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title)
self.time = np.arange(10000)
self.data = np.random.random(10000)
self.sb = self.CreateStatusBar()
self.create_main_panel()
self.axes.plot(self.time, self.data)
self.canvas.draw()
def create_main_panel(self):
self.panel = wx.Panel(self)
self.fig = Figure((5.0, 4.0), dpi=100)
self.canvas = FigCanvas(self.panel, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes)
self.cursor = Cursor(self.axes, useblit=True, color='red')
self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def cbUpdateCursor(self, event):
if event.inaxes:
text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata)
self.sb.SetStatusText(text)
#self.text.set_text(text)
#self.canvas.draw()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = wxPlotting()
app.frame.Show()
app.MainLoop()
Basically I want something similar to the text that gets displayed using pyplot, i.e. the bottom right corner when the code below is run:
Code:
import matplotlib.pyplot as plt
plt.plot(range(10000), range(10000))
plt.show()
EDIT:
In my actual program, I want the static text to be within the matplotlib axes, not really above it. So I don't think I can just use a wxpython statictext to display it.
You could use blitting, similar to the animation examples here.
This make a very large performance difference in this case, as only a small portion of the window needs to be redrawn.
Unfortunately, I can't figure out how to get a gray background behind the text when it's redrawn, to match the default figure background behind it... The performance is excellent, though.
As a stand-alone example based on your code above:
import wx
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.widgets import Cursor
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
class wxPlotting(wx.Frame):
title = 'Test'
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title)
self.time = np.arange(10000)
self.data = np.random.random(10000)
self.sb = self.CreateStatusBar()
self.create_main_panel()
self.axes.plot(self.time, self.data)
self.background = self.canvas.copy_from_bbox(self.fig.bbox)
self.canvas.draw()
def create_main_panel(self):
self.panel = wx.Panel(self)
self.fig = Figure((5.0, 4.0), dpi=100)
self.canvas = FigCanvas(self.panel, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes, animated=True)
self.cursor = Cursor(self.axes, useblit=True, color='red')
self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def cbUpdateCursor(self, event):
if event.inaxes:
text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata)
self.sb.SetStatusText(text)
self.canvas.restore_region(self.background)
self.text.set_text(text)
self.axes.draw_artist(self.text)
self.canvas.blit(self.text.get_window_extent())
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = wxPlotting()
app.frame.Show()
app.MainLoop()
You could add a static text box on top, and just update it's label:
import wx
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.widgets import Cursor
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
class wxPlotting(wx.Frame):
title = 'Test'
def __init__(self):
wx.Frame.__init__(self, None, -1, self.title)
self.time = np.arange(10000)
self.data = np.random.random(10000)
self.sb = self.CreateStatusBar()
self.create_main_panel()
self.axes.plot(self.time, self.data)
self.canvas.draw()
def create_main_panel(self):
self.panel = wx.Panel(self)
self.fig = Figure((5.0, 4.0), dpi=100)
self.canvas = FigCanvas(self.panel, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.text = self.axes.text(0., 1.005, '', transform = self.axes.transAxes)
self.cursor = Cursor(self.axes, useblit=True, color='red')
self.canvas.mpl_connect('motion_notify_event', self.cbUpdateCursor)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.cursor_pos = wx.StaticText(self.panel,-1, label="")
self.vbox.Add(self.cursor_pos, 0, wx.LEFT | wx.TOP | wx.GROW)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def cbUpdateCursor(self, event):
if event.inaxes:
text = 'x = %5.4f, y = %5.4f' % (event.xdata, event.ydata)
self.sb.SetStatusText(text)
self.cursor_pos.SetLabel(text)
#self.text.set_text(text)
#self.canvas.draw()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = wxPlotting()
app.frame.Show()
app.MainLoop()

matplotlib: how to refresh figure.canvas

I can't understand how to refresh FigureCanvasWxAgg instance. Here is the example:
import wx
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.NewId(), "Main")
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.figure = Figure(figsize=(1,2))
self.axe = self.figure.add_subplot(111)
self.figurecanvas = FigureCanvas(self, -1, self.figure)
self.buttonPlot = wx.Button(self, wx.NewId(), "Plot")
self.buttonClear = wx.Button(self, wx.NewId(), "Clear")
self.sizer.Add(self.figurecanvas, proportion=1, border=5, flag=wx.ALL | wx.EXPAND)
self.sizer.Add(self.buttonPlot, proportion=0, border=2, flag=wx.ALL)
self.sizer.Add(self.buttonClear, proportion=0, border=2, flag=wx.ALL)
self.SetSizer(self.sizer)
self.figurecanvas.Bind(wx.EVT_LEFT_DCLICK, self.on_dclick)
self.buttonPlot.Bind(wx.EVT_BUTTON, self.on_button_plot)
self.buttonClear.Bind(wx.EVT_BUTTON, self.on_button_clear)
self.subframe_opened = False
def on_dclick(self, evt):
self.subframe = SubFrame(self, self.figure)
self.subframe.Show(True)
self.subframe_opened = True
def on_button_plot(self, evt):
self.axe.plot(range(10), color='green')
self.figurecanvas.draw()
def on_button_clear(self, evt):
if self.subframe_opened:
self.subframe.Close()
self.figure.set_canvas(self.figurecanvas)
self.axe.clear()
self.figurecanvas.draw()
class SubFrame(wx.Frame):
def __init__(self, parent, figure):
wx.Frame.__init__(self, parent, wx.NewId(), "Sub")
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.figurecanvas = FigureCanvas(self, -1, figure)
self.sizer.Add(self.figurecanvas, proportion=1, border=5, flag=wx.ALL | wx.EXPAND)
self.SetSizer(self.sizer)
self.Bind(wx.EVT_CLOSE, self.on_close)
def on_close(self, evt):
self.GetParent().subframe_opened = False
evt.Skip()
class MyApp(wx.App):
def OnInit(self):
frame = MainFrame()
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
I'm interested in the following sequence of operations:
run a script
resize the main frame
press Plot button
double click on plot
press Clear button
Now I get a mess on main frame plot. If I resize the frame it redraws properly. My question is what should I add to my code to do that without resizing?
By a "mess" I mean something like this:
http://img227.imageshack.us/img227/5407/mess.png
Thanks in advance.
As I said in the comments, I don't think that the figure canvas refresh is your problem, in fact I think it's doing exactly what it's supposed to (redrawing itself based on it's last state [ie as it was in your subplot]). I think your problem is more that the wxFrame is not refreshing.
The easiest way to fix that would be to make it resize itself on your "Clear" event. Something like:
def on_button_clear(self, evt):
if self.subframe_opened:
self.subframe.Close()
self.figure.set_canvas(self.figurecanvas)
self.axe.clear()
self.figurecanvas.draw()
self.SetSize((self.Size[0],self.figurecanvas.Size[1]))
The set size would cause the frame and all the controls it contains to be redrawn.
That said, I think sharing your figure between the two figurecanvas is dangerous. I was able to produce some serious errors by clicking/resizing things in different combinations. On occasion your figure would be garbage collected when the subframe was closed, making it unavailable to your main frame.

Categories