I am making a simple gui for a signal generator with wx. I want to display a small plot of say a sine wave when the option "Sine" is chosen, and a plot of a square wave when "Square" is chosen from the menu. I am a newbie in python, and therefore the classes and inheritance of these is difficult for me.
In the (much stripped down) code i have a variable 'wave' inside the class 'ServelPanel' that holds which wavetype is chosen. However, when I try and plot the waveform in the MainFrame class, I cannot for the life of me retrieve the 'wave'-variable. I have tried to inherit classes, set the 'wave'-variable global, and am out of ideas.
import wx, random, math, os
from pyo import *
from numpy import arange, sin, pi, sign
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
import matplotlib.pyplot as plt
class BasePanel(wx.Panel):
def __init__(self, parent, title):
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
self.SetMaxSize((230,150))
self.sliders = []
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.titleSizer = wx.BoxSizer(wx.HORIZONTAL)
self.close = wx.StaticText(self, id=-1, label="X")
self.close.Bind(wx.EVT_LEFT_DOWN, self.MouseDown)
self.titleSizer.Add(self.close, 0, wx.LEFT, 5)
self.title = wx.StaticText(self, id=-1, label=title)
off = (210 - self.title.GetSize()[0]) / 2
self.titleSizer.Add(self.title, 0, wx.LEFT, off)
self.sizer.Add(self.titleSizer, 0, wx.BOTTOM|wx.TOP, 4)
class ServerPanel(wx.Panel):
def __init__(self, parent, colour="#DDDDE7"):
wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER)
self.SetBackgroundColour(colour)
self.SetMinSize((230,250))
self.fileformat = self.wavetype = 0
self.title = wx.StaticText(self, id=-1, label="--- Simple Wave
Generator ---", pos=(40,5))
font, psize = self.title.GetFont(),
self.title.GetFont().GetPointSize()
font.SetPointSize(psize-2)
self.waveText =
wx.StaticText(self,id=-1,label="Waveform",pos(15,60))
self.popupWave = wx.Choice(self, id=wx.ID_ANY, pos=(13,70),
size(70,20), choices=["Sine","Square"])
self.popupWave.Bind(wx.EVT_CHOICE, self.changeWave)
objs = [self.waveText]
for obj in objs:
obj.SetFont(font)
def changeWave(self, evt):
global wave
wave = evt.GetInt()
if wave == 1: wave = 2
self.wavetype = wave
class MainFrame(wx.Frame,ServerPanel):
def __init__(self, parent=None, title=u"Sine Test", size=(250,300)):
wx.Frame.__init__(self, parent, id=-1, title=title, size=size)
self.server = Server().boot()
self.sizer = wx.FlexGridSizer(4,4,0,0)
self.panel = wx.Panel(self)
self.serverPanel = ServerPanel(self.panel)
self.sizer.Add(self.serverPanel)
self.panel.SetSizer(self.sizer)
pan = CanvasPanel(self.panel)
pan.draw()
self.Show()
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=-1, pos=wx.Point(100,60),
size=wx.Size(80, 50))
figsize = (80*1.0/80, 50*1.0/80)
self.figure = Figure(figsize, dpi=80)
self.axes = self.figure.add_subplot(111)
self.axes.set_axis_off()
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()
def draw(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
def drawSqr(self):
t = arange(0.0, 3.0, 0.01)
s = sign(sin(2 * pi * t))
self.axes.plot(t, s)
mainFrame = MainFrame()
app.MainLoop()
Ps: I know the "minimal working example" is not really minimal, but it's what I could get working for the moment. Please excuse a novice.
In the example I have hard coded a drawing to show in the MainFrame class, so you see where the drawing should be.
If we boil your code down a bit, remove un-needed classes, use sizers rather than mixing sizers and positional statements, clean up the indentation issues, remove undeclared items and unused imports.
We get something along these lines.
import wx
from numpy import arange, sin, pi, sign
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class MainFrame(wx.Frame):
def __init__(self, parent=None, title=u"Sine Test", size=(250,300)):
wx.Frame.__init__(self, parent, id=-1, title=title, size=size)
self.panel = wx.Panel(self,-1,style=wx.SUNKEN_BORDER)
self.panel.SetBackgroundColour("#DDDDE7")
self.panel.SetMinSize((230,250))
self.wavetype = 0
self.title = wx.StaticText(self.panel, id=-1, label="--- Simple Wave Generator ---")
font, psize = self.title.GetFont(), self.title.GetFont().GetPointSize()
font.SetPointSize(psize-2)
self.waveText = wx.StaticText(self.panel,id=-1,label="Waveform")
self.popupWave = wx.Choice(self.panel, id=wx.ID_ANY, size=(70,30), choices=["Sine","Square"])
self.popupWave.Bind(wx.EVT_CHOICE, self.changeWave)
self.pan = CanvasPanel(self)
self.sizer = wx.GridBagSizer(hgap=5,vgap=5)
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.title, pos=(0,0), span=(1,4), flag = wx.ALL|wx.ALIGN_CENTER)
self.sizer.Add(self.waveText, pos=(2,0), flag = wx.ALL)
self.sizer.Add(self.popupWave, pos=(3,0), flag = wx.ALL)
self.sizer.Add(self.pan, pos=(3,1), span=(3,3), flag = wx.ALL|wx.EXPAND)
self.panel.SetSizer(self.sizer)
self.main_sizer.Add(self.panel,1,wx.EXPAND)
self.SetSizer(self.main_sizer)
self.popupWave.SetSelection(0)
self.pan.wavetype = self.popupWave.GetSelection()
self.pan.draw()
self.Show()
def changeWave(self, evt):
wave = evt.GetSelection()
self.pan.wavetype = wave
self.pan.draw()
class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, id=-1, size=wx.Size(80, 50))
figsize = (80*1.0/80, 50*1.0/80)
self.figure = Figure(figsize, dpi=80)
self.wavetype = parent.wavetype
self.axes = self.figure.add_subplot(111)
self.axes.set_axis_off()
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()
def draw(self):
#Clear any previous data
self.axes.clear()
self.axes.set_axis_off()
#Plot new wave
if self.wavetype == 0:
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
else:
t = arange(0.0, 3.0, 0.01)
s = sign(sin(2 * pi * t))
self.axes.plot(t, s)
self.Layout()
app=wx.App()
mainFrame = MainFrame()
app.MainLoop()
It may not be exactly what you are after, so feel free to play with it!
Related
I'd like plot a figure(having many subfigures) in a scrolledwindow(or scrolledpanel).
figure width depends on the width of window and figure height is calculated like width * 2.
if the height of window ls less than width * 2, scrollbar of window is enabled.
with the following code, I checked the size of figure, canvas and window.
the height of figure and figure is bigger than the height of window. but scrollbar is not enabled.
I tried with boxsizer also, but it did not work.
Can you make this code work?
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigureCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
import wx
from wx.lib.scrolledpanel import ScrolledPanel
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=title,
pos=wx.DefaultPosition, size=wx.Size(200, 300))
# self.T_panel = wx.Panel(self)
# self.T_wd = wx.ScrolledWindow(self.T_panel, -1, wx.DefaultPosition,
# wx.DefaultSize, wx.VSCROLL|wx.ALWAYS_SHOW_SB)
self.T_wd = ScrolledPanel(self, -1, size=wx.Size(200, 300))
self.Bind(wx.EVT_SIZE, self.onSize)
self.T_wd.SetupScrolling()
self.T_fig = Figure((5.0,5.0), dpi=80)
self.T_canvas = FigureCanvas(self.T_wd, -1, self.T_fig)
self.graph = self.T_fig.add_subplot(111)
self.T_wd.Layout()
def onSize(self, event):
size = event.GetSize()
x = np.arange(5)
y = np.random.rand(5) * 5.
self.T_fig.set_size_inches(size[0]/80., size[0]/40.)
self.T_canvas.SetSize((size[0], size[0]*2))
self.graph.hold(False)
self.graph.plot(x,y)
self.T_canvas.draw()
self.T_wd.SetSize(size)
self.T_wd.SetupScrolling()
print 'event size', size
print 'fig size', self.T_fig.get_size_inches()*80
print 'canvas size', self.T_canvas.GetSize()
print 'window(panel) wize', self.T_wd.GetSize()
print ''
class my_App(wx.App):
def OnInit(self):
frame = MyFrame(None, title = 'test')
frame.Show(True)
self.SetTopWindow(frame)
return True
my_App().MainLoop()
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()
In the following example the scrollbar disappears after using the calculating button, although the layout function was called. If you manually resize the frame it reappears. This behavior occurs only under windows, in linux the scrollbar functions as it should.
To fix it I tried the functions refresh() and update() (in layout funtion of class GUI_Diagrams_GHL) - but it didn't help.
I tried to reduce my application to this minmal working example:
# -*- coding: utf-8 -*-
import wx
from wx.lib.pubsub import pub
import wx.lib.scrolledpanel as scrolled
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanvas
class MainWindow(wx.Frame):
'''Frame that contains pretty much everything'''
def __init__(self,*args,**kwargs):
'''Constructor'''
super(MainWindow,self).__init__(*args,**kwargs)
self.panel = wx.Panel(self)
notebook = Notebook(self.panel)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(notebook,1, wx.ALL|wx.EXPAND,4)
self.panel.SetSizerAndFit(sizer)
self.panel.Layout()
class Notebook(wx.Notebook):
def __init__(self, parent):
wx.Notebook.__init__(self, parent, id=wx.ID_ANY, style = wx.BK_DEFAULT)
tabTwo = GUI_Input_GHL(self)
self.AddPage(tabTwo, 'Input')
tabThree = GUI_Diagrams_GHL(self)
self.AddPage(tabThree, 'Diagrams')
class GUI_Input_GHL(scrolled.ScrolledPanel):
"""This panel contains the input fields for basic data."""
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent=parent, id=wx.ID_ANY)
self.label_1 = wx.StaticText(self,-1,label=u'Label 1')
self.button2 = wx.Button(self,-1,label=u'Start')
self.Bind(wx.EVT_BUTTON, self.StartCalc, self.button2)
self.layout()
def layout(self):
sizer = wx.GridBagSizer()
sizer.Add(self.button2, (8,0),(2,3), flag =wx.EXPAND)
sizer.Add(self.label_1, (0,0),flag=wx.ALIGN_CENTER_VERTICAL)
self.SetAutoLayout(1)
self.SetupScrolling()
self.SetSizerAndFit(sizer)
def StartCalc(self,event):
pub.sendMessage('GUI_Diagrams_Listener', message = 'test')
class GUI_Diagrams_GHL(scrolled.ScrolledPanel):
"""This panel contains diagrams"""
def __init__(self, parent):
scrolled.ScrolledPanel.__init__(self, parent=parent, id=wx.ID_ANY)
self.parent = parent
self.fig1 = Figure()
self.fig6 = Figure()
self.canvas1 = FigCanvas(self,-1,self.fig1)
self.axes1 = self.fig1.add_subplot(111)
self.canvas6 = FigCanvas(self,-1,self.fig6)
self.axes6 = self.fig6.add_subplot(111)
self.dia_R_hat_SetValues('test')
self.dia_theta_SetValues('test')
self.layout()
pub.subscribe(self.diagrams_SetValues, "GUI_Diagrams_Listener")
def layout(self):
sizer = wx.GridBagSizer()
sizer.Add(self.canvas1, (1,0), (12,12), wx.EXPAND)
sizer.Add(self.canvas6, (53,0), (12,12), wx.EXPAND)
## I guess here is the problem somewhere:
self.SetSizerAndFit(sizer)
self.SetAutoLayout(1)
self.SetupScrolling()
#self.Fit()
#self.Layout()
#self.FitInside()
#self.AlwaysShowScrollbars(True,True)
#self.Refresh()
#self.Update()
#self.parent.SetSize(self.parent.GetSize())
def diagrams_SetValues(self, message):
self.Output = message
self.dia_R_hat_SetValues(message)
self.dia_theta_SetValues(message)
self.layout()
def dia_R_hat_SetValues(self, Output):
self.axes1.clear()
self.axes1.plot(range(15),range(15), 'r-', linewidth = 2)
self.canvas1.draw()
def dia_theta_SetValues(self, Output):
self.axes6.clear()
self.axes6.plot(range(5),'k')
self.axes6.set_title(r"Absolute Temperature")
self.canvas6.draw()
if __name__ == '__main__':
app = wx.App()
frame = MainWindow(None, -1, 'MyApp')
frame.Show()
app.MainLoop()
I've figured it out myself :)
If you call the layout function of the main panel ('MainWindow.panel.Layout()') after updating the diagrams/in the layout of the diagram class, the whole panel refreshes and
the Scrollbars reappear. So a 'parent.parent.Layout()' worked for me in this case.
Any other suggestions/solutions?
I had the same problem like here.
It works, but this figure shall not cover the whole frame; so I changed size and position of his panel. Problem now is, that my figure still has the same size of my frame and therefore I only changed the panelsize, my figure gets "cut". How can I solve this?
Example for better understanding: My Frame is 800x600 and my figure shall only have the size 400x300 inside this frame.
edit:
import wx
from numpy import arange, sin, pi
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
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Pyramid App",size=(800,600),pos=((wx.DisplaySize()[0]-800)/2,(wx.DisplaySize()[1]-600)/2),style= wx.SYSTEM_MENU | wx.CAPTION | wx.MINIMIZE_BOX | wx.CLOSE_BOX)
self.PageThree = pageThree(self)
class pageThree(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,size=(800,525))
self.myparent=parent
self.pageThree=wx.Panel(self,size=(800,525)) # put some elements at this panel
self.pylabfigure = wx.Panel(self,size=(440,420),pos=(350,105)) # this is the panel for my figure
self.drawPylabFigure()
self.draw()
def drawPylabFigure(self):
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self.pylabfigure, -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()
def draw(self):
#example drawing
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()
This will give you an idea of how to do it.
Main thing to have into account it to put your panels/canvas organized by one sizer.
Also, if the canvas has to fill his side there is not need to put it in a panel.
import wx
from numpy import arange, sin, pi
import matplotlib
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FCW
from matplotlib.figure import Figure
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY,
"Pyramid App",size=(800,600),
pos=((wx.DisplaySize()[0]-800)/2,(wx.DisplaySize()[1]-600)/2),
style=wx.SYSTEM_MENU | wx.CAPTION | wx.MINIMIZE_BOX | wx.CLOSE_BOX)
self.PageThree = pageThree(self)
class pageThree(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent=parent,size=(800,525))
self.myparent=parent
self.pageThree=wx.Panel(self, size=(500,525))
self.drawPyFigure()
self.draw()
def drawPyFigure(self):
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FCW(self, -1, self.figure)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.pageThree, 0)
sizer.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(sizer)
self.Fit()
def draw(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
if __name__ == "__main__":
app = wx.App()
MainFrame().Show()
app.MainLoop()
In a nutshell, you want self.figure = Figure(figsize=(400 / 80.0, 300 / 80.0)).
You need to specify the size of the figure (by default 8 inches wide by 6 inches tall) and/or the dpi of the figure (by default, 80 for on-screen display).
However, be aware that the size of the figure on-screen depends purely on the total number of pixels (in other words, width_inches*dpi x height_inches*dpi). For that reason, if we want a figure to be a specific number of pixels, we need to convert that to "inches" (which have nothing to do with the figure's display size if it's displayed on-screen) by dividing the number of pixels by the figure's dpi.
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()