title: Matplotlib: multiple stacked graphs in one figure
body: <p>Simulating the remote control of the dji mavic pro I have a display showing the stick position in a polar plot and its (x, y) values by projecting these on the x and y axis of a Cartesian plot placing the polar plot and the Cartesian plot in the same location using <code>fig.add_axes(rect)</code> calculating the correct values for <code>rect = [left, bottom, width, height]</code>.</p>
  <p>I then make two instances one for the stick on the left and one for the stick on the right, all in one figure.</p>
  <p><a href="https://i.stack.imgur.com/vGOw9.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vGOw9.png" alt="enter image description here" /></a></p>
  <p>The program works, but I wonder if matplotlib has better way of gridding stacked plots?</p>
  <pre><code>import numpy as np
  import matplotlib as mpl
  import matplotlib.pyplot as plt
  from matplotlib import patches as mpl_patches
  from matplotlib import lines as mpl_lines
  
  fig_size = (13, 6)
  stick_end_color = 'red'
  stick_end_size = 5
  stick_color = 'blue'
  stick_width = 5
  bar_color = 'orange'
  bar_width = 10
  
  def conv_xy_to_polar(x, y):
      return np.degrees(np.arctan2(y, x)), np.sqrt(x*x + y*y)
  
  
  class RemoteControlDisplay:
      def __init__(self, rmax):
          self._rmax = rmax
          mpl.rcParams['toolbar'] = 'None'
          self.rc_fig = plt.figure('Remote Control', figsize=fig_size)
          self.rc_fig.suptitle(None)
  
      def blit(self):
          self.fig.canvas.draw()
          self.fig.canvas.flush_events()
  
      @property
      def fig(self):
          return self.rc_fig
  
      @property
      def rmax(self):
          return  self._rmax
  
  
  class RcStick():
  
      def __init__(self, fig, rmax, stick_name, left=True):
          rect_carth_offs = 0.050
          rect_polar_offs = 0.075
          tick_intval = 20
  
          if left:
              rect_carth = [
                  rect_carth_offs, rect_carth_offs,
                  0.5-rect_carth_offs, 1 - 2*rect_carth_offs
              ]
              rect_polar = [
                  rect_carth[0] + 0.5*rect_polar_offs, rect_carth[1] + rect_polar_offs,
                  rect_carth[2] - 1*rect_polar_offs, rect_carth[3] - 2*rect_polar_offs
              ]
  
          else:
              rect_carth = [
                  0.25+2*rect_carth_offs, rect_carth_offs,
                  1-4*rect_carth_offs, 1 - 2*rect_carth_offs
              ]
              rect_polar = [
                  rect_carth[0] + rect_polar_offs, rect_carth[1] + rect_polar_offs,
                  rect_carth[2] - 2*rect_polar_offs, rect_carth[3] - 2*rect_polar_offs
              ]
  
          # place carthesian grid in fig
          ax_carth = fig.add_axes(rect_carth)
  
          # adjust x, y limits to match the one on the polar plot
          self.bv = rmax * (1-2*rect_carth_offs) / (1-2*rect_carth_offs-2*rect_polar_offs)
          ax_carth.set(
              xlim=(-self.bv, self.bv), ylim=(-self.bv, self.bv), aspect='equal'
          )
  
          # axis ticks
          ticks = np.arange(0, self.bv, tick_intval)
          ticks = sorted(np.append(-ticks[1:], ticks))
          ax_carth.set_xticks(ticks)
          ax_carth.set_yticks(ticks)
  
          # place polar grid in carthesian grid
          ax_polar = fig.add_axes(rect_polar, polar=True, frameon=False)
          ax_polar.set_rmax(rmax)
  
          # display of stick
          self.stick_end = mpl_patches.Circle(
              (0, 0), radius=stick_end_size, fc=stick_end_color,
              transform=ax_polar.transData._b
          )
          self.stick = mpl_lines.Line2D(
              [0, 0], [0, 0], linewidth=stick_width, color=stick_color
          )
          ax_polar.add_patch(self.stick_end)
          ax_polar.add_line(self.stick)
          self.stick_val(0, 0)
  
          # display of x, y bars
          self.bar_x = mpl_lines.Line2D(
              [0, 0], [0, 0], linewidth=bar_width, color=bar_color, solid_capstyle='butt',
          )
          self.bar_y = mpl_lines.Line2D(
              [0, 0], [0, 0], linewidth=bar_width, color=bar_color, solid_capstyle='butt',
          )
          ax_carth.add_line(self.bar_x)
          ax_carth.add_line(self.bar_y)
          self.bar_val(0, 0)
  
          ax_carth.set_title(stick_name)
          ax_polar.set_yticklabels([])
  
      def stick_val(self, x, y):
          theta, r = conv_xy_to_polar(x, y)
          self.stick_end.center = (x, y)
          self.stick.set_data([0, np.radians(theta)], [0, r])
  
      def bar_val(self, x, y):
          # from (0, -bv) to (x, -bv)
          self.bar_x.set_data([0, x], [-self.bv, -self.bv])
          # from (-bv, 0) to (-bv, y)
          self.bar_y.set_data([-self.bv, -self.bv], [0, y])
  
      def rc_vals(self, x, y):
          self.stick_val(x, y)
          self.bar_val(x, y)
  
  
  if __name__ == '__main__':
      rc = RemoteControlDisplay(120)
      rc_left = RcStick(rc.fig, rc.rmax, 'climb/ yaw', left=True)
      rc_right = RcStick(rc.fig, rc.rmax, 'pitch/ roll', left=False)
      rc_left.rc_vals(80, -60)
      rc_right.rc_vals(-30, 50)
      plt.show()
  </code></pre>