Adding axis labels and toolbar to embedding_in_tk.py

Quite impressed with matplotlib but the learning curve is

    > steep and I am feeling my way along a tortuous cave in
    > dim light!!

Well it appears you are doing a good job of it. Fortunately, there's
often someone around here to strike a match if you lose your way.
Documentation is scant, especially on the OO / application embedding
side. Make sure you have at least read through
examples/pythonic_matplotlib.py in the examples subdirectory of the
source distribution, and here is a link to a draft version of the
users guide

But these will be no substitute for opening up
matplotlib/backends/backend_tkagg.py and learning by example. Ie, if
you want to figure out how to embed a toolbar in tkagg, the best
reference is often the source code, which does just that.

Well, often, but not always. This list is another good resource to
turn to. I myself did not know how to do it for tkagg since I didn't
write that backend. So I read through the source and found that the
tkagg toolbar in the current implementation is actually weakly tied to
the pylab interface in that is uses a variable called figman, which is
a reference to the "FigureManager" which is a class pylab uses to
manage figures.

No worries, it was trivial to factor out this dependence, and at the
end of this email, I'll attach a modified backend_tkagg.py which you
can drop into site-packages/matplotlib/backends that enables the
toolbar to work with matplotlib embedded in tk apps. It was a trivial
change. I'll also attach some example code showing you how to use
it.

The example code includes the exact toolbar that matplotlib uses. Of
course, you'll probably want to customize the toolbar, maybe to add
some widgets of your own. To do that, you'll want to subclass
NavigationToolbar2TkAgg, override the default _init_toolbar method to
create the buttons and widgets you need, and define functions that
will be called when your custom buttons are clicked, eg myfunction
below. Since you're an avowed OO newbie, I'll give a sketch of that
approach below, which is untested code meant merely to shine a dim
light. But the example I'm including below, embedding_in_tk2.py, does
work, and adds the default matplotlib toolbar to a tk/matplotlib app.
Surprisingly, you appear to be the first person to ever attempt to
embed the tkagg toolbar2 into your own app.

So here is how you might go about customizing a toolbar

from backend_tkagg import NavigationToolbar2TkAgg

class MyToolbar(NavigationToolbar2TkAgg)
    def _init_toolbar(self):
        # this was all copied verbatim from backend_tkagg.py
        xmin, xmax = self.canvas.figure.bbox.intervalx().get_bounds()
        height, width = 50, xmax-xmin
        Tk.Frame.__init__(self, master=self.window,
                          width=width, height=height,
                          borderwidth=2)
        
        self.update() # Make axes menu

        self.bHome = self._Button( text="Home", file="home.ppm",
                                   command=self.home)

        self.bBack = self._Button( text="Back", file="back.ppm",
                                   command = self.back)
        
        self.bForward = self._Button(text="Forward", file="forward.ppm",
                                     command = self.forward)

        self.bPan = self._Button( text="Pan", file="move.ppm",
                                  command = self.pan)

        self.bZoom = self._Button( text="Zoom",
                                   file="zoom_to_rect.ppm",
                                   command = self.zoom)

        self.bsave = self._Button( text="Save", file="filesave.ppm",
                                   command = self.save_figure)

        ### now I'm going to add a custom button that calls myfunction
        self.mybutton = self._Button( text="Save", file="myicon.ppm",
                                      command = self.myfunction)
        self.message = Tk.StringVar(master=self)
        self._message_label = Tk.Label(master=self, textvariable=self.message)
        self._message_label.pack(side=Tk.RIGHT)
        self.pack(side=Tk.BOTTOM, fill=Tk.X)

    def myfunction(self, *args):
        # this function is called when "mybutton" is clicked
        print "You clicked me!"

Now, all you need to do is create a MyToolbar instance rather than a
NavigationToolbar2Tkagg instance in the example code
embedding_in_tk2.py.

    > After 41 years in software development, I get to OOP and
    > Python!! Most of my usage has and will be Fortran but I
    > have a smattering of C (a few months on an 8-bit
    > machine), a log of PL/1 a long time ago, some PDP 8-I
    > assembly, and Basic. I have not used C++ and do not plan
    > to do so.

It's a long hard road I plowed myself. I cut my teeth on an A/D
controller and 8 channel digital oscilloscope that I wrote entirely
from scratch in quick basic -- not a single external library to help
me out -- with which I did all my experiments for my dissertation. I
also did a lot of numerical analysis purely in FORTRAN in those days.
Unlike you, I did willingly learn C++ after all that and it was
sunlight to me after years in the dark -- it appeared designed to
solve all the problems I had experienced firsthand in my years of
coding BASIC and FORTRAN. But you do predate me - I've never touched
a PL/1 or PDP 8.

Anyway, you may find yourself backing away from the "and will be" part
of your statement above. Time will tell.

Hope this helps - feel free to post again when and if you get stuck.

JDH

backend_tkagg.py (21.2 KB)

embedding_in_tk2.py (910 Bytes)

Thanks John, I got that working and spent some time fiddling
with the classic toolbar as well. It seems to fit my current
needs better thant the new toolbar. However, it appears to
be a bit more tied to the pylab interface. My trials at extraplating
from the changes you made for the new interface foundered. I need
to spend more time getting up to speed on other items, such as adding
menus for user file selection, etc.

My application involves potentially long time series, perhaps months
or even years long, of flow or water-surface elevation. It would
be nice to be able to quickly pan through the series at a large scale
looking for details that might need correction or where the modeling
does an especially poor job.

I'm continuing to poke and prode on the source and the draft guide has
helped out on getting a better overview on what is going on. Clearly
OOP can be obscure at first. It is sometimes unclear where the actual
work gets done.

···

On Saturday 18 December 2004 08:25 pm, John Hunter wrote:

    > Quite impressed with matplotlib but the learning curve is
    > steep and I am feeling my way along a tortuous cave in
    > dim light!!

Well it appears you are doing a good job of it. Fortunately, there's
often someone around here to strike a match if you lose your way.
Documentation is scant, especially on the OO / application embedding
side. Make sure you have at least read through
examples/pythonic_matplotlib.py in the examples subdirectory of the
source distribution, and here is a link to a draft version of the
users guide
http://cvs.sourceforge.net/viewcvs.py/*checkout*/matplotlib/users_guide/users_guide.pdf?rev=1.3

But these will be no substitute for opening up
matplotlib/backends/backend_tkagg.py and learning by example. Ie, if
you want to figure out how to embed a toolbar in tkagg, the best
reference is often the source code, which does just that.

Well, often, but not always. This list is another good resource to
turn to. I myself did not know how to do it for tkagg since I didn't
write that backend. So I read through the source and found that the
tkagg toolbar in the current implementation is actually weakly tied to
the pylab interface in that is uses a variable called figman, which is
a reference to the "FigureManager" which is a class pylab uses to
manage figures.

No worries, it was trivial to factor out this dependence, and at the
end of this email, I'll attach a modified backend_tkagg.py which you
can drop into site-packages/matplotlib/backends that enables the
toolbar to work with matplotlib embedded in tk apps. It was a trivial
change. I'll also attach some example code showing you how to use
it.

The example code includes the exact toolbar that matplotlib uses. Of
course, you'll probably want to customize the toolbar, maybe to add
some widgets of your own. To do that, you'll want to subclass
NavigationToolbar2TkAgg, override the default _init_toolbar method to
create the buttons and widgets you need, and define functions that
will be called when your custom buttons are clicked, eg myfunction
below. Since you're an avowed OO newbie, I'll give a sketch of that
approach below, which is untested code meant merely to shine a dim
light. But the example I'm including below, embedding_in_tk2.py, does
work, and adds the default matplotlib toolbar to a tk/matplotlib app.
Surprisingly, you appear to be the first person to ever attempt to
embed the tkagg toolbar2 into your own app.

So here is how you might go about customizing a toolbar

from backend_tkagg import NavigationToolbar2TkAgg

class MyToolbar(NavigationToolbar2TkAgg)
    def _init_toolbar(self):
        # this was all copied verbatim from backend_tkagg.py
        xmin, xmax = self.canvas.figure.bbox.intervalx().get_bounds()
        height, width = 50, xmax-xmin
        Tk.Frame.__init__(self, master=self.window,
                          width=width, height=height,
                          borderwidth=2)
        
        self.update() # Make axes menu

        self.bHome = self._Button( text="Home", file="home.ppm",
                                   command=self.home)

        self.bBack = self._Button( text="Back", file="back.ppm",
                                   command = self.back)
        
        self.bForward = self._Button(text="Forward", file="forward.ppm",
                                     command = self.forward)

        self.bPan = self._Button( text="Pan", file="move.ppm",
                                  command = self.pan)

        self.bZoom = self._Button( text="Zoom",
                                   file="zoom_to_rect.ppm",
                                   command = self.zoom)

        self.bsave = self._Button( text="Save", file="filesave.ppm",
                                   command = self.save_figure)

        ### now I'm going to add a custom button that calls myfunction
        self.mybutton = self._Button( text="Save", file="myicon.ppm",
                                      command = self.myfunction)
        self.message = Tk.StringVar(master=self)
        self._message_label = Tk.Label(master=self, textvariable=self.message)
        self._message_label.pack(side=Tk.RIGHT)
        self.pack(side=Tk.BOTTOM, fill=Tk.X)

    def myfunction(self, *args):
        # this function is called when "mybutton" is clicked
        print "You clicked me!"

Now, all you need to do is create a MyToolbar instance rather than a
NavigationToolbar2Tkagg instance in the example code
embedding_in_tk2.py.

    > After 41 years in software development, I get to OOP and
    > Python!! Most of my usage has and will be Fortran but I
    > have a smattering of C (a few months on an 8-bit
    > machine), a log of PL/1 a long time ago, some PDP 8-I
    > assembly, and Basic. I have not used C++ and do not plan
    > to do so.

It's a long hard road I plowed myself. I cut my teeth on an A/D
controller and 8 channel digital oscilloscope that I wrote entirely
from scratch in quick basic -- not a single external library to help
me out -- with which I did all my experiments for my dissertation. I
also did a lot of numerical analysis purely in FORTRAN in those days.
Unlike you, I did willingly learn C++ after all that and it was
sunlight to me after years in the dark -- it appeared designed to
solve all the problems I had experienced firsthand in my years of
coding BASIC and FORTRAN. But you do predate me - I've never touched
a PL/1 or PDP 8.

Anyway, you may find yourself backing away from the "and will be" part
of your statement above. Time will tell.

Hope this helps - feel free to post again when and if you get stuck.

JDH