At the moment I have a scatter graph which I can zoom in, move around etc.
What I also want to do is be able to select a certain number of points on the graph, then store the selected points in an array.
Is there any special function in matplotlib I can use?
Any help will be greatly appreciated
My code
import os
import wx
import numpy as nump
import matplotlib
matplotlib.use('WXAgg')
import matplotlib.figure as fg
import matplotlib.backends.backend_wxagg as wxagg
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Title')
self.create_main_panel()
self.draw_figure()
def create_main_panel(self):
self.panel = wx.Panel(self)
self.dpi = 100
self.fig = fg.Figure((5.0, 4.0), dpi=self.dpi)
self.canvas = wxagg.FigureCanvasWxAgg(self.panel, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.toolbar = wxagg.NavigationToolbar2WxAgg(self.canvas)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.vbox.AddSpacer(25)
self.vbox.Add(self.toolbar, 0, wx.EXPAND)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
def draw_figure(self):
self.axes.clear()
x, y = [2,3,4,5]
self.axes.scatter(x, y)
self.canvas.draw()
def on_exit(self, event):
self.Destroy()
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = MyFrame()
app.frame.Show()
app.MainLoop()
The code below provides a possible solution. The basic method may be summarized as follows:
One attaches a handler for pick_event that keeps appending the picked data indices to a list self._picked_indices.
A key_press_event handler clears the self._picked_indices list whenever the user presses the escape key.
The method MyFrame.picked_points returns a list of coordinates of currently selected points. This method returns None if no points have been selected yet (you can modify it to return an empty list in this case if that is more convenient).
This way you can keep selecting points by clicking on them. But if you want to start over again, just press escape and start picking again.
#! /usr/bin/env python
import os
import wx
import numpy as nump
import matplotlib
matplotlib.use('WXAgg')
import matplotlib.figure as fg
import matplotlib.backends.backend_wxagg as wxagg
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Title')
self.create_main_panel()
self.draw_figure()
self._is_pick_started = False
self._picked_indices = None
def create_main_panel(self):
self.panel = wx.Panel(self)
self.dpi = 100
self.fig = fg.Figure((5.0, 4.0), dpi=self.dpi)
self.canvas = wxagg.FigureCanvasWxAgg(self.panel, -1, self.fig)
self.axes = self.fig.add_subplot(111)
self.toolbar = wxagg.NavigationToolbar2WxAgg(self.canvas)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.vbox.AddSpacer(25)
self.vbox.Add(self.toolbar, 0, wx.EXPAND)
self.panel.SetSizer(self.vbox)
self.vbox.Fit(self)
self.fig.canvas.mpl_connect('pick_event', self.on_pick)
self.fig.canvas.mpl_connect('key_press_event', self.on_key)
def draw_figure(self):
self.axes.clear()
self._x_data, self._y_data = [[2,3], [4,5]]
self.axes.scatter(self._x_data, self._y_data, picker=5)
self.canvas.draw()
def on_exit(self, event):
self.Destroy()
def picked_points(self):
if self._picked_indices is None:
return None
else:
return [ [self._x_data[i], self._y_data[i]]
for i in self._picked_indices ]
def on_pick(self, event):
if not self._is_pick_started:
self._picked_indices = []
self._is_pick_started = True
for index in event.ind:
if index not in self._picked_indices:
self._picked_indices.append(index)
print self.picked_points()
def on_key(self, event):
"""If the user presses the Escape key then stop picking points and
reset the list of picked points."""
if 'escape' == event.key:
self._is_pick_started = False
self._picked_indices = None
return
if __name__ == '__main__':
app = wx.PySimpleApp()
app.frame = MyFrame()
app.frame.Show()
app.MainLoop()
Related
I create a world map using Geopandas and matplotlib in python. When I try to highlight a certain country, the map dimensions change. How can I preserve the map dimensions?
import matplotlib
from matplotlib import pyplot
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.collections import PatchCollection
from matplotlib.figure import Figure
import geopandas as gpd
import pandas
self.figure = Figure()
self.canvas = FigureCanvas(self, -1, self.figure)
self.axes = self.figure.add_axes([0, 0, 1, 1])
self.axes.margins(0.0)
self.world_data = gpd.read_file(WORLD)
self.axes.clear()
self.axes.axis('off')
self.figure.set_facecolor(WATER)
self.map_plot = self.world_data.to_crs(epsg=4326).plot(ax=self.axes, color=LAND)
if country_highlight:
self.world_data[self.world_data.ISO_A2_EH ==country_code].plot(edgecolor=u'gray', color='#fa8a48', ax=self.map_plot)
self.canvas.draw()
I attached a runnable below. Each time the user left clicks on the map, a random country is selected. But at each click, map dimensions change. What's wrong here? "ne_110m_admin_0_countries.shp" map file can be downloaded from: this page. You should put the sample code inside the map file directory.
import wx
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import geopandas as gpd
import random
import pathlib
import os
this_directory = print(pathlib.Path(__file__).parent.resolve())
WORLDX = os.path.join(this_directory, "ne_110m_admin_0_countries.shp")
WATER = '#defafc'
LAND = '#807e7e'
LEFT_MOUSE = matplotlib.backend_bases.MouseButton.LEFT
class MapFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "Map",
style=wx.DEFAULT_FRAME_STYLE,
size=wx.Size(1200, 900))
self.main_panel = MapPanel(self)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, _event):
self.Destroy()
wx.Exit()
class MapPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.SetDoubleBuffered(True)
self.figure = Figure()
self.result_frame = parent
self.canvas = FigureCanvas(self, -1, self.figure)
self.axes = self.figure.add_axes([0, 0, 1, 1])
self.axes.margins(0.0)
self.axes.set_anchor('C')
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()
self.Layout()
self.countries = ["US", "FR", "GB", "BR", "IN", "AU", "NZ", "IT", "DE"]
self.gdf = None
self.canvas.mpl_connect('button_press_event', self.onLeftClick)
self.world_data = gpd.read_file(WORLDX)
self.map_plot = None
self.drawMap()
def drawMap(self, country_highlight=None):
"""Draw the map on the canvas. """
self.axes.clear()
self.axes.axis('off')
self.figure.set_facecolor(WATER)
self.map_plot = self.world_data.to_crs(epsg=4326).plot(ax=self.axes, color=LAND)
print("country highlight: ", country_highlight)
if country_highlight:
ylim = self.map_plot.get_ylim()
xlim = self.map_plot.get_xlim()
self.world_data[self.world_data.ISO_A2_EH == country_highlight].plot(edgecolor=u'gray', color='#fa8a48', ax=self.map_plot)
self.map_plot.set_ylim(*ylim)
self.map_plot.set_xlim(*xlim)
self.canvas.draw()
def onLeftClick(self, event):
if event.button == LEFT_MOUSE:
self.drawMap(random.choice(self.countries))
class MyApp(wx.App):
def OnInit(self):
frame = MapFrame()
frame.Show()
return True
if __name__ == "__main__":
app = MyApp()
app.MainLoop()
If you want to hold the map extent for an axis constant after a certain point, you can save them and re-enforce them later:
# I'll drop references to `self` for the rest of the answer for clarity
ax = self.axes
world_data = self.world_data
world_data.to_crs(epsg=4326).plot(ax=ax, color=LAND)
ylim = ax.get_ylim()
xlim = ax.get_xlim()
if country_highlight:
self.world_data[
world_data.ISO_A2_EH == country_code
].plot(edgecolor=u'gray', color='#fa8a48', ax=ax)
ax.set_ylim(*ylim)
ax.set_xlim(*xlim)
I'm trying to show an image inside a panel of a wxpython notebook page using matplotlib. It works perfectly using wx.StaticBitmap but I need to use matplotlib for image manipulation. The code gives this result:
The right panel is a terminal and sizes correctly. The left panel is where the image is meant to fit. The result of wx.lib.inspection is showing that the wxFigureCanvas minsize is (640, 480) but I don't understand why and can't find how to change that. I suspect it's the problem. The code (sorry, short as I could make it):
import cv2 as cv
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import wx
import wx.lib.agw.aui as aui
class TopRight(wx.Panel):
def __init__(self, parent):
super(TopRight, self).__init__(parent)
self.term = wx.TextCtrl(self,
size=(200, 100),
style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH
)
self.term.SetDefaultStyle(wx.TextAttr(wx.GREEN))
self.term.SetBackgroundColour(wx.BLACK)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.term, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.term.WriteText("hello")
self.term.Update
class TopLeft(wx.Panel):
def __init__(self, parent):
super(TopLeft , self).__init__(parent)
img_pth = "av_cat.png"
# Intitialise the matplotlib figure
self.figure = Figure()
# Create an axes, turn off the labels and add them to the figure
self.axes = plt.Axes(self.figure,[0,0,1,1])
self.axes.set_axis_off()
self.figure.add_axes(self.axes)
# Add the figure to the wxFigureCanvas
self.canvas = FigureCanvas(self, -1, self.figure)
# Sizer to contain the canvas
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.EXPAND|wx.ALL)
self.SetSizer(self.sizer)
# If there is an initial image, display it on the figure
if img_pth is not None:
self.setImage(img_pth)
def setImage(self, pathToImage):
# Load the image into matplotlib
image = cv.imread(pathToImage)
# Add the image to the figure and redraw the canvas.
self.axes.imshow(image)
self.canvas.draw()
class explorer_panel(wx.Panel):
def __init__(self, parent):
#Constructor
wx.Panel.__init__(self, parent)
topSplitter = wx.SplitterWindow(self)
image_panel = TopLeft(topSplitter)
terminal_panel = TopRight(topSplitter)
topSplitter.SplitVertically(image_panel, terminal_panel)
topSplitter.SetSashGravity(0.5)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(topSplitter, 1, wx.EXPAND)
self.SetSizer(sizer)
class Main(wx.Frame):
def __init__(self):
wx.Frame.__init__(
self,
parent = None,
title = "Borges Infinite Image",
size = (600,300)
)
panel = wx.Panel(self)
notebook = wx.Notebook(panel)
explorer = explorer_panel(notebook)
notebook.AddPage(explorer, 'Explorer')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook, 1, wx.ALL|wx.EXPAND, 5)
panel.SetSizer(sizer)
if __name__ == "__main__":
app = wx.App()
frame = Main()
frame.Show()
import wx.lib.inspection as wxli
wxli.InspectionTool().Show()
app.MainLoop()
I'm trying to create a gui with the following layout:
Where the top panel is going to contain three figures, the three middle panels are going to contain textboxes and the bottom panel some buttons
Here is what I have so far:
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import gui_functions as gf
class MainWindow(wx.Frame):
def __init__(self, parent, id, title = "Test GUI"):
wx.Frame.__init__(self, parent, wx.ID_ANY, title = "MyTitle", size=(1200,1000))
self.graphicsPanel = wx.Panel(self,-1,style=wx.CLIP_CHILDREN)
self.textPanel = wx.Panel(self)
self.buttonsPanel = wx.Panel(self)
self.graphicsPanel.figure = Figure()
self.graphicsPanel.canvas = FigureCanvas(self.graphicsPanel, -1, self.graphicsPanel.figure)
self.axeshandleA = self.graphicsPanel.figure.add_subplot(131, projection='3d',facecolor='#f3f3f3')
self.axeshandleB = self.graphicsPanel.figure.add_subplot(132, projection='3d',facecolor='#f3f3f3')
self.axeshandleC = self.graphicsPanel.figure.add_subplot(133, projection='3d',facecolor='#f3f3f3')
if __name__ == '__main__':
app = wx.App(False)
app.frame = MainWindow(None, wx.ID_ANY)
app.frame.Show()
app.MainLoop()
I've assumed that I need to start with three panels, that I somehow have to align vertically.
I'm thinking in the middle panel I will then need to add three additional panels and align them horizontally
My brain is thinking along the lines of how to structure div boxes in HTML and CSS.
My first problem is that I can't work out how to get the panels to a) fill the parent and b) align vertically.
Secondly, I'm working Spyder. Why do I have to start a new console after every call to the gui? If I don't then I get the error
The wx.App object must be created first!
I would really appreciate any help, because I'm stuck! Many thanks
I'm running wxPython 4.0.0rc1.dev3617+346e3b7 and Python 3.5.4
After much work and phaffing I now have this GUI:
The buttons are still wider than I would like, but I can't figure out how to make them narrower.
The code I have used is
import wx
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
class MainWindow(wx.Frame):
def __init__(self, parent, id, title = "Test GUI"):
wx.Frame.__init__(self, parent, wx.ID_ANY, title = "MyTitle", size=(1200,1000))
self.graphicsPanel = wx.Panel(self,-1,style=wx.CLIP_CHILDREN)
self.graphicsPanel.SetBackgroundColour('#3aafbb')
self.textPanel = wx.Panel(self)
self.textPanel.SetBackgroundColour('#ffaf33')
self.buttonsPanel = wx.Panel(self)
self.buttonsPanel.SetBackgroundColour('#489e3e')
self.graphicsPanel.figure = Figure()
self.graphicsPanel.canvas = FigureCanvas(self.graphicsPanel, 1, self.graphicsPanel.figure)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(self.graphicsPanel, 3, wx.EXPAND, border = 20)
main_sizer.Add(self.textPanel, 6, wx.EXPAND, border = 20)
main_sizer.Add(self.buttonsPanel, 1, wx.EXPAND, border = 20)
self.SetSizer(main_sizer)
self.Maximize(True)
self.axeshandleA = self.graphicsPanel.figure.add_subplot(131, projection='3d',facecolor='#f3f3f3')
self.axeshandleB = self.graphicsPanel.figure.add_subplot(132, projection='3d',facecolor='#f3f3f3')
self.axeshandleC = self.graphicsPanel.figure.add_subplot(133, projection='3d',facecolor='#f3f3f3')
self.graphicsPanel_sizer = wx.BoxSizer()
self.graphicsPanel_sizer.Add(self.graphicsPanel.canvas, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border=10)
self.graphicsPanel.SetSizerAndFit(self.graphicsPanel_sizer)
self.textPanelR = wx.Panel(self.textPanel)
self.textPanelR.SetBackgroundColour('#f2c4f5')
self.textPanelA = wx.Panel(self.textPanel)
self.textPanelA.SetBackgroundColour('#86478a')
self.textPanelC = wx.Panel(self.textPanel)
self.textPanelC.SetBackgroundColour('#e11fee')
self.textPanel_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.textPanel_sizer.Add(self.textPanelR, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border=20)
self.textPanel_sizer.Add(self.textPanelA, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border=20)
self.textPanel_sizer.Add(self.textPanelC, 1, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP|wx.BOTTOM, border=20)
self.textPanel.SetSizerAndFit(self.textPanel_sizer)
self.backBtn = wx.Button(self.buttonsPanel,label='<<BACK',style=wx.BU_EXACTFIT, size=(50,30))
self.backBtn.Bind(wx.EVT_BUTTON, self.backBtnClicked)
self.exitBtn = wx.Button(self.buttonsPanel,label='EXIT',style=wx.BU_EXACTFIT, size=(50,30))
self.exitBtn.Bind(wx.EVT_BUTTON, self.exitBtnClicked)
self.nextBtn = wx.Button(self.buttonsPanel,label='NEXT>>',style=wx.BU_EXACTFIT, size=(50,30))
self.nextBtn.Bind(wx.EVT_BUTTON, self.nextBtnClicked)
self.buttonsPanel_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.buttonsPanel_sizer.Add(self.backBtn, 1, wx.CENTER|wx.LEFT|wx.RIGHT, border=20)
self.buttonsPanel_sizer.Add(self.exitBtn, 1, wx.CENTER|wx.LEFT|wx.RIGHT, border=20)
self.buttonsPanel_sizer.Add(self.nextBtn, 1, wx.CENTER|wx.LEFT|wx.RIGHT, border=20)
self.buttonsPanel.SetSizer(self.buttonsPanel_sizer)
def backBtnClicked(self, event):
print("Back Button")
def nextBtnClicked(self, event):
print("Next Button")
def exitBtnClicked(self, event):
print("Exit Button")
if __name__ == '__main__':
app = wx.App(False)
app.frame = MainWindow(None, wx.ID_ANY)
app.frame.Show()
app.MainLoop()
I doubt it's as efficient as it could be, but it seems to mostly do what I want it to. Any further advice or recommendations would be gratefully received.
In a wxPython application I have an embedded a FigureCanvas with a matplotlib figure. I want to be able to switch the figure by loading a new one. However, the figure is not being updated.
Answers to similar topics suggests that panel.canvas.draw() and panel.Refresh() should do the trick, but I've also tried panel.Update() and panel.canvas.Refresh(). I fear that this only works if you want to redraw the canvas with the same figure?
So my question is: how do you replace the figure inside a canvas and make it update?
Below is a small (non-working) example. First a figure is loaded with a single axis. If you from the embedded shell type panel.LoadFigure() a new figure with 2x2 subplots is created and put into the canvas. But the new figure is not shown.
import numpy as np
import wx
from wx.py.shell import Shell
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.pyplot as plt
class ShellPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.shell = Shell(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.shell, 1, wx.GROW)
self.SetSizer(self.sizer)
self.Layout()
self.Fit()
class FigurePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.parent = parent
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.figure, ax = plt.subplots()
self.canvas = FigureCanvas(self, -1, self.figure)
self.shellpanel = ShellPanel(self)
s1 = wx.BoxSizer(wx.VERTICAL)
s1.Add(self.canvas, 0, wx.GROW)
s1.Add(self.shellpanel, 1 , wx.EXPAND)
self.sizer.Add(s1, 5, wx.GROW)
self.SetSizer(self.sizer)
self.Layout()
self.Fit()
def LoadFigure(self):
self.figure, ax = plt.subplots(2, 2)
self.canvas.draw()
self.Refresh()
class FigureFrame(wx.Frame):
def __init__(self, parent, id, title, size):
wx.Frame.__init__(self, parent, id, title, size=size)
if __name__ == "__main__":
app = wx.App(False)
fr = FigureFrame(None, -1, title='Figure Loader', size=(wx.DisplaySize()[0]/2, wx.DisplaySize()[1]*3/4))
panel = FigurePanel(fr)
fr.Show()
app.MainLoop()
Is there any reason you are trying to create a new Figure and not simply new Axes on the already defined Figure?
I would write your code like so:
def LoadFigure(self):
self.figure.clf() # clear current figure
# add arbitrary number of new Axes
self.figure.add_subplot(221)
self.figure.add_subplot(222)
self.figure.add_subplot(223)
self.figure.add_subplot(224)
self.canvas.draw() # refresh canvas
EDIT: following your comment, I think the problem is that you're creating a new figure, but your Canvas is still referencing the old one. I don't know if you can change that directly in the FigureCanvas properties, maybe someone with more experience can provide a better answer. For the moment, I would Destroy the previous canvas, and create a new FigureCanvas object with your new figure.
def LoadFigure(self):
self.figure, ax = plt.subplots(2, 2)
self.canvas.Destroy()
self.canvas = FigureCanvas(self, -1, self.figure)
self.canvas.draw()
Try using the Figure itself to make your figure, not pyplot. It has usually worked for me because it gives you more room to improvise.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import wx
from wx.py.shell import Shell
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import matplotlib.pyplot as plt
class ShellPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.shell = Shell(self)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.shell, 1, wx.GROW)
self.SetSizer(self.sizer)
self.Layout()
self.Fit()
class FigurePanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.parent = parent
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.enlarged_figure = Figure(figsize = (8,6.1), dpi = 100)
self.ax2 = self.enlarged_figure.add_subplot(2,1,2)
self.canvas = FigureCanvas(self, -1, self.figure)
self.canvas.Show()
self.enlarged_canvas = FigureCanvas(self,-1,self.enlarged_figure)
self.enlarged_canvas.Show()
self.shellpanel = ShellPanel(self)
s1 = wx.BoxSizer(wx.VERTICAL)
s1.Add(self.canvas, 0, wx.GROW)
s1.Add(self.shellpanel, 1 , wx.EXPAND)
self.sizer.Add(s1, 5, wx.GROW)
self.SetSizer(self.sizer)
self.Layout()
self.Fit()
def LoadFigure(self):
self.figure, ax = plt.subplots(2, 2)
self.canvas.draw()
self.Refresh()
class FigureFrame(wx.Frame):
def __init__(self, parent, id, title, size):
wx.Frame.__init__(self, parent, id, title, size=size)
if __name__ == "__main__":
app = wx.App(False)
fr = FigureFrame(None, -1, title='Figure Loader', size=(wx.DisplaySize()[0]/2, wx.DisplaySize()[1]*3/4))
panel = FigurePanel(fr)
fr.Show()
app.MainLoop()
In the shell type in 'panel.LoadFigure()' and the new figure will show up very quickly and then disappear. Then type 'panel.canvas.draw()' and the new figure will be there. Then type 'panel.LoadFigure()' to load the old figure back. Repeat.
I don't know why the figure remains hidden after the MainLoop() continues but this is sort of a quick fix to your problem.
I have a scatter plot with several thousand points. This post tells me how to label them:
Matplotlib: How to put individual tags for a scatter plot
But that will look like a disaster with so many points. What I would like instead is to have a "tool tip" type label that pops up when you mouseover a point. Is that possible using matplotlib?
Once you get the coords of the point you can show them or any object-linked info in a textctrl in the toolbar. For this you have to instantiate a toolbar (NavigationToolbar2Wx()) in your canvas and add the textcontrol there. This is not as nice as a popup but it does the job.
Here you have an example of customizing your toolbar (only showing the x coordinate in the txtctrl):
#!/usr/bin/env python
#-*- coding: utf-8 -*-
#
"""
jvisor_spectrum_panel (visor_07)
25 julio 2010
"""
#
import wx
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
#
#
class SpectrumPanel(wx.Panel):
def __init__(self, parent, xlabel='m/z', ylabel='Intensity'):
wx.Panel.__init__(self, parent)
#
self.parent = parent
self.xlabel = xlabel
self.ylabel = ylabel
self.SetBackgroundColour("white")
#
self.figure = Figure()
self.canvas = FigureCanvas(self, -1, self.figure)
#
self.add_toolbar()
#
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP| wx.GROW| wx.EXPAND)
sizer.Add(self.toolbar, 0, wx.LEFT)
self.canvas.mpl_connect('motion_notify_event', self.on_motion)
self.SetSizer(sizer)
self.Fit()
self.clean()
#
def add_toolbar(self):
""
self.toolbar = NavigationToolbar2Wx(self.canvas)
mass_txt = wx.StaticText(self.toolbar, label='m/z', pos=(230, 7),
size=(25, 17))
mass_txt.SetBackgroundColour("light gray")
self.mass = wx.TextCtrl(self.toolbar, pos=(260,4), size=(50, 22),
style=wx.TE_READONLY)
#
self.toolbar.SetToolBitmapSize(wx.Size(24, 25))
self.toolbar.SetMinSize((1500, 31))
self.toolbar.Realize()
self.toolbar.Update()
#
def clean(self):
""
self.figure.clear()
self.axes = self.figure.add_subplot(111)
#
def dibuja(self):
"dibuja el canvas"
self.axes.set_xlabel(self.xlabel)
self.axes.set_ylabel(self.ylabel)
self.canvas.draw()
#
def on_motion(self, evt):
if evt.inaxes:
xpos = evt.xdata
self.mass.SetValue(' %0.1f' % (xpos))
if __name__ == '__main__':
""
class TestFrame(wx.Frame):
def __init__(self, *args, **kargs):
wx.Frame.__init__(self, *args, **kargs)
self.panel = SpectrumPanel(self)
self.Fit()
#
app = wx.PySimpleApp()
fr = TestFrame(None)
fr.Show()
app.MainLoop()
And here you can see the new control in the toolbar: