Related
I used figurecanvas to show histplot on the GUI made by PyQt5.
I made Optionwindow to make the graph with the region I want.
the code wrote:
class OptionWindow(QDialog):
def __init__(self, parent): # 부모 window 설정
super(OptionWindow, self).__init__(parent)
uic.loadUi("graph tool.ui", self)
self.fig = plt.Figure()
self.canvas = FigureCanvas(self.fig)
self.QVlayout_graph.addWidget(self.canvas)
self.graph_button.clicked.connect(self.graph_draw)
self.save_button.clicked.connect(self.graph_save)
self.show()
def graph_draw(self):
self.fig.clf()
global bin_width, x_begin, x_end, y_begin, y_end
bin_width = float(self.edit_bin.text())
x_begin = float(self.edit_x_begin.text())
x_end = float(self.edit_x_end.text())
y_begin = float(self.edit_y_begin.text())
y_end = float(self.edit_y_end.text())
ax = self.fig.add_subplot(111)
sns.histplot(data = df['contour_area (mm2)'], binwidth=bin_width, ax=ax)
ax.set_xlabel('Pin-hole Area (${mm^2}$)', size = 13)
ax.set_ylabel('Count', size = 13)
ax.set_xlim([x_begin, x_end])
ax.set_ylim([y_begin, y_end])
self.fig.tight_layout()
self.canvas.draw()
and the graph is a little diffrent with the axis.
It's not fit correctly to the axis.
How can I fix it?
enter image description here
I'm trying to do something similar to that shown in this link to draw a cursor and report the data coordinates in the status bar. However, my code is a little bit different since I need to use wx.SplitterWindow to separate buttons and plots. Basically, in the main frame module I create the status bar, but the plot is generated in a separated module. See below my current code:
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar, \
wxc as wxc
import pylab
import wx
from numpy import arange, sin, pi
class data:
def __init__(self):
self.t = []
self.s = []
class Plot_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
checkSizer = wx.BoxSizer(wx.HORIZONTAL)
# create figrue
self.fig = Figure()
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
# create the widgets
self.toggleMarker = wx.CheckBox(self, label="Show Marker")
# layout the widgets
mainSizer.Add(self.canvas, 1, wx.EXPAND)
checkSizer.Add(self.toggleMarker, 0, wx.ALL, 5)
mainSizer.Add(checkSizer)
self.SetSizer(mainSizer)
def draw_plot(self, data):
# Clear the previous figure
self.fig.clear()
# Redraw figure
self.axes = self.fig.add_subplot(111)
# Define data to plot
self.plot_data= self.axes.plot(data.t, data.s, linewidth=3, color='y',)[0]
# Draw Cursor or not
if self.toggleMarker.IsChecked():
# Note that event is a MplEvent
self.canvas.mpl_connect('motion_notify_event', self.UpdateStatusBar)
self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor)
self.canvas.draw()
def ChangeCursor(self, event):
self.canvas.SetCursor(wxc.StockCursor(wx.CURSOR_BULLSEYE))
def UpdateStatusBar(self, event):
if event.inaxes:
x, y = event.xdata, event.ydata
# self.statusBar.SetStatusText(("x= "+str(Pos.x)+" y="+str(Pos.y)))
class Button_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create the widgets
self.toggleStart = wx.Button(self, id=wx.ID_ANY, label="Plot data")
class ProportionalSplitter(wx.SplitterWindow):
def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
self.SetMinimumPaneSize(50) #the minimum size of a pane.
self.proportion = proportion
if not 0 < self.proportion < 1:
raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
self.ResetSash()
self.Bind(wx.EVT_SIZE, self.OnReSize)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.firstpaint = True
def SplitHorizontally(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
def SplitVertically(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitVertically(self, win1, win2,
int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
def GetExpectedSashPosition(self):
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
return int(round(tot * self.proportion))
def ResetSash(self):
self.SetSashPosition(self.GetExpectedSashPosition())
def OnReSize(self, event):
"Window has been resized, so we need to adjust the sash based on self.proportion."
self.ResetSash()
event.Skip()
def OnSashChanged(self, event):
"We'll change self.proportion now based on where user dragged the sash."
pos = float(self.GetSashPosition())
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
self.proportion = pos / tot
event.Skip()
def OnPaint(self, event):
if self.firstpaint:
if self.GetSashPosition() != self.GetExpectedSashPosition():
self.ResetSash()
self.firstpaint = False
event.Skip()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title)
# Create a StatusBar at the bottom of the window
self.statusBar = wx.StatusBar(self, -1)
self.SetStatusBar(self.statusBar)
# Set plot panel
self.splitter = ProportionalSplitter(self,-1, 0.85)
self.ppanel = Plot_Panel(self.splitter)
self.ppanel.SetBackgroundColour('#ffffff')
# Set button panel
self.bpanel = Button_Panel(self.splitter)
# Set frame
self.splitter.SplitVertically(self.ppanel, self.bpanel)
self.Show(True)
self.Maximize(True)
# bind the widgets
self.ppanel.toggleMarker.Bind(wx.EVT_CHECKBOX, self.onToggleMarker)
self.bpanel.toggleStart.Bind(wx.EVT_BUTTON, self.onToggleStart)
# Set classes
self.data = data()
def onToggleMarker(self, event):
self.ppanel.draw_plot(self.data)
def onToggleStart(self, event):
self.data.t = arange(0.0, 1.0, 0.01)
self.data.s = sin(2*2*pi*self.data.t)
# plot data
self.ppanel.draw_plot(self.data)
def main():
app = wx.App(False)
frame = Main_Window(None, "GUI")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()
The plot is shown when the button "Plot data" is pressed. What I would like to do is to show the x and y position in the status bar when the checkbox "Show Marker" is checked (in a similar way as it is done in the code posted in the link), and stop when it is unchecked. But I'm not sure if it is possible to do it in my code due to having the definitions of the status bar and the plot in different modules. Any hint will be welcome.
What a joy to get a full, working example program with the question.
You just need to pass the base module as a parameter to self.ppanel
i.e.
self.ppanel = Plot_Panel(self.splitter, self)
then refer to that when updating the status bar, see below for references to base
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar, \
wxc as wxc
import pylab
import wx
from numpy import arange, sin, pi
class data:
def __init__(self):
self.t = []
self.s = []
class Plot_Panel(wx.Panel):
def __init__(self, parent, base):
wx.Panel.__init__(self, parent)
self.base = base
# create some sizers
mainSizer = wx.BoxSizer(wx.VERTICAL)
checkSizer = wx.BoxSizer(wx.HORIZONTAL)
# create figrue
self.fig = Figure()
self.canvas = FigCanvas(self, -1, self.fig)
self.axes = self.fig.add_subplot(111)
# create the widgets
self.toggleMarker = wx.CheckBox(self, label="Show Marker")
# layout the widgets
mainSizer.Add(self.canvas, 1, wx.EXPAND)
checkSizer.Add(self.toggleMarker, 0, wx.ALL, 5)
mainSizer.Add(checkSizer)
self.SetSizer(mainSizer)
def draw_plot(self, data):
# Clear the previous figure
self.fig.clear()
# Redraw figure
self.axes = self.fig.add_subplot(111)
# Define data to plot
self.plot_data= self.axes.plot(data.t, data.s, linewidth=3, color='y',)[0]
# Draw Cursor or not
if self.toggleMarker.IsChecked():
# Note that event is a MplEvent
self.canvas.mpl_connect('motion_notify_event', self.UpdateStatusBar)
self.canvas.Bind(wx.EVT_ENTER_WINDOW, self.ChangeCursor)
self.canvas.draw()
def ChangeCursor(self, event):
self.canvas.SetCursor(wxc.StockCursor(wx.CURSOR_BULLSEYE))
def UpdateStatusBar(self, event):
if event.inaxes:
x, y = event.xdata, event.ydata
self.base.statusBar.SetStatusText(("x= "+str(x)+" y="+str(y)))
class Button_Panel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# create the widgets
self.toggleStart = wx.Button(self, id=wx.ID_ANY, label="Plot data")
class ProportionalSplitter(wx.SplitterWindow):
def __init__(self,parent, id = -1, proportion=0.66, size = wx.DefaultSize, **kwargs):
wx.SplitterWindow.__init__(self,parent,id,wx.Point(0, 0),size, **kwargs)
self.SetMinimumPaneSize(50) #the minimum size of a pane.
self.proportion = proportion
if not 0 < self.proportion < 1:
raise ValueError, "proportion value for ProportionalSplitter must be between 0 and 1."
self.ResetSash()
self.Bind(wx.EVT_SIZE, self.OnReSize)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED, self.OnSashChanged, id=id)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.firstpaint = True
def SplitHorizontally(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitHorizontally(self, win1, win2,
int(round(self.GetParent().GetSize().GetHeight() * self.proportion)))
def SplitVertically(self, win1, win2):
if self.GetParent() is None: return False
return wx.SplitterWindow.SplitVertically(self, win1, win2,
int(round(self.GetParent().GetSize().GetWidth() * self.proportion)))
def GetExpectedSashPosition(self):
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
return int(round(tot * self.proportion))
def ResetSash(self):
self.SetSashPosition(self.GetExpectedSashPosition())
def OnReSize(self, event):
"Window has been resized, so we need to adjust the sash based on self.proportion."
self.ResetSash()
event.Skip()
def OnSashChanged(self, event):
"We'll change self.proportion now based on where user dragged the sash."
pos = float(self.GetSashPosition())
if self.GetSplitMode() == wx.SPLIT_HORIZONTAL:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().height)
else:
tot = max(self.GetMinimumPaneSize(),self.GetParent().GetClientSize().width)
self.proportion = pos / tot
event.Skip()
def OnPaint(self, event):
if self.firstpaint:
if self.GetSashPosition() != self.GetExpectedSashPosition():
self.ResetSash()
self.firstpaint = False
event.Skip()
class Main_Window(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title = title)
# Create a StatusBar at the bottom of the window
self.statusBar = wx.StatusBar(self, -1)
self.SetStatusBar(self.statusBar)
# Set plot panel
self.splitter = ProportionalSplitter(self,-1, 0.85)
self.ppanel = Plot_Panel(self.splitter, self)
self.ppanel.SetBackgroundColour('#ffffff')
# Set button panel
self.bpanel = Button_Panel(self.splitter)
# Set frame
self.splitter.SplitVertically(self.ppanel, self.bpanel)
self.Show(True)
self.Maximize(True)
# bind the widgets
self.ppanel.toggleMarker.Bind(wx.EVT_CHECKBOX, self.onToggleMarker)
self.bpanel.toggleStart.Bind(wx.EVT_BUTTON, self.onToggleStart)
# Set classes
self.data = data()
def onToggleMarker(self, event):
self.ppanel.draw_plot(self.data)
def onToggleStart(self, event):
self.data.t = arange(0.0, 1.0, 0.01)
self.data.s = sin(2*2*pi*self.data.t)
# plot data
self.ppanel.draw_plot(self.data)
def main():
app = wx.App(False)
frame = Main_Window(None, "GUI")
frame.Show()
app.MainLoop()
if __name__ == "__main__" :
main()
I'm brand new to matplotlib. I have found many examples of embedding bar graphs and plots into a wxPython panel. When I try to replace a graph or plot with a pie chart, the pie chart displays and only when it closes does the panel displays. It doesn't embed. I've been searching for 2 days.
I would like to embed this or this pie chart into this example.
I can't take the code for the pie chart and replace the code for the graph in the example. (Which means I'm missing something pretty big)
This is what I have tried:
import wx
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.pyplot as plt
class MainFrame(wx.Frame):
"""docstring for MainFrame"""
def __init__(self, parent):
wx.Frame.__init__(self, parent, title=">>>> by www.UmarYusuf.com", size=(800, 580))
# Add SplitterWindow panels
self.split_win = wx.SplitterWindow(self)
self.top_split = MatplotPanel(self.split_win)
self.bottom_split = wx.Panel(self.split_win, style=wx.SUNKEN_BORDER)
self.split_win.SplitHorizontally(self.top_split, self.bottom_split, 480)
# Add some contrls/widgets (StaticText and Buttons)
# Add Text control to the bottom_split window
self.text1 = wx.StaticText(self.bottom_split, -1, u"You can also plot from file", size=(250, 30), pos=(510, 10), style=wx.ALIGN_CENTER)
self.text1.SetBackgroundColour('Gray')
font = wx.Font(15, wx.SWISS, wx.NORMAL, wx.NORMAL)
self.text1.SetFont(font)
# Add Buttons to the bottom_split window and bind them to plot functions
self.Button1 = wx.Button(self.bottom_split, -1, "Plot1", size=(80, 40), pos=(10, 10))
self.Button1.Bind(wx.EVT_BUTTON, self.plot1)
self.Button2 = wx.Button(self.bottom_split, -1, "Plot2", size=(80, 40), pos=(110, 10))
self.Button2.Bind(wx.EVT_BUTTON, self.plot2)
self.Button3 = wx.Button(self.bottom_split, -1, "Plot3", size=(80, 40), pos=(210, 10))
self.Button3.Bind(wx.EVT_BUTTON, self.plot3)
def plot1(self, event):
pass
def plot2(self, event):
pass
def plot3(self, event):
pass
def plot4(self, event):
pass
def plot5(self, event):
pass
class MatplotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent,-1,size=(50,50))
self.figure = Figure()
# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice (i.e. 'Hogs')
fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, startangle=90)
ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.show()
self.canvas = FigureCanvas(self, -1, self.figure)
app = wx.App()
frame = MainFrame(None).Show()
app.MainLoop()
Thank you.
There is a question about embedding matplotlib into wxpython, where the answer is showing a minimal example. Several examples are also available on the matplotlib examples page.
The main problem you are facing here is that you are creating two different figures. The first one, self.figure = Figure(), is the one you connect to the canvas, but the second one, fig1 is the one you plot your graph to. The second one is the one shown in a new window when plt.show() gets called.
However, calling plt.show() does not make any sense when embedding into GUIs since the GUI should take care of showing the figure.
Therefore the MatplotPanel class from the question should look like this:
class MatplotPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent,-1,size=(50,50))
self.figure = Figure()
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
ax1 = self.figure.add_subplot(111)
ax1.pie(sizes, labels=labels, autopct='%1.1f%%')
ax1.axis('equal')
self.canvas = FigureCanvas(self, -1, self.figure)
I'm writing a python application using pyside and matplotlib. Following a combination of this tutorial and this SO post, I have created a matplotlib widget that I can successfully add to a parent. However when I go to actually add data to it, nothing seems to get displayed.
If I add static data like the SO post had, it shows up, but when I change it to update on the fly (currently every second on a timer, but it will eventually be using a signal from another class), I never get anything but the empty axes to appear. I suspect that I'm missing a call to force a draw or invalidate or that there is something wrong with the way I'm calling update_datalim (though the values that get passed to it seem correct).
from PySide import QtCore, QtGui
import matplotlib
import random
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib import pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.patches import Rectangle
from collections import namedtuple
DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height'])
class BaseWidget(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
# We want the axes cleared every time plot() is called
self.axes.hold(False)
self.axes.set_xlabel('X Label')
self.axes.set_ylabel('Y Label')
self.axes.set_title('My Data')
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class DynamicWidget(BaseWidget):
def set_data(self, the_data):
self.axes.clear()
xys = list()
cmap = plt.cm.hot
for datum in the_data:
bottom_left = (datum.start_x, datum.start_y)
top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height)
rect = Rectangle(
xy=bottom_left,
width=datum.width, height=datum.height, color=cmap(100)
)
xys.append(bottom_left)
xys.append(top_right)
self.axes.add_artist(rect)
self.axes.update_datalim(xys)
self.axes.figure.canvas.draw_idle()
class RandomDataWidget(DynamicWidget):
def __init__(self, *args, **kwargs):
DynamicWidget.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.generate_and_set_data)
timer.start(1000)
def generate_and_set_data(self):
fake_data = [DataModel(
start_x=random.randint(1, 100),
width=random.randint(20, 40),
start_y=random.randint(80, 160),
height=random.randint(20, 90)
) for i in range(100)]
self.set_data(fake_data)
Edit: I'm suspecting that there's an issue with updating the limits of the plot. When running the above code, the plot opens with limits of 0 and 1 on both the x and y axis. Since none of my generated data falls into that range, I created another subclass of DynamicWidget that plots only data between 0 and 1 (the same data from the linked SO post). When instantiating the class below, the data shows up successfully. Do I need to do something more than calling update_datalim to get the graph to re-bound itself?
class StaticWidget(DynamicWidget):
def __init__(self):
DynamicWidget.__init__(self)
static_data = [
DataModel(0.5, 0.05, 0.2, 0.05),
DataModel(0.1, 0.2, 0.7, 0.2),
DataModel(0.3, 0.1, 0.8, 0.1)
]
self.set_data(static_data)
Yes, update_datalim only updates the bounding box that is kept internally by the axes. You also need to enable auto scaling for it to be used. Add self.axes.autoscale(enable=True) after the self.axes.clear() statement and it will work. Or you can set the axes' range to a fixed value by using self.axes.set_xlim and self.axes.set_ylim.
edit: here is my code, which works for me
from PySide import QtCore, QtGui
import matplotlib
import random, sys
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib import pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.patches import Rectangle
from collections import namedtuple
DataModel = namedtuple('DataModel', ['start_x', 'start_y', 'width', 'height'])
class BaseWidget(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
# We want the axes cleared every time plot() is called
self.axes.hold(False)
#self.axes.autoscale(enable=True)
self.axes.set_xlabel('X Label')
self.axes.set_ylabel('Y Label')
self.axes.set_title('My Data')
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class DynamicWidget(BaseWidget):
def set_data(self, the_data):
self.axes.clear()
self.axes.autoscale(enable=True)
#self.axes.set_xlim(0, 300)
#self.axes.set_ylim(0, 300)
xys = list()
cmap = plt.cm.hot
for datum in the_data:
print datum
bottom_left = (datum.start_x, datum.start_y)
top_right = (bottom_left[0] + datum.width, bottom_left[1] + datum.height)
rect = Rectangle(
xy=bottom_left,
width=datum.width, height=datum.height, color=cmap(100)
)
xys.append(bottom_left)
xys.append(top_right)
self.axes.add_artist(rect)
self.axes.update_datalim(xys)
self.axes.figure.canvas.draw_idle()
class RandomDataWidget(DynamicWidget):
def __init__(self, *args, **kwargs):
DynamicWidget.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.generate_and_set_data)
timer.start(1000)
def generate_and_set_data(self):
fake_data = [DataModel(
start_x=random.randint(1, 100),
width=random.randint(20, 40),
start_y=random.randint(80, 160),
height=random.randint(20, 90)) for i in range(100)]
self.set_data(fake_data)
print "done:...\n\n"
def main():
qApp = QtGui.QApplication(sys.argv)
aw = RandomDataWidget()
aw.show()
aw.raise_()
sys.exit(qApp.exec_())
if __name__ == "__main__":
main()
In following code, when the button is clicked, I insert the plots into the tabs of an auinotebook in another frame.
for example, When I have multiple plots in the plt window, I can drag a notebook tab into the bottom (that results in displaying two plots). Later on when I delete the bottom tab, and try to go into other plots, I see a flicker like the closed tab is still there.
I guess the issue is with my on_nb_tab_close. Because, without that I was not able to notice any such problem.
I appreciate help. Code samples will be very useful. (wxpython version 2.812)
import wx
import wx.lib.agw.aui as aui
import matplotlib as mpl
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as mplCanvas
def create_plotter(self):
try:
self.plotter.Show()
except AttributeError:
self.plotter =PlotFrame(self, 500, 500)
self.plotter.Show()
return self.plotter
class PlotFrame(wx.Frame):
def __init__(self, parent, height, width):
wx.Frame.__init__(self, None, size=(height,width), title="plts")
self.parent=parent
self.nb = aui.AuiNotebook(self)
self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_nb_tab_close, self.nb)
def AddPlotTab(self,name="plot"):
page = Plot(self.nb)
self.nb.AddPage(page,name)
return page
def on_nb_tab_close(self, evt):
print "tab close fired"
s=self.nb.GetSelection()
v=self.nb.RemovePage(s)
if not self.nb.GetPageCount():
self.on_Close(evt)
evt.Veto()
class Plot(wx.Panel):
def __init__(self, parent, id = -1, dpi = None, **kwargs):
wx.Panel.__init__(self, parent, id=id, **kwargs)
self.figure = mpl.figure.Figure(dpi=dpi)
self.canvas = mplCanvas(self, -1, self.figure) # wxPanel object containing plot
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas,1,wx.EXPAND)
self.SetSizer(sizer)
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Plotting test", size=(300, 300))
self.btn1 = wx.Button(self, -1, "Print 1")
self.Bind(wx.EVT_BUTTON, self.OnBtn1, self.btn1)
def OnBtn1(self, evt):
plotter=create_plotter(self)
page1 = plotter.AddPlotTab("case 1: first_plot")
page1.figure.gca().plot(range(10),range(10),'+')
page1.figure.gca().plot(range(10),range(10),'-',color='red')
page1.figure.canvas.draw()
if __name__ == '__main__':
APP = wx.App(False)
FRAME = MainFrame(None)
FRAME.Show()
APP.MainLoop()