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. |