Matplotlib Widgets - Drop down boxes

Hi all, I have been looking at the Matplotlib widget examples

    > particularly the Slider one and then looking at the widgets
    > available in matplotlib.widgets. I noticed that there are no
    > list or drop down widgets available? Is this something that
    > is available elsewhere in Matplotlib or something that needs
    > to be added? It would be extremely convenient to be able to
    > use drop down boxes in plots to control the data going into
    > the plot.

    > Does anyone have any thoughts or suggestions. Is this
    > something others are interested in or already working on? Is
    > there a technical difficulty that drop downs present that has
    > caused people to be hesitate to add them?

Hey Tom,

The GUI neutral widgets we have already were either added because we
needed them (sliders to support the new subplot tool) or as
proof-of-concept (check and radio buttons). Others can be added as
needed. The most pressing one in my view is a text entry box, which
has been delayed because of an idiosyncrasy in the way mpl aligns text
in the y direction (we have 'top', 'bottom' and 'center' but we need
'baseline').

The other technical difficulty in drop downs is efficient blitting of
rectangular regions of the canvas, which we have just begun to solve.
See http://www.scipy.org/wikis/topical_software/Animations . GTKagg
supports region blitting in the last release, and TkAgg does in CVS
(thanks to Charles Moad).

So yes, a drop down would be useful and welcome. If you feel up to
tackling this, by all means do!

JDH

PS

I have a proof-of-concept text box implemented for GTKAgg, which I'll
include below. If you type some random text into it, you may better
understand my point about aligning to baseline. For example, if you
type "This is a recording" notice what happens when you type "g".
You will need a backend that supports blitting.

from pylab import *
from matplotlib.transforms import lbwh_to_bbox, identity_transform

class TextBox(Widget):
    def __init__(self, ax, s=''):
        self.canvas = ax.figure.canvas
        self.text = ax.text(0.025, 0.2, s,
                            fontsize=14,
                            #verticalalignment='baseline',
                            horizontalalignment='left',
                            transform=ax.transAxes)
        self.ax = ax
        ax.set_yticks([])
        ax.set_xticks([])
        
        ax.set_navigate(False)
        self.canvas.draw()
        self.canvas.mpl_connect('key_press_event', self.keypress)

        self.region = self.canvas.copy_from_bbox(ax.bbox)
        
        r = self._get_text_right()
        self.cursor, = ax.plot([r,r], [0.2, 0.8], transform=ax.transAxes)
        self.redraw()

    def redraw(self):
        self.ax.redraw_in_frame()
        self.canvas.blit(self.ax.bbox)

    def keypress(self, event):
        if event.key is not None and len(event.key)>1: return

        t = self.text.get_text()
        if event.key is None: # simulate backspace
            if len(t): newt = t[:-1]
            else: newt = ''
        else:
            newt = t + event.key
            
        self.text.set_text(newt)

        r = self._get_text_right()
        self.cursor.set_xdata([r,r])
        self.redraw()

    def _get_text_right(self):
        l,b,w,h = self.text.get_window_extent().get_bounds()
        r = l+w+2
        t = b+h
        s = self.text.get_text()
        # adjust cursor position for trailing space
        numtrail = len(s) - len(s.rstrip())
        en = self.ax.get_renderer_cache().points_to_pixels(self.text.get_fontsize())/2.

        r += numtrail*en
        l,b = self.ax.transAxes.inverse_xy_tup((l,b))
        r,t = self.ax.transAxes.inverse_xy_tup((r,t))
        return r
        
ion()
f = figure()
ax = axes([0.1, 0.1, 0.8, 0.7])
plot([1,2,3])

#rc('text', usetex=1)
f.text(0.39, 0.875, 'My label: ',
       horizontalalignment='right', verticalalignment='center')
axtext = axes([0.4, 0.85, 0.5, 0.05])

draw()
box = TextBox(axtext)

show()

Thanks John this is really helpful.

I think even the text box would be useful. I'll take a look at the
latest release.

···

On 8/10/05, John Hunter <jdhunter@...8...> wrote:

   > Hi all, I have been looking at the Matplotlib widget examples
   > particularly the Slider one and then looking at the widgets
   > available in matplotlib.widgets. I noticed that there are no
   > list or drop down widgets available? Is this something that
   > is available elsewhere in Matplotlib or something that needs
   > to be added? It would be extremely convenient to be able to
   > use drop down boxes in plots to control the data going into
   > the plot.

   > Does anyone have any thoughts or suggestions. Is this
   > something others are interested in or already working on? Is
   > there a technical difficulty that drop downs present that has
   > caused people to be hesitate to add them?

Hey Tom,

The GUI neutral widgets we have already were either added because we
needed them (sliders to support the new subplot tool) or as
proof-of-concept (check and radio buttons). Others can be added as
needed. The most pressing one in my view is a text entry box, which
has been delayed because of an idiosyncrasy in the way mpl aligns text
in the y direction (we have 'top', 'bottom' and 'center' but we need
'baseline').

The other technical difficulty in drop downs is efficient blitting of
rectangular regions of the canvas, which we have just begun to solve.
See http://www.scipy.org/wikis/topical_software/Animations . GTKagg
supports region blitting in the last release, and TkAgg does in CVS
(thanks to Charles Moad).

So yes, a drop down would be useful and welcome. If you feel up to
tackling this, by all means do!

JDH

PS

I have a proof-of-concept text box implemented for GTKAgg, which I'll
include below. If you type some random text into it, you may better
understand my point about aligning to baseline. For example, if you
type "This is a recording" notice what happens when you type "g".
You will need a backend that supports blitting.

from pylab import *
from matplotlib.transforms import lbwh_to_bbox, identity_transform

class TextBox(Widget):
   def __init__(self, ax, s=''):
       self.canvas = ax.figure.canvas
       self.text = ax.text(0.025, 0.2, s,
                           fontsize=14,
                           #verticalalignment='baseline',
                           horizontalalignment='left',
                           transform=ax.transAxes)
       self.ax = ax
       ax.set_yticks([])
       ax.set_xticks([])

       ax.set_navigate(False)
       self.canvas.draw()
       self.canvas.mpl_connect('key_press_event', self.keypress)

       self.region = self.canvas.copy_from_bbox(ax.bbox)

       r = self._get_text_right()
       self.cursor, = ax.plot([r,r], [0.2, 0.8], transform=ax.transAxes)
       self.redraw()

   def redraw(self):
       self.ax.redraw_in_frame()
       self.canvas.blit(self.ax.bbox)

   def keypress(self, event):
       if event.key is not None and len(event.key)>1: return

       t = self.text.get_text()
       if event.key is None: # simulate backspace
           if len(t): newt = t[:-1]
           else: newt = ''
       else:
           newt = t + event.key

       self.text.set_text(newt)

       r = self._get_text_right()
       self.cursor.set_xdata([r,r])
       self.redraw()

   def _get_text_right(self):
       l,b,w,h = self.text.get_window_extent().get_bounds()
       r = l+w+2
       t = b+h
       s = self.text.get_text()
       # adjust cursor position for trailing space
       numtrail = len(s) - len(s.rstrip())
       en = self.ax.get_renderer_cache().points_to_pixels(self.text.get_fontsize())/2.

       r += numtrail*en
       l,b = self.ax.transAxes.inverse_xy_tup((l,b))
       r,t = self.ax.transAxes.inverse_xy_tup((r,t))
       return r

ion()
f = figure()
ax = axes([0.1, 0.1, 0.8, 0.7])
plot([1,2,3])

#rc('text', usetex=1)
f.text(0.39, 0.875, 'My label: ',
      horizontalalignment='right', verticalalignment='center')
axtext = axes([0.4, 0.85, 0.5, 0.05])

draw()
box = TextBox(axtext)

show()