1""" 2The axes_divider module provides helper classes to adjust the positions of 3multiple axes at drawing time. 4 5 Divider: this is the class that is used to calculate the axes 6 position. It divides the given rectangular area into several sub 7 rectangles. You initialize the divider by setting the horizontal 8 and vertical lists of sizes that the division will be based on. You 9 then use the new_locator method, whose return value is a callable 10 object that can be used to set the axes_locator of the axes. 11 12""" 13from __future__ import (absolute_import, division, print_function, 14 unicode_literals) 15 16import six 17from six.moves import map 18 19import matplotlib.transforms as mtransforms 20 21from matplotlib.axes import SubplotBase 22 23from . import axes_size as Size 24 25 26class Divider(object): 27 """ 28 This class calculates the axes position. It 29 divides the given rectangular area into several 30 sub-rectangles. You initialize the divider by setting the 31 horizontal and vertical lists of sizes 32 (:mod:`mpl_toolkits.axes_grid.axes_size`) that the division will 33 be based on. You then use the new_locator method to create a 34 callable object that can be used as the axes_locator of the 35 axes. 36 """ 37 38 def __init__(self, fig, pos, horizontal, vertical, 39 aspect=None, anchor="C"): 40 """ 41 Parameters 42 ---------- 43 fig : Figure 44 pos : tuple of 4 floats 45 position of the rectangle that will be divided 46 horizontal : list of :mod:`~mpl_toolkits.axes_grid.axes_size` 47 sizes for horizontal division 48 vertical : list of :mod:`~mpl_toolkits.axes_grid.axes_size` 49 sizes for vertical division 50 aspect : bool 51 if True, the overall rectangular area is reduced 52 so that the relative part of the horizontal and 53 vertical scales have the same scale. 54 anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'} 55 placement of the reduced rectangle when *aspect* is True 56 """ 57 58 self._fig = fig 59 self._pos = pos 60 self._horizontal = horizontal 61 self._vertical = vertical 62 self._anchor = anchor 63 self._aspect = aspect 64 self._xrefindex = 0 65 self._yrefindex = 0 66 self._locator = None 67 68 def get_horizontal_sizes(self, renderer): 69 return [s.get_size(renderer) for s in self.get_horizontal()] 70 71 def get_vertical_sizes(self, renderer): 72 return [s.get_size(renderer) for s in self.get_vertical()] 73 74 def get_vsize_hsize(self): 75 76 from .axes_size import AddList 77 78 vsize = AddList(self.get_vertical()) 79 hsize = AddList(self.get_horizontal()) 80 81 return vsize, hsize 82 83 @staticmethod 84 def _calc_k(l, total_size): 85 86 rs_sum, as_sum = 0., 0. 87 88 for _rs, _as in l: 89 rs_sum += _rs 90 as_sum += _as 91 92 if rs_sum != 0.: 93 k = (total_size - as_sum) / rs_sum 94 return k 95 else: 96 return 0. 97 98 @staticmethod 99 def _calc_offsets(l, k): 100 101 offsets = [0.] 102 103 #for s in l: 104 for _rs, _as in l: 105 #_rs, _as = s.get_size(renderer) 106 offsets.append(offsets[-1] + _rs*k + _as) 107 108 return offsets 109 110 def set_position(self, pos): 111 """ 112 set the position of the rectangle. 113 114 Parameters 115 ---------- 116 pos : tuple of 4 floats 117 position of the rectangle that will be divided 118 """ 119 self._pos = pos 120 121 def get_position(self): 122 "return the position of the rectangle." 123 return self._pos 124 125 def set_anchor(self, anchor): 126 """ 127 Parameters 128 ---------- 129 anchor : {'C', 'SW', 'S', 'SE', 'E', 'NE', 'N', 'NW', 'W'} 130 anchor position 131 132 ===== ============ 133 value description 134 ===== ============ 135 'C' Center 136 'SW' bottom left 137 'S' bottom 138 'SE' bottom right 139 'E' right 140 'NE' top right 141 'N' top 142 'NW' top left 143 'W' left 144 ===== ============ 145 146 """ 147 if anchor in mtransforms.Bbox.coefs or len(anchor) == 2: 148 self._anchor = anchor 149 else: 150 raise ValueError('argument must be among %s' % 151 ', '.join(mtransforms.BBox.coefs)) 152 153 def get_anchor(self): 154 "return the anchor" 155 return self._anchor 156 157 def set_horizontal(self, h): 158 """ 159 Parameters 160 ---------- 161 h : list of :mod:`~mpl_toolkits.axes_grid.axes_size` 162 sizes for horizontal division 163 """ 164 self._horizontal = h 165 166 def get_horizontal(self): 167 "return horizontal sizes" 168 return self._horizontal 169 170 def set_vertical(self, v): 171 """ 172 Parameters 173 ---------- 174 v : list of :mod:`~mpl_toolkits.axes_grid.axes_size` 175 sizes for vertical division 176 """ 177 self._vertical = v 178 179 def get_vertical(self): 180 "return vertical sizes" 181 return self._vertical 182 183 def set_aspect(self, aspect=False): 184 """ 185 Parameters 186 ---------- 187 aspect : bool 188 """ 189 self._aspect = aspect 190 191 def get_aspect(self): 192 "return aspect" 193 return self._aspect 194 195 def set_locator(self, _locator): 196 self._locator = _locator 197 198 def get_locator(self): 199 return self._locator 200 201 def get_position_runtime(self, ax, renderer): 202 if self._locator is None: 203 return self.get_position() 204 else: 205 return self._locator(ax, renderer).bounds 206 207 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 208 """ 209 Parameters 210 ---------- 211 nx, nx1 : int 212 Integers specifying the column-position of the 213 cell. When *nx1* is None, a single *nx*-th column is 214 specified. Otherwise location of columns spanning between *nx* 215 to *nx1* (but excluding *nx1*-th column) is specified. 216 ny, ny1 : int 217 Same as *nx* and *nx1*, but for row positions. 218 axes 219 renderer 220 """ 221 222 figW, figH = self._fig.get_size_inches() 223 x, y, w, h = self.get_position_runtime(axes, renderer) 224 225 hsizes = self.get_horizontal_sizes(renderer) 226 vsizes = self.get_vertical_sizes(renderer) 227 k_h = self._calc_k(hsizes, figW*w) 228 k_v = self._calc_k(vsizes, figH*h) 229 230 if self.get_aspect(): 231 k = min(k_h, k_v) 232 ox = self._calc_offsets(hsizes, k) 233 oy = self._calc_offsets(vsizes, k) 234 235 ww = (ox[-1] - ox[0])/figW 236 hh = (oy[-1] - oy[0])/figH 237 pb = mtransforms.Bbox.from_bounds(x, y, w, h) 238 pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) 239 pb1_anchored = pb1.anchored(self.get_anchor(), pb) 240 x0, y0 = pb1_anchored.x0, pb1_anchored.y0 241 242 else: 243 ox = self._calc_offsets(hsizes, k_h) 244 oy = self._calc_offsets(vsizes, k_v) 245 x0, y0 = x, y 246 247 if nx1 is None: 248 nx1 = nx+1 249 if ny1 is None: 250 ny1 = ny+1 251 252 x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW 253 y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH 254 255 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 256 257 def new_locator(self, nx, ny, nx1=None, ny1=None): 258 """ 259 Returns a new locator 260 (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for 261 specified cell. 262 263 Parameters 264 ---------- 265 nx, nx1 : int 266 Integers specifying the column-position of the 267 cell. When *nx1* is None, a single *nx*-th column is 268 specified. Otherwise location of columns spanning between *nx* 269 to *nx1* (but excluding *nx1*-th column) is specified. 270 ny, ny1 : int 271 Same as *nx* and *nx1*, but for row positions. 272 """ 273 return AxesLocator(self, nx, ny, nx1, ny1) 274 275 def append_size(self, position, size): 276 277 if position == "left": 278 self._horizontal.insert(0, size) 279 self._xrefindex += 1 280 elif position == "right": 281 self._horizontal.append(size) 282 elif position == "bottom": 283 self._vertical.insert(0, size) 284 self._yrefindex += 1 285 elif position == "top": 286 self._vertical.append(size) 287 else: 288 raise ValueError("the position must be one of left," + 289 " right, bottom, or top") 290 291 def add_auto_adjustable_area(self, 292 use_axes, pad=0.1, 293 adjust_dirs=None, 294 ): 295 if adjust_dirs is None: 296 adjust_dirs = ["left", "right", "bottom", "top"] 297 from .axes_size import Padded, SizeFromFunc, GetExtentHelper 298 for d in adjust_dirs: 299 helper = GetExtentHelper(use_axes, d) 300 size = SizeFromFunc(helper) 301 padded_size = Padded(size, pad) # pad in inch 302 self.append_size(d, padded_size) 303 304 305class AxesLocator(object): 306 """ 307 A simple callable object, initialized with AxesDivider class, 308 returns the position and size of the given cell. 309 """ 310 def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): 311 """ 312 Parameters 313 ---------- 314 axes_divider : AxesDivider 315 nx, nx1 : int 316 Integers specifying the column-position of the 317 cell. When *nx1* is None, a single *nx*-th column is 318 specified. Otherwise location of columns spanning between *nx* 319 to *nx1* (but excluding *nx1*-th column) is specified. 320 ny, ny1 : int 321 Same as *nx* and *nx1*, but for row positions. 322 """ 323 self._axes_divider = axes_divider 324 325 _xrefindex = axes_divider._xrefindex 326 _yrefindex = axes_divider._yrefindex 327 328 self._nx, self._ny = nx - _xrefindex, ny - _yrefindex 329 330 if nx1 is None: 331 nx1 = nx+1 332 if ny1 is None: 333 ny1 = ny+1 334 335 self._nx1 = nx1 - _xrefindex 336 self._ny1 = ny1 - _yrefindex 337 338 def __call__(self, axes, renderer): 339 340 _xrefindex = self._axes_divider._xrefindex 341 _yrefindex = self._axes_divider._yrefindex 342 343 return self._axes_divider.locate(self._nx + _xrefindex, 344 self._ny + _yrefindex, 345 self._nx1 + _xrefindex, 346 self._ny1 + _yrefindex, 347 axes, 348 renderer) 349 350 def get_subplotspec(self): 351 if hasattr(self._axes_divider, "get_subplotspec"): 352 return self._axes_divider.get_subplotspec() 353 else: 354 return None 355 356 357from matplotlib.gridspec import SubplotSpec, GridSpec 358 359 360class SubplotDivider(Divider): 361 """ 362 The Divider class whose rectangle area is specified as a subplot geometry. 363 """ 364 365 def __init__(self, fig, *args, **kwargs): 366 """ 367 Parameters 368 ---------- 369 fig : :class:`matplotlib.figure.Figure` 370 args : tuple (*numRows*, *numCols*, *plotNum*) 371 The array of subplots in the figure has dimensions *numRows*, 372 *numCols*, and *plotNum* is the number of the subplot 373 being created. *plotNum* starts at 1 in the upper left 374 corner and increases to the right. 375 376 If *numRows* <= *numCols* <= *plotNum* < 10, *args* can be the 377 decimal integer *numRows* * 100 + *numCols* * 10 + *plotNum*. 378 """ 379 380 self.figure = fig 381 382 if len(args) == 1: 383 if isinstance(args[0], SubplotSpec): 384 self._subplotspec = args[0] 385 else: 386 try: 387 s = str(int(args[0])) 388 rows, cols, num = map(int, s) 389 except ValueError: 390 raise ValueError( 391 'Single argument to subplot must be a 3-digit integer') 392 self._subplotspec = GridSpec(rows, cols)[num-1] 393 # num - 1 for converting from MATLAB to python indexing 394 elif len(args) == 3: 395 rows, cols, num = args 396 rows = int(rows) 397 cols = int(cols) 398 if isinstance(num, tuple) and len(num) == 2: 399 num = [int(n) for n in num] 400 self._subplotspec = GridSpec(rows, cols)[num[0]-1:num[1]] 401 else: 402 self._subplotspec = GridSpec(rows, cols)[int(num)-1] 403 # num - 1 for converting from MATLAB to python indexing 404 else: 405 raise ValueError('Illegal argument(s) to subplot: %s' % (args,)) 406 407 # total = rows*cols 408 # num -= 1 # convert from matlab to python indexing 409 # # i.e., num in range(0,total) 410 # if num >= total: 411 # raise ValueError( 'Subplot number exceeds total subplots') 412 # self._rows = rows 413 # self._cols = cols 414 # self._num = num 415 416 # self.update_params() 417 418 # sets self.fixbox 419 self.update_params() 420 421 pos = self.figbox.bounds 422 423 horizontal = kwargs.pop("horizontal", []) 424 vertical = kwargs.pop("vertical", []) 425 aspect = kwargs.pop("aspect", None) 426 anchor = kwargs.pop("anchor", "C") 427 428 if kwargs: 429 raise Exception("") 430 431 Divider.__init__(self, fig, pos, horizontal, vertical, 432 aspect=aspect, anchor=anchor) 433 434 def get_position(self): 435 "return the bounds of the subplot box" 436 437 self.update_params() # update self.figbox 438 return self.figbox.bounds 439 440 # def update_params(self): 441 # 'update the subplot position from fig.subplotpars' 442 443 # rows = self._rows 444 # cols = self._cols 445 # num = self._num 446 447 # pars = self.figure.subplotpars 448 # left = pars.left 449 # right = pars.right 450 # bottom = pars.bottom 451 # top = pars.top 452 # wspace = pars.wspace 453 # hspace = pars.hspace 454 # totWidth = right-left 455 # totHeight = top-bottom 456 457 # figH = totHeight/(rows + hspace*(rows-1)) 458 # sepH = hspace*figH 459 460 # figW = totWidth/(cols + wspace*(cols-1)) 461 # sepW = wspace*figW 462 463 # rowNum, colNum = divmod(num, cols) 464 465 # figBottom = top - (rowNum+1)*figH - rowNum*sepH 466 # figLeft = left + colNum*(figW + sepW) 467 468 # self.figbox = mtransforms.Bbox.from_bounds(figLeft, figBottom, 469 # figW, figH) 470 471 def update_params(self): 472 'update the subplot position from fig.subplotpars' 473 474 self.figbox = self.get_subplotspec().get_position(self.figure) 475 476 def get_geometry(self): 477 'get the subplot geometry, e.g., 2,2,3' 478 rows, cols, num1, num2 = self.get_subplotspec().get_geometry() 479 return rows, cols, num1+1 # for compatibility 480 481 # COVERAGE NOTE: Never used internally or from examples 482 def change_geometry(self, numrows, numcols, num): 483 'change subplot geometry, e.g., from 1,1,1 to 2,2,3' 484 self._subplotspec = GridSpec(numrows, numcols)[num-1] 485 self.update_params() 486 self.set_position(self.figbox) 487 488 def get_subplotspec(self): 489 'get the SubplotSpec instance' 490 return self._subplotspec 491 492 def set_subplotspec(self, subplotspec): 493 'set the SubplotSpec instance' 494 self._subplotspec = subplotspec 495 496 497class AxesDivider(Divider): 498 """ 499 Divider based on the pre-existing axes. 500 """ 501 502 def __init__(self, axes, xref=None, yref=None): 503 """ 504 Parameters 505 ---------- 506 axes : :class:`~matplotlib.axes.Axes` 507 xref 508 yref 509 """ 510 self._axes = axes 511 if xref is None: 512 self._xref = Size.AxesX(axes) 513 else: 514 self._xref = xref 515 if yref is None: 516 self._yref = Size.AxesY(axes) 517 else: 518 self._yref = yref 519 520 Divider.__init__(self, fig=axes.get_figure(), pos=None, 521 horizontal=[self._xref], vertical=[self._yref], 522 aspect=None, anchor="C") 523 524 def _get_new_axes(self, **kwargs): 525 axes = self._axes 526 527 axes_class = kwargs.pop("axes_class", None) 528 529 if axes_class is None: 530 if isinstance(axes, SubplotBase): 531 axes_class = axes._axes_class 532 else: 533 axes_class = type(axes) 534 535 ax = axes_class(axes.get_figure(), 536 axes.get_position(original=True), **kwargs) 537 538 return ax 539 540 def new_horizontal(self, size, pad=None, pack_start=False, **kwargs): 541 """ 542 Add a new axes on the right (or left) side of the main axes. 543 544 Parameters 545 ---------- 546 size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string 547 A width of the axes. If float or string is given, *from_any* 548 function is used to create the size, with *ref_size* set to AxesX 549 instance of the current axes. 550 pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string 551 Pad between the axes. It takes same argument as *size*. 552 pack_start : bool 553 If False, the new axes is appended at the end 554 of the list, i.e., it became the right-most axes. If True, it is 555 inserted at the start of the list, and becomes the left-most axes. 556 kwargs 557 All extra keywords arguments are passed to the created axes. 558 If *axes_class* is given, the new axes will be created as an 559 instance of the given class. Otherwise, the same class of the 560 main axes will be used. 561 """ 562 563 if pad: 564 if not isinstance(pad, Size._Base): 565 pad = Size.from_any(pad, 566 fraction_ref=self._xref) 567 if pack_start: 568 self._horizontal.insert(0, pad) 569 self._xrefindex += 1 570 else: 571 self._horizontal.append(pad) 572 573 if not isinstance(size, Size._Base): 574 size = Size.from_any(size, 575 fraction_ref=self._xref) 576 577 if pack_start: 578 self._horizontal.insert(0, size) 579 self._xrefindex += 1 580 locator = self.new_locator(nx=0, ny=self._yrefindex) 581 else: 582 self._horizontal.append(size) 583 locator = self.new_locator(nx=len(self._horizontal)-1, ny=self._yrefindex) 584 585 ax = self._get_new_axes(**kwargs) 586 ax.set_axes_locator(locator) 587 588 return ax 589 590 def new_vertical(self, size, pad=None, pack_start=False, **kwargs): 591 """ 592 Add a new axes on the top (or bottom) side of the main axes. 593 594 Parameters 595 ---------- 596 size : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string 597 A height of the axes. If float or string is given, *from_any* 598 function is used to create the size, with *ref_size* set to AxesX 599 instance of the current axes. 600 pad : :mod:`~mpl_toolkits.axes_grid.axes_size` or float or string 601 Pad between the axes. It takes same argument as *size*. 602 pack_start : bool 603 If False, the new axes is appended at the end 604 of the list, i.e., it became the right-most axes. If True, it is 605 inserted at the start of the list, and becomes the left-most axes. 606 kwargs 607 All extra keywords arguments are passed to the created axes. 608 If *axes_class* is given, the new axes will be created as an 609 instance of the given class. Otherwise, the same class of the 610 main axes will be used. 611 """ 612 613 if pad: 614 if not isinstance(pad, Size._Base): 615 pad = Size.from_any(pad, 616 fraction_ref=self._yref) 617 if pack_start: 618 self._vertical.insert(0, pad) 619 self._yrefindex += 1 620 else: 621 self._vertical.append(pad) 622 623 if not isinstance(size, Size._Base): 624 size = Size.from_any(size, 625 fraction_ref=self._yref) 626 627 if pack_start: 628 self._vertical.insert(0, size) 629 self._yrefindex += 1 630 locator = self.new_locator(nx=self._xrefindex, ny=0) 631 else: 632 self._vertical.append(size) 633 locator = self.new_locator(nx=self._xrefindex, ny=len(self._vertical)-1) 634 635 ax = self._get_new_axes(**kwargs) 636 ax.set_axes_locator(locator) 637 638 return ax 639 640 def append_axes(self, position, size, pad=None, add_to_figure=True, 641 **kwargs): 642 """ 643 create an axes at the given *position* with the same height 644 (or width) of the main axes. 645 646 *position* 647 ["left"|"right"|"bottom"|"top"] 648 649 *size* and *pad* should be axes_grid.axes_size compatible. 650 """ 651 652 if position == "left": 653 ax = self.new_horizontal(size, pad, pack_start=True, **kwargs) 654 elif position == "right": 655 ax = self.new_horizontal(size, pad, pack_start=False, **kwargs) 656 elif position == "bottom": 657 ax = self.new_vertical(size, pad, pack_start=True, **kwargs) 658 elif position == "top": 659 ax = self.new_vertical(size, pad, pack_start=False, **kwargs) 660 else: 661 raise ValueError("the position must be one of left," + 662 " right, bottom, or top") 663 664 if add_to_figure: 665 self._fig.add_axes(ax) 666 return ax 667 668 def get_aspect(self): 669 if self._aspect is None: 670 aspect = self._axes.get_aspect() 671 if aspect == "auto": 672 return False 673 else: 674 return True 675 else: 676 return self._aspect 677 678 def get_position(self): 679 if self._pos is None: 680 bbox = self._axes.get_position(original=True) 681 return bbox.bounds 682 else: 683 return self._pos 684 685 def get_anchor(self): 686 if self._anchor is None: 687 return self._axes.get_anchor() 688 else: 689 return self._anchor 690 691 def get_subplotspec(self): 692 if hasattr(self._axes, "get_subplotspec"): 693 return self._axes.get_subplotspec() 694 else: 695 return None 696 697 698class HBoxDivider(SubplotDivider): 699 700 def __init__(self, fig, *args, **kwargs): 701 SubplotDivider.__init__(self, fig, *args, **kwargs) 702 703 @staticmethod 704 def _determine_karray(equivalent_sizes, appended_sizes, 705 max_equivalent_size, 706 total_appended_size): 707 708 n = len(equivalent_sizes) 709 import numpy as np 710 A = np.mat(np.zeros((n+1, n+1), dtype="d")) 711 B = np.zeros((n+1), dtype="d") 712 # AxK = B 713 714 # populated A 715 for i, (r, a) in enumerate(equivalent_sizes): 716 A[i, i] = r 717 A[i, -1] = -1 718 B[i] = -a 719 A[-1, :-1] = [r for r, a in appended_sizes] 720 B[-1] = total_appended_size - sum([a for rs, a in appended_sizes]) 721 722 karray_H = (A.I*np.mat(B).T).A1 723 karray = karray_H[:-1] 724 H = karray_H[-1] 725 726 if H > max_equivalent_size: 727 karray = ((max_equivalent_size - 728 np.array([a for r, a in equivalent_sizes])) 729 / np.array([r for r, a in equivalent_sizes])) 730 return karray 731 732 @staticmethod 733 def _calc_offsets(appended_sizes, karray): 734 offsets = [0.] 735 736 #for s in l: 737 for (r, a), k in zip(appended_sizes, karray): 738 offsets.append(offsets[-1] + r*k + a) 739 740 return offsets 741 742 def new_locator(self, nx, nx1=None): 743 """ 744 returns a new locator 745 (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for 746 specified cell. 747 748 Parameters 749 ---------- 750 nx, nx1 : int 751 Integers specifying the column-position of the 752 cell. When *nx1* is None, a single *nx*-th column is 753 specified. Otherwise location of columns spanning between *nx* 754 to *nx1* (but excluding *nx1*-th column) is specified. 755 ny, ny1 : int 756 Same as *nx* and *nx1*, but for row positions. 757 """ 758 return AxesLocator(self, nx, 0, nx1, None) 759 760 def _locate(self, x, y, w, h, 761 y_equivalent_sizes, x_appended_sizes, 762 figW, figH): 763 """ 764 Parameters 765 ---------- 766 x 767 y 768 w 769 h 770 y_equivalent_sizes 771 x_appended_sizes 772 figW 773 figH 774 """ 775 776 equivalent_sizes = y_equivalent_sizes 777 appended_sizes = x_appended_sizes 778 779 max_equivalent_size = figH*h 780 total_appended_size = figW*w 781 karray = self._determine_karray(equivalent_sizes, appended_sizes, 782 max_equivalent_size, 783 total_appended_size) 784 785 ox = self._calc_offsets(appended_sizes, karray) 786 787 ww = (ox[-1] - ox[0])/figW 788 ref_h = equivalent_sizes[0] 789 hh = (karray[0]*ref_h[0] + ref_h[1])/figH 790 pb = mtransforms.Bbox.from_bounds(x, y, w, h) 791 pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) 792 pb1_anchored = pb1.anchored(self.get_anchor(), pb) 793 x0, y0 = pb1_anchored.x0, pb1_anchored.y0 794 795 return x0, y0, ox, hh 796 797 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 798 """ 799 Parameters 800 ---------- 801 axes_divider : AxesDivider 802 nx, nx1 : int 803 Integers specifying the column-position of the 804 cell. When *nx1* is None, a single *nx*-th column is 805 specified. Otherwise location of columns spanning between *nx* 806 to *nx1* (but excluding *nx1*-th column) is specified. 807 ny, ny1 : int 808 Same as *nx* and *nx1*, but for row positions. 809 axes 810 renderer 811 """ 812 813 figW, figH = self._fig.get_size_inches() 814 x, y, w, h = self.get_position_runtime(axes, renderer) 815 816 y_equivalent_sizes = self.get_vertical_sizes(renderer) 817 x_appended_sizes = self.get_horizontal_sizes(renderer) 818 x0, y0, ox, hh = self._locate(x, y, w, h, 819 y_equivalent_sizes, x_appended_sizes, 820 figW, figH) 821 if nx1 is None: 822 nx1 = nx+1 823 824 x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW 825 y1, h1 = y0, hh 826 827 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 828 829 830class VBoxDivider(HBoxDivider): 831 """ 832 The Divider class whose rectangle area is specified as a subplot geometry. 833 """ 834 835 def new_locator(self, ny, ny1=None): 836 """ 837 returns a new locator 838 (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for 839 specified cell. 840 841 Parameters 842 ---------- 843 ny, ny1 : int 844 Integers specifying the row-position of the 845 cell. When *ny1* is None, a single *ny*-th row is 846 specified. Otherwise location of rows spanning between *ny* 847 to *ny1* (but excluding *ny1*-th row) is specified. 848 """ 849 return AxesLocator(self, 0, ny, None, ny1) 850 851 def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): 852 """ 853 Parameters 854 ---------- 855 axes_divider : AxesDivider 856 nx, nx1 : int 857 Integers specifying the column-position of the 858 cell. When *nx1* is None, a single *nx*-th column is 859 specified. Otherwise location of columns spanning between *nx* 860 to *nx1* (but excluding *nx1*-th column) is specified. 861 ny, ny1 : int 862 Same as *nx* and *nx1*, but for row positions. 863 axes 864 renderer 865 """ 866 867 figW, figH = self._fig.get_size_inches() 868 x, y, w, h = self.get_position_runtime(axes, renderer) 869 870 x_equivalent_sizes = self.get_horizontal_sizes(renderer) 871 y_appended_sizes = self.get_vertical_sizes(renderer) 872 873 y0, x0, oy, ww = self._locate(y, x, h, w, 874 x_equivalent_sizes, y_appended_sizes, 875 figH, figW) 876 if ny1 is None: 877 ny1 = ny+1 878 879 x1, w1 = x0, ww 880 y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH 881 882 return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) 883 884 885class LocatableAxesBase(object): 886 def __init__(self, *kl, **kw): 887 888 self._axes_class.__init__(self, *kl, **kw) 889 890 self._locator = None 891 self._locator_renderer = None 892 893 def set_axes_locator(self, locator): 894 self._locator = locator 895 896 def get_axes_locator(self): 897 return self._locator 898 899 def apply_aspect(self, position=None): 900 901 if self.get_axes_locator() is None: 902 self._axes_class.apply_aspect(self, position) 903 else: 904 pos = self.get_axes_locator()(self, self._locator_renderer) 905 self._axes_class.apply_aspect(self, position=pos) 906 907 def draw(self, renderer=None, inframe=False): 908 909 self._locator_renderer = renderer 910 911 self._axes_class.draw(self, renderer, inframe) 912 913 def _make_twin_axes(self, *kl, **kwargs): 914 """ 915 Need to overload so that twinx/twiny will work with 916 these axes. 917 """ 918 if 'sharex' in kwargs and 'sharey' in kwargs: 919 raise ValueError("Twinned Axes may share only one axis.") 920 ax2 = type(self)(self.figure, self.get_position(True), *kl, **kwargs) 921 ax2.set_axes_locator(self.get_axes_locator()) 922 self.figure.add_axes(ax2) 923 self.set_adjustable('datalim') 924 ax2.set_adjustable('datalim') 925 self._twinned_axes.join(self, ax2) 926 return ax2 927 928_locatableaxes_classes = {} 929 930 931def locatable_axes_factory(axes_class): 932 933 new_class = _locatableaxes_classes.get(axes_class) 934 if new_class is None: 935 new_class = type(str("Locatable%s" % (axes_class.__name__)), 936 (LocatableAxesBase, axes_class), 937 {'_axes_class': axes_class}) 938 939 _locatableaxes_classes[axes_class] = new_class 940 941 return new_class 942 943#if hasattr(maxes.Axes, "get_axes_locator"): 944# LocatableAxes = maxes.Axes 945#else: 946 947 948def make_axes_locatable(axes): 949 if not hasattr(axes, "set_axes_locator"): 950 new_class = locatable_axes_factory(type(axes)) 951 axes.__class__ = new_class 952 953 divider = AxesDivider(axes) 954 locator = divider.new_locator(nx=0, ny=0) 955 axes.set_axes_locator(locator) 956 957 return divider 958 959 960def make_axes_area_auto_adjustable(ax, 961 use_axes=None, pad=0.1, 962 adjust_dirs=None): 963 if adjust_dirs is None: 964 adjust_dirs = ["left", "right", "bottom", "top"] 965 divider = make_axes_locatable(ax) 966 967 if use_axes is None: 968 use_axes = ax 969 970 divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad, 971 adjust_dirs=adjust_dirs) 972 973#from matplotlib.axes import Axes 974from .mpl_axes import Axes 975LocatableAxes = locatable_axes_factory(Axes) 976