Revision: 6640
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6640&view=rev
Author:   leejjoon
Date:     2008-12-17 00:50:56 +0000 (Wed, 17 Dec 2008)

Log Message:
-----------
Another attempt to fix dpi-dependent behavior of Legend

Modified Paths:
--------------
    branches/v0_98_5_maint/CHANGELOG
    branches/v0_98_5_maint/lib/matplotlib/legend.py
    branches/v0_98_5_maint/lib/matplotlib/offsetbox.py

Modified: branches/v0_98_5_maint/CHANGELOG
===================================================================
--- branches/v0_98_5_maint/CHANGELOG    2008-12-16 22:26:11 UTC (rev 6639)
+++ branches/v0_98_5_maint/CHANGELOG    2008-12-17 00:50:56 UTC (rev 6640)
@@ -1,3 +1,5 @@
+2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL
+
 ======================================================================
 2008-12-16 Release 0.98.5.1 at r6636
 

Modified: branches/v0_98_5_maint/lib/matplotlib/legend.py
===================================================================
--- branches/v0_98_5_maint/lib/matplotlib/legend.py     2008-12-16 22:26:11 UTC 
(rev 6639)
+++ branches/v0_98_5_maint/lib/matplotlib/legend.py     2008-12-17 00:50:56 UTC 
(rev 6640)
@@ -34,7 +34,7 @@
 from matplotlib.collections import LineCollection, RegularPolyCollection
 from matplotlib.transforms import Bbox
 
-from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea
+from matplotlib.offsetbox import HPacker, VPacker, PackerBase, TextArea, 
DrawingArea
 
 
 class Legend(Artist):
@@ -207,11 +207,6 @@
         reps =  int(self.numpoints / len(self._scatteryoffsets)) + 1
         self._scatteryoffsets = np.tile(self._scatteryoffsets, 
reps)[:self.scatterpoints]
 
-        # handles & labels (which can be iterators) need to be
-        # explicitly converted to list.
-        self._handles_labels = list(handles), list(labels)
-
-
         # _legend_box is an OffsetBox instance that contains all
         # legend items and will be initialized from _init_legend_box()
         # method.
@@ -277,12 +272,13 @@
         self._set_artist_props(self.legendPatch)
 
         self._drawFrame = True
-
+        
         # init with null renderer
-        #self._init_legend_box(handles, labels, None)
-        #self._legend_box.set_figure(self.figure)
+        self._init_legend_box(handles, labels)
 
+        self._last_fontsize_points = self.fontsize
 
+
     def _set_artist_props(self, a):
         """
         set the boilerplate props for artists added to axes
@@ -294,9 +290,9 @@
 
         a.set_transform(self.get_transform())
 
-    def _findoffset_best(self, width, height, xdescent, ydescent):
+    def _findoffset_best(self, width, height, xdescent, ydescent, renderer):
         "Heper function to locate the legend at its best position"
-        ox, oy = self._find_best_position(width, height)
+        ox, oy = self._find_best_position(width, height, renderer)
         return ox+xdescent, oy+ydescent
 
     def _findoffset_loc(self, width, height, xdescent, ydescent, renderer):
@@ -317,10 +313,7 @@
         "Draw everything that belongs to the legend"
         if not self.get_visible(): return
 
-        # populate the legend_box with legend items.
-        handles, labels = self._handles_labels
-        self._init_legend_box(handles, labels, renderer)
-        self._legend_box.set_figure(self.figure)
+        self._update_legend_box(renderer)
 
         renderer.open_group('legend')
 
@@ -328,12 +321,15 @@
         # _legend_box will draw itself at the location of the return
         # value of the find_offset.
         if self._loc == 0:
-            self._legend_box.set_offset(self._findoffset_best)
+            _findoffset = self._findoffset_best
         else:
-            def _findoffset_loc(width, height, xdescent, ydescent):
-                return self._findoffset_loc(width, height, xdescent, ydescent, 
renderer)
-            self._legend_box.set_offset(_findoffset_loc)
+            _findoffset = self._findoffset_loc
 
+        def findoffset(width, height, xdescent, ydescent):
+            return _findoffset(width, height, xdescent, ydescent, renderer)
+        
+        self._legend_box.set_offset(findoffset)
+        
         fontsize = renderer.points_to_pixels(self.fontsize)
 
         # if mode == fill, set the width of the legend_box to the
@@ -361,15 +357,18 @@
         renderer.close_group('legend')
 
 
-    def _approx_text_height(self):
+    def _approx_text_height(self, renderer=None):
         """
         Return the approximate height of the text. This is used to place
         the legend handle.
         """
-        return self.fontsize/72.0*self.figure.dpi
+        if renderer is None:
+            return self.fontsize
+        else:
+            return renderer.points_to_pixels(self.fontsize)
 
 
-    def _init_legend_box(self, handles, labels, renderer=None):
+    def _init_legend_box(self, handles, labels):
         """
         Initiallize the legend_box. The legend_box is an instance of
         the OffsetBox, which is packed with legend handles and
@@ -377,10 +376,7 @@
         drawing time.
         """
 
-        if renderer is None:
-            fontsize = self.fontsize
-        else:
-            fontsize = renderer.points_to_pixels(self.fontsize)
+        fontsize = self.fontsize
 
         # legend_box is a HPacker, horizontally packed with
         # columns. Each column is a VPacker, vertically packed with
@@ -415,10 +411,13 @@
         height = self._approx_text_height() * 0.7
         descent = 0.
 
-        # each handle needs to be drawn inside a box of
-        # (x, y, w, h) = (0, -descent, width, height).
-        # And their corrdinates should be given in the display coordinates.
+        # each handle needs to be drawn inside a box of (x, y, w, h) =
+        # (0, -descent, width, height).  And their corrdinates should
+        # be given in the display coordinates.
 
+        # NOTE : the coordinates will be updated again in
+        # _update_legend_box() method.
+
         # The transformation of each handle will be automatically set
         # to self.get_trasnform(). If the artist does not uses its
         # default trasnform (eg, Collections), you need to
@@ -548,8 +547,8 @@
         for i0, di in largecol+smallcol:
             # pack handleBox and labelBox into itemBox
             itemBoxes = [HPacker(pad=0,
-                                    sep=self.handletextpad*fontsize,
-                                    children=[h, t], align="baseline")
+                                 sep=self.handletextpad*fontsize,
+                                 children=[h, t], align="baseline")
                          for h, t in handle_label[i0:i0+di]]
             # minimumdescent=False for the text of the last row of the column
             itemBoxes[-1].get_children()[1].set_minimumdescent(False)
@@ -572,10 +571,100 @@
                                       mode=mode,
                                       children=columnbox)
 
+        self._legend_box.set_figure(self.figure)
+
         self.texts = text_list
         self.legendHandles = handle_list
 
 
+
+
+    def _update_legend_box(self, renderer):
+        """
+        Update the dimension of the legend_box. This is required
+        becuase the paddings, the hadle size etc. depends on the dpi
+        of the renderer.
+        """
+
+        # fontsize in points.
+        fontsize = renderer.points_to_pixels(self.fontsize)
+
+        if self._last_fontsize_points == fontsize:
+            # no update is needed
+            return
+
+        # each handle needs to be drawn inside a box of
+        # (x, y, w, h) = (0, -descent, width, height).
+        # And their corrdinates should be given in the display coordinates.
+
+        # The approximate height and descent of text. These values are
+        # only used for plotting the legend handle.
+        height = self._approx_text_height(renderer) * 0.7
+        descent = 0.
+
+        for handle in self.legendHandles:
+            if isinstance(handle, RegularPolyCollection):
+                npoints = self.scatterpoints
+            else:
+                npoints = self.numpoints
+            if npoints > 1:
+                # we put some pad here to compensate the size of the
+                # marker
+                xdata = np.linspace(0.3*fontsize,
+                                    (self.handlelength-0.3)*fontsize,
+                                    npoints)
+                xdata_marker = xdata
+            elif npoints == 1:
+                xdata = np.linspace(0, self.handlelength*fontsize, 2)
+                xdata_marker = [0.5*self.handlelength*fontsize]
+
+            if isinstance(handle, Line2D):
+                legline = handle
+                ydata = ((height-descent)/2.)*np.ones(xdata.shape, float)
+                legline.set_data(xdata, ydata)
+
+                legline_marker = legline._legmarker
+                legline_marker.set_data(xdata_marker, 
ydata[:len(xdata_marker)])
+
+            elif isinstance(handle, Patch):
+                p = handle
+                p.set_bounds(0., 0.,
+                             self.handlelength*fontsize,
+                             (height-descent),
+                             )
+
+            elif isinstance(handle, RegularPolyCollection):
+
+                p = handle
+                ydata = height*self._scatteryoffsets
+                p.set_offsets(zip(xdata_marker,ydata))
+
+
+        # correction factor
+        cor = fontsize / self._last_fontsize_points
+
+        # helper function to iterate over all children
+        def all_children(parent):
+            yield parent
+            for c in parent.get_children():
+                for cc in all_children(c): yield cc
+
+
+        #now update paddings
+        for box in all_children(self._legend_box):
+            if isinstance(box, PackerBase):
+                box.pad = box.pad * cor
+                box.sep = box.sep * cor
+
+            elif isinstance(box, DrawingArea):
+                box.width = self.handlelength*fontsize
+                box.height = height
+                box.xdescent = 0.
+                box.ydescent=descent
+
+        self._last_fontsize_points = fontsize
+
+
     def _auto_legend_data(self):
         """
         Returns list of vertices and extents covered by the plot.
@@ -683,7 +772,7 @@
         return anchored_box.x0, anchored_box.y0
 
 
-    def _find_best_position(self, width, height, consider=None):
+    def _find_best_position(self, width, height, renderer, consider=None):
         """
         Determine the best location to place the legend.
 
@@ -696,7 +785,7 @@
         verts, bboxes, lines = self._auto_legend_data()
 
         bbox = Bbox.from_bounds(0, 0, width, height)
-        consider = [self._get_anchored_bbox(x, bbox, self.parent.bbox) for x 
in range(1, len(self.codes))]
+        consider = [self._get_anchored_bbox(x, bbox, self.parent.bbox, 
renderer) for x in range(1, len(self.codes))]
 
         #tx, ty = self.legendPatch.get_x(), self.legendPatch.get_y()
 

Modified: branches/v0_98_5_maint/lib/matplotlib/offsetbox.py
===================================================================
--- branches/v0_98_5_maint/lib/matplotlib/offsetbox.py  2008-12-16 22:26:11 UTC 
(rev 6639)
+++ branches/v0_98_5_maint/lib/matplotlib/offsetbox.py  2008-12-17 00:50:56 UTC 
(rev 6640)
@@ -164,7 +164,7 @@
 
         accepts float
         """
-        self._width = width
+        self.width = width
 
     def set_height(self, height):
         """
@@ -172,7 +172,7 @@
 
         accepts float
         """
-        self._height = height
+        self.height = height
         
     def get_children(self):
         """
@@ -215,7 +215,31 @@
         bbox_artist(self, renderer, fill=False, props=dict(pad=0.))
     
 
-class VPacker(OffsetBox):
+class PackerBase(OffsetBox):
+    def __init__(self, pad=None, sep=None, width=None, height=None,
+                 align=None, mode=None,
+                 children=None):
+        """
+        *pad* : boundary pad
+        *sep* : spacing between items
+        *width*, *height* : width and height of the container box.
+           calculated if None.
+        *align* : alignment of boxes
+        *mode* : packing mode
+        """
+        super(PackerBase, self).__init__()
+
+        self.height = height
+        self.width = width
+        self.sep = sep
+        self.pad = pad
+        self.mode = mode
+        self.align = align
+
+        self._children = children
+
+
+class VPacker(PackerBase):
     """
     The VPacker has its children packed vertically. It automatically
     adjust the relative postisions of children in the drawing time.
@@ -231,18 +255,12 @@
         *align* : alignment of boxes
         *mode* : packing mode
         """
-        super(VPacker, self).__init__()
+        super(VPacker, self).__init__(pad, sep, width, height,
+                                      align, mode, 
+                                      children)
 
-        self._height = height
-        self._width = width
-        self._align = align
-        self._sep = sep
-        self._pad = pad
-        self._mode = mode
-        
-        self._children = children
-        
 
+
     def get_extent_offsets(self, renderer):
         """
         update offset of childrens and return the extents of the box
@@ -254,12 +272,12 @@
 
         wd_list = [(w, xd) for w, h, xd, yd in whd_list]
         width, xdescent, xoffsets = _get_aligned_offsets(wd_list,
-                                                         self._width,
-                                                         self._align)
+                                                         self.width,
+                                                         self.align)
 
         pack_list = [(h, yd) for w,h,xd,yd in whd_list]
-        height, yoffsets_ = _get_packed_offsets(pack_list, self._height,
-                                                self._sep, self._mode)
+        height, yoffsets_ = _get_packed_offsets(pack_list, self.height,
+                                                self.sep, self.mode)
             
         yoffsets = yoffsets_  + [yd for w,h,xd,yd in whd_list]
         ydescent = height - yoffsets[0]
@@ -268,18 +286,17 @@
         #w, h, xd, h_yd = whd_list[-1]
         yoffsets = yoffsets - ydescent
 
-        return width + 2*self._pad, height + 2*self._pad, \
-               xdescent+self._pad, ydescent+self._pad, \
+        return width + 2*self.pad, height + 2*self.pad, \
+               xdescent+self.pad, ydescent+self.pad, \
                zip(xoffsets, yoffsets)
 
 
-
-class HPacker(OffsetBox):
+class HPacker(PackerBase):
     """
     The HPacker has its children packed horizontally. It automatically
     adjust the relative postisions of children in the drawing time.
     """
-    def __init__(self, pad=None, width=None, height=None, sep=None,
+    def __init__(self, pad=None, sep=None, width=None, height=None, 
                  align="baseline", mode="fixed",
                  children=None):
         """
@@ -290,19 +307,10 @@
         *align* : alignment of boxes
         *mode* : packing mode
         """
-        super(HPacker, self).__init__()
+        super(HPacker, self).__init__(pad, sep, width, height,
+                                      align, mode, children)
 
-        self._height = height
-        self._width = width
-        self._align = align
-        
-        self._sep = sep
-        self._pad = pad
-        self._mode = mode
 
-        self._children = children
-        
-
     def get_extent_offsets(self, renderer):
         """
         update offset of childrens and return the extents of the box
@@ -310,30 +318,30 @@
 
         whd_list = [c.get_extent(renderer) for c in self.get_children()]
 
-        if self._height is None:
+        if self.height is None:
             height_descent = max([h-yd for w,h,xd,yd in whd_list])  
             ydescent = max([yd for w,h,xd,yd in whd_list])
             height = height_descent + ydescent
         else:
-            height = self._height - 2*self._pad # width w/o pad
+            height = self.height - 2*self._pad # width w/o pad
 
         hd_list = [(h, yd) for w, h, xd, yd in whd_list]
         height, ydescent, yoffsets = _get_aligned_offsets(hd_list,
-                                                          self._height,
-                                                          self._align)
+                                                          self.height,
+                                                          self.align)
 
 
         pack_list = [(w, xd) for w,h,xd,yd in whd_list]
-        width, xoffsets_ = _get_packed_offsets(pack_list, self._width,
-                                               self._sep, self._mode)
+        width, xoffsets_ = _get_packed_offsets(pack_list, self.width,
+                                               self.sep, self.mode)
 
         xoffsets = xoffsets_  + [xd for w,h,xd,yd in whd_list]
 
         xdescent=whd_list[0][2]
         xoffsets = xoffsets - xdescent
         
-        return width + 2*self._pad, height + 2*self._pad, \
-               xdescent + self._pad, ydescent + self._pad, \
+        return width + 2*self.pad, height + 2*self.pad, \
+               xdescent + self.pad, ydescent + self.pad, \
                zip(xoffsets, yoffsets)
 
         


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
SF.Net email is Sponsored by MIX09, March 18-20, 2009 in Las Vegas, Nevada.
The future of the web can't happen without you.  Join us at MIX09 to help
pave the way to the Next Web now. Learn more and register at
http://ad.doubleclick.net/clk;208669438;13503038;i?http://2009.visitmix.com/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to