more on plugins..

Hi. I realized that having plugins loaded from the

    > __init__.py file could cause dependency problems,
    > depending on what was being done in the plugin. So I
    > moved the code to a separate file called 'plugins.py'
    > (attached). This also allows you to decide whether you
    > want to load the plugins in the first place. It's also a
    > cleaner solution in general..

    > It's pretty rough code hacked together during wait
    > time. I also wrote a quick 'print button' plugin (also
    > attached) for proof of concept. The plugin only works
    > with GTK right now, but it checks the backend being used,
    > so it should be fairly simple to hack the other
    > GUI/platform backends to use it.

    > I'm interested in what people think.. Even if it doesn't
    > get included in the main source, it's easy enough to add
    > in, that upgrades will no longer be a problem.

I tried out your code and have a few questions.

Your example only works on the current figure toolbar - it this
desirable, or would it be better to modify the default toolbar? Also,
how do you use it? After your figure is realized, do you call

>>> load_plugins(rcParams)

to get your buttons? That seems like it would be cumbersome to have
to do this with every figure....

And why does load_plugins take rcParams as an arg rather than work on
the rcParams default? Do you plan on keeping different params with a
different plugins directories?

I may not be understanding how your code is supposed to work, but I
was envisioning something a little different after reading your
original post. I thought something along the lines of the following
in rc

# format is Name, Tooltip, image basename, callback
plugins.dirs : /path/to/plugins1:/path/to/plugins2
toolbar.button : Home, Reset original view, home_icon, mybackend.home
toolbar.button : Back, Back to previous view, back_icon, mybackend.back
toolbar.button : Forward, Forward to next view, forward_icon, mybackend.forward
toolbar.button : Pan, Pan axes with left mouse, zoom with right, move_icon, mybackend.pan
toolbar.button : Zoom, Zoom to rectangle, zoom_to_rect, mybackend.zoom
toolbar.button : Print, Print to PS, print_icon, plugins.print_ps

The order of the toolbar.button calls would determine which buttons
were placed in the toolbar and in what order. mybackend would be a
special string that matplotlib would use to substitute in for the
current backend, and each backend would implement these functions.

plugins (in the example plugins.print_ps) is a python code dir
somewhere in the plugins.dirs search path. Thus the same code could
be used to both build the default toolbar and used to customize it
(remove buttons, change their order, add user buttons).

I used icon names without extensions above because the different
backends generally need icons of different types, and the backend
could be responsible for supplying the extension. GTK could also
detect special names like STOCK_PRINT and handle those.

One thing this setup doesn't provide for is the ability to easily
switch to different configurations in the middle of an interactive
session. Is this important?

Another design issue that comes to mind is how to handle communication
between buttons. In the current toolbar2 design, when the zoom to
rectangle button is pressed, the pan/zoom callbacks are disconnected
and vis-a-versa. Presumably a more sophisticated model would have to
be used to handle the connection and disconnection of multiple buttons
that want to listen in on press/motion events.

It would provide a nice framework for supporting user contributions,
eg measurement tools, other navigation tools, etc...

JDH

I hadn't put much thought about passing rcParams as a parameter, as load_plugins wasn't supposed to be called manually, but rather called automatically when importing 'matplotlib.plugins'.

The code I had sent previously was more proof of concept of what could be done with the plugin system. I like your ideas of how to add buttons to the toolbar, so I implemented it last night. Now in the .matplotlibrc file you can do:

toolbar.widget : print, print, prints figure, mprint, print_button
toolbar.connect : print.clicked -> nothing.do_toggle
toolbar.widget : nothing, nothing, does nothing, mprint, nothing_button

There are several things happening here:
(1) I had to alter __init__.py some, so that rcparams acted more like opt.parse. The default behavior is the exact same as usual, except that there is an addition. Normally for an entry you have:
[None, validate_x]

There is now an extra optional field, which defaults to 'store', but can also take on the value of 'append':
[None, validate_x, 'append']

Thus, several entries of 'toolbar.widget', will add append each entry to a big list.

(2) The fields of 'toolbar.widget' are:
<name>, <text>, <tooltip>, <module>, <class>

The name is needed for the toolbar.connect, which I will get to shortly. The text and tooltip, are the same as before. The module is required, as although it is loaded as plugin, the toolbar code that creates widgets from these, still needs to import something. The class is a child class of 'toolbar_widget'.

(3) The reason for creating a 'toolbar_widget' class, is to handle the complexity of signalling between buttons that you mention. Basically it is your generic signal/slot design, where one widget's signal can be connected to another widget's slot. Thus the line:

toolbar.connect: print.clicked -> nothing.do_toggle

This uses the named widget (<name>) to connect it's signal (clicked) to the other widget's slot (do_toggle).

While currently the code only supports buttons, I thought it might be wise to allow other widget types. The backends can figure out what to render on the class type.

(4) backend_bases.py: added a 'widgets' dict, that is loaded with _init_toolbar from the rcParams files. The connections are then iterated through, so the newly loaded widgets are all connected correctly.

(5) backend_gtk: changed to work with the new backend_bases.py widget .. basically just iterates through 'self.widgets', and adds them to the toolbar

All the code (hopefully, let me know if it's not working/I left something out) can be found:
http://www.cns.nyu.edu/~abes/matplotlib/

I included a patch in the file for all the changes to the code, which should hopefully work. As well, the new files created, and changed files are also in the directory:
* mprint.py: gets placed in your plugin directory, and defines the print_button, and nothing_button.
* plugins.py: the plugin code
* toolbar_widget.py: defines the toolbar_widget's, which are used in mprint

The code is still pretty rough, and small things like allowing for more than one plugin directory hasn't yet been addressed (or going through them recursively).

As far as changing the configuration midsession, the toolbar would have to be reinitialized.

Abe

···

I tried out your code and have a few questions.

Your example only works on the current figure toolbar - it this
desirable, or would it be better to modify the default toolbar? Also,
how do you use it? After your figure is realized, do you call

>>> load_plugins(rcParams)

to get your buttons? That seems like it would be cumbersome to have
to do this with every figure....

And why does load_plugins take rcParams as an arg rather than work on
the rcParams default? Do you plan on keeping different params with a
different plugins directories?

I may not be understanding how your code is supposed to work, but I
was envisioning something a little different after reading your
original post. I thought something along the lines of the following
in rc

# format is Name, Tooltip, image basename, callback
plugins.dirs : /path/to/plugins1:/path/to/plugins2
toolbar.button : Home, Reset original view, home_icon, mybackend.home
toolbar.button : Back, Back to previous view, back_icon, mybackend.back
toolbar.button : Forward, Forward to next view, forward_icon, mybackend.forward
toolbar.button : Pan, Pan axes with left mouse, zoom with right, move_icon, mybackend.pan
toolbar.button : Zoom, Zoom to rectangle, zoom_to_rect, mybackend.zoom
toolbar.button : Print, Print to PS, print_icon, plugins.print_ps

The order of the toolbar.button calls would determine which buttons
were placed in the toolbar and in what order. mybackend would be a
special string that matplotlib would use to substitute in for the
current backend, and each backend would implement these functions.

plugins (in the example plugins.print_ps) is a python code dir
somewhere in the plugins.dirs search path. Thus the same code could
be used to both build the default toolbar and used to customize it
(remove buttons, change their order, add user buttons).

I used icon names without extensions above because the different
backends generally need icons of different types, and the backend
could be responsible for supplying the extension. GTK could also
detect special names like STOCK_PRINT and handle those.

One thing this setup doesn't provide for is the ability to easily
switch to different configurations in the middle of an interactive
session. Is this important?

Another design issue that comes to mind is how to handle communication
between buttons. In the current toolbar2 design, when the zoom to
rectangle button is pressed, the pan/zoom callbacks are disconnected
and vis-a-versa. Presumably a more sophisticated model would have to
be used to handle the connection and disconnection of multiple buttons
that want to listen in on press/motion events.

It would provide a nice framework for supporting user contributions,
eg measurement tools, other navigation tools, etc...

JDH

-------------------------------------------------------
SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media
100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33
Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift.
http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel