Hi!
A few weeks ago I started using Python, Matplotlib,
and WxPython for a new project, and continue to be quite impressed. I now ran
into a problem however, which I cannot solve in a decent way: I am using a slightly
more complicated GUI than the ones in the examples, and positioned a Matplotlib
canvas and the toolbar inside a sizer, which is not the “root”-most
sizer (in the init constructor of my wx.Frame-derived class I call “self.SetSizer(sizer_A)”
while I add the Matplotlib canvas to a different sizer_B, kind of like “self.sizer_B.Add(self.canvas,
1, wxTOP | wxLEFT | wxEXPAND)”, with sizer_B being added via intermediate
objects to sizer_A).
What now happens isn’t completely clear to me –
but it looks as if the events aren’t handled correctly anymore (nothing at
all happens, if I use the custom sizing given in one of the examples; if I call
self.SetToolBar(self.toolbar) instead, some of the buttons work, I think). If I
position both the canvas and the toolbar in the “root”-most sizer
instead, everything runs fine. Does anyone know a way to solve this?
I am using WxGlade to write some of the GUI code,
because it seems to save some typing work. Below, I added a simple example of
the problem I encounter. I basically combined the “embedding_in_wx4.py”
with some WxGlade-created code (the frame contains a sizer_A with a button and
a sizer_2 which contains the sizer_B that in turn contains the canvas. Of
course, this setup would be possible without this structure, it is just meant to
illustrate…)
Does someone also have some input on WxGlade or
other choices that are out there? What about Boa Constructor? Is that any good?
Currently I am using SPE…
Thanks a lot in advance for your help,
Michael Sielemann
______ example code (not working like this, to
illustrate the problem I am encountering ________________
#!/usr/bin/env python
-- coding: ISO-8859-1 --
generated by wxGlade 0.3.5.1 on Tue Jan 18
17:42:12 2005
from matplotlib.numerix import arange, sin, pi
import matplotlib
uncomment the following to use wx rather than
wxagg
#matplotlib.use(‘WX’)
#from matplotlib.backends.backend_wx import
FigureCanvasWx as FigureCanvas
comment out the following to use wx rather than
wxagg
matplotlib.use(‘WXAgg’)
from matplotlib.backends.backend_wxagg import
FigureCanvasWxAgg as FigureCanvas
from matplotlib.backends.backend_wx import
NavigationToolbar2Wx
from matplotlib.backends.backend_wx import
_load_bitmap
from matplotlib.figure import Figure
from matplotlib.numerix import rand
import wx
class MyNavigationToolbar(NavigationToolbar2Wx):
"""
Extend the default wx toolbar
with your own event handlers
"""
ON_CUSTOM = wx.NewId()
def __init__(self, canvas,
cankill):
NavigationToolbar2Wx.init(self, canvas)
# for
simplicity I’m going to reuse a bitmap from wx, you’ll
···
#
probably want to add your own.
self.AddSimpleTool(self.ON_CUSTOM, _load_bitmap(‘stock_left.xpm’),
‘Click me’, ‘Activate custom contol’)
EVT_TOOL(self, self.ON_CUSTOM, self._on_custom)
def _on_custom(self, evt):
# add
some text to the axes in a random location in axes (0,1)
# coords)
with a random color
# get the
axes
ax =
self.canvas.figure.axes[0]
#
generate a random location can color
x,y =
tuple(rand(2))
rgb =
tuple(rand(3))
# add the
text and draw
ax.text(x, y, ‘You clicked me’,
transform=ax.transAxes,
color=rgb)
self.canvas.draw()
evt.Skip()
class MyFrame(wx.Frame):
def __init__(self, *args,
**kwds):
# begin
wxGlade: MyFrame.init
kwds[“style”] = wx.DEFAULT_FRAME_STYLE
wx.Frame.init(self, *args, **kwds)
self.panel_1 = wx.Panel(self, -1)
self.panel_2 = wx.Panel(self.panel_1, -1)
self.button_1 = wx.Button(self.panel_1, -1, “Useless”)
# moved
downwards
#self.__set_properties()
#self.__do_layout()
# end
wxGlade
#
“embedding_in_wx4.py” sample code integration
self.SetBackgroundColour(wxNamedColor(“WHITE”))
self.figure = Figure(figsize=(5,4), dpi=100)
self.axes
= self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
t =
arange(0.0,3.0,0.01)
s =
sin(2pit)
self.axes.plot(t,s)
self.toolbar = MyNavigationToolbar(self.canvas, True)
self.__set_properties()
self.__do_layout()
# end of
“embedding_in_wx4.py” sample code integration
def __set_properties(self):
# begin
wxGlade: MyFrame.__set_properties
self.SetTitle(“frame_1”)
self.SetSize((600, 500))
# end
wxGlade
#
“embedding_in_wx4.py” sample code integration
self.toolbar.Realize()
# end of
“embedding_in_wx4.py” sample code integration
def __do_layout(self):
# begin
wxGlade: MyFrame.__do_layout
sizer_A =
wx.BoxSizer(wx.VERTICAL)
sizer_2 =
wx.BoxSizer(wx.HORIZONTAL)
# next
lines were modified by hand
self.sizer_B = wxBoxSizer(wxVERTICAL)
self.sizer_B.Add(self.canvas, 1, wxTOP | wxLEFT | wxEXPAND)
sizer_2.Add(self.sizer_B, 2, wxTOP | wxLEFT | wxEXPAND, 0)
sizer_2.Add(self.button_1, 1, wx.EXPAND|wx.FIXED_MINSIZE, 0)
self.panel_1.SetAutoLayout(True)
self.panel_1.SetSizer(sizer_2)
sizer_2.Fit(self.panel_1)
sizer_2.SetSizeHints(self.panel_1)
sizer_A.Add(self.panel_1, 1, wx.EXPAND, 0)
#self.SetAutoLayout(True)
#self.SetSizer(sizer_A)
#self.Layout()
# end
wxGlade
#
“embedding_in_wx4.py” sample code integration
if
wxPlatform == ‘WXMAC’:
Mac platform (OSX 10.3, MacPython) does not seem to cope with
having a toolbar in a sizer. This work-around gets the buttons
back, but at the expense of having the toolbar at the top
self.SetToolBar(self.toolbar)
else:
On Windows platform, default window size is incorrect, so set
toolbar width to figure width.
tw, th = self.toolbar.GetSizeTuple()
fw, fh = self.canvas.GetSizeTuple()
By adding toolbar in sizer, we are able to put it at the bottom
of the frame - so appearance is closer to GTK version.
As noted above, doesn’t work for Mac.
self.toolbar.SetSize(wxSize(fw, th))
self.sizer_B.Add(self.toolbar, 0, wxLEFT | wxEXPAND)
# Capture
the paint message what was the use of
this?
#EVT_PAINT(self, self.OnPaint)
# update
the axes menu on the toolbar
self.toolbar.update()
#self.SetSizer(self.sizer)
#self.Fit()
# end of
“embedding_in_wx4.py” sample code integration
self.SetAutoLayout(True)
self.SetSizer(sizer_A)
self.Layout()
end of class MyFrame
class MyAtmosphereApp(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
frame_1 =
MyFrame(None, -1, “”)
self.SetTopWindow(frame_1)
frame_1.Show()
return 1
end of class MyAtmosphereApp
if name == “main”:
import gettext
gettext.install(“AtmosphereApp”) # replace with the appropriate
catalog name
# more of my init stuff
AtmosphereApp =
MyAtmosphereApp(0)
AtmosphereApp.MainLoop()