|
From: <lee...@us...> - 2008-12-17 00:55:55
|
Revision: 6641
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=6641&view=rev
Author: leejjoon
Date: 2008-12-17 00:55:52 +0000 (Wed, 17 Dec 2008)
Log Message:
-----------
Merged revisions 6640 via svnmerge from
https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_98_5_maint
........
r6640 | leejjoon | 2008-12-16 19:50:56 -0500 (Tue, 16 Dec 2008) | 1 line
Another attempt to fix dpi-dependent behavior of Legend
........
Modified Paths:
--------------
trunk/matplotlib/CHANGELOG
trunk/matplotlib/lib/matplotlib/legend.py
trunk/matplotlib/lib/matplotlib/offsetbox.py
Property Changed:
----------------
trunk/matplotlib/
Property changes on: trunk/matplotlib
___________________________________________________________________
Modified: svnmerge-integrated
- /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6637
+ /branches/v0_91_maint:1-6428 /branches/v0_98_5_maint:1-6640
Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG 2008-12-17 00:50:56 UTC (rev 6640)
+++ trunk/matplotlib/CHANGELOG 2008-12-17 00:55:52 UTC (rev 6641)
@@ -1,3 +1,4 @@
+2008-12-16 Another attempt to fix dpi-dependent behavior of Legend. -JJL
2008-12-16 Fixed dpi-dependent behavior of Legend and fancybox in Text.
Modified: trunk/matplotlib/lib/matplotlib/legend.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/legend.py 2008-12-17 00:50:56 UTC (rev 6640)
+++ trunk/matplotlib/lib/matplotlib/legend.py 2008-12-17 00:55:52 UTC (rev 6641)
@@ -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: trunk/matplotlib/lib/matplotlib/offsetbox.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/offsetbox.py 2008-12-17 00:50:56 UTC (rev 6640)
+++ trunk/matplotlib/lib/matplotlib/offsetbox.py 2008-12-17 00:55:52 UTC (rev 6641)
@@ -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.
|