title: tkinter canvas not displaying line with matplotlib chart
body: <p>I have a candlestick chart that I am displaying in tkinter using mplfinance. I have mplfinance return the figure so that I can use matplotlib to find the x and y coordinates that the user may need to draw lines on the chart.</p>
  <p>I have been successful with drawing lines on the chart using the underlying canvas. My idea is to save the lines in a database so that when the user returns to the chart, the lines are still displayed. In addition, the user should be able to edit or delete the lines as well after returning to the chart.</p>
  <p>I have been able to save the lines in the database and retrieve them as well. My problem is that I cannot get them to reappear on the canvas when I start the program. The program is retrieving the lines from the database, and it appears that it is going through the motions of drawing the lines. The lines are not appearing though.</p>
  <p>Using a few print statements, the program is telling me that the lines have been drawn. What do I need to do in order to get the lines to appear on the canvas? My minimal example is below.</p>
  <p>I have not included the code for storing the lines in the database. In my example, the line I am asking the program to draw is not showing up. That is the only problem I am having. What am I missing?</p>
  <p>You can find the csv file that I use <a href="http://www.yellowcas.com/test/ATOS.csv" rel="nofollow noreferrer">here</a>, or you can use any csv file that has open, high, low, close, volume information for a particular equity. Any help would be greatly appreciated.</p>
  <pre><code>from tkinter import *
  import pandas as pd
  import numpy as np
  import datetime as dt
  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  from matplotlib.widgets import MultiCursor
  import mplfinance as mpf
  from functools import partial
  import math
  
  class Example:
      def __init__(self, figax, color='#0000FF', width=1):
          &quot;&quot;&quot;
          This class is used to draw on the canvas of a matplotlib chart.
  
          :param: figax The figure axes object created by matplotlib
          :param: color The color that should be used currently. The default
          color is blue (#0000FF).
          :param: width The width of the line stroke. The default is 1.
          &quot;&quot;&quot;
          self.fig, self.ax = figax
          self.cur_ax = None
          #bbox_height is total height of figure.
          self.bbox_height = self.fig.canvas.figure.bbox.height
          #  bbox_width is total width of figure.
          self.bbox_width = self.fig.canvas.figure.bbox.width
          ax_len = len(self.ax)
          #  Create a list to hold the dimensions of the axes.
          self.ax_dims = []
          #  Create a variable to hold the number of axes in the figure.
          self.ax_ct = 0
  
          self.ax_bounds = None
          #  Get the width and height of each axis in pixels.
          for i in range(0, ax_len, 2):
              self.ax_ct += 1
              dims = self.ax[i].get_window_extent().transformed(self.fig.dpi_scale_trans.inverted())
              awidth, aheight = dims.width, dims.height
              #  awidth is in pixels.
              awidth *= self.fig.dpi
              #  aheight is in pixels.
              aheight *= self.fig.dpi
              d = {'Width': awidth, 'Height': aheight}
              self.ax_dims.append(d)
  
          self.ax_bounds = None
          self.calc_axes_bounds()
          #  Set the ID of the object currently being drawn.
          self.cur_id = None
          self.color = color
          self.width = width
          self.draw_line()
  
      def setColor(self, color):
          self.color = color
  
      def setWidth(self, width):
          self.width = width
  
      def calc_axes_bounds(self):
          self.ax_bounds = []
          #  The first axis (ax[0]) will have a top y coordinate of 0.
          heightTot = 0
          #  Calculate the bounding x, y coordinates for each axis.
  
          for i in range(self.ax_ct):
              #  The x axis is shared by all plots;  therefore, all axes
              #  will start and end at the same x mark.
              x0 = 0
              x1 = math.ceil(self.ax_dims[i]['Width'])
              #  Dealing with the top axis.
              y0 = heightTot
              y1 = self.ax_dims[i]['Height'] + y0
              heightTot += y1
              d = {'x0': x0, 'y0': y0, 'x1': x1, 'y1': y1}
              self.ax_bounds.append(d)
  
      def inaxes(self, x, y):
          for i in range(len(self.ax_bounds)):
              if (self.ax_bounds[i]['x0'] &lt;= x &lt;= self.ax_bounds[i]['x1']) and (self.ax_bounds[i]['y0'] &lt;= y &lt;= self.ax_bounds[i]['y1']):
                 self.cur_ax = i
                 ylim = self.ax[self.cur_ax].get_ylim()
  
      def draw_line(self):
          self.cur_ax = 0
          self.cur_id = Line(self, 156, 39, 861, 273, self.color, self.width)
          print(&quot;Done!&quot;)
  
  
  class Line:
      def __init__(self, parent, x0, y0, x1, y1, color, width):
          self.parent = parent
          self.ax = self.parent.ax
          self.id = None
          self.x0 = x0
          self.y0 = y0
          self.x1 = x1
          self.y1 = y1
          self.fig = self.parent.fig
          self.color = color
          self.width = width
          #bbox_height is total height of figure.
          self.bbox_height = self.fig.canvas.figure.bbox.height
          #  bbox_width is total width of figure.
          self.bbox_width = self.fig.canvas.figure.bbox.width
          #  The current axis that is being worked with
          self.cur_ax = self.parent.cur_ax
          #print(&quot;Current axis is:&quot;, self.cur_ax)
          #self.ax_bounds = self.parent.ax_bounds
          self.id = None
          self.draw()
  
      def draw(self):
          print(&quot;x0 is:&quot;, self.x0)
          print(&quot;y0 is:&quot;, self.y0)
          print(&quot;x1 is:&quot;, self.x1)
          print(&quot;y1 is:&quot;, self.y1)
          self.id = self.fig.canvas._tkcanvas.create_line(self.x0, self.y0, self.x1, self.y1, fill=self.color, width=self.width, activewidth=2, smooth=True)
          print(&quot;ID is:&quot;, self.id)
  
      def __str__(self):
          return str(self.id)
  
  
  if __name__ == '__main__':
      dashboard = Tk()
      dashboard.geometry(&quot;1200x700&quot;)
      dashboard['bg'] = 'grey'
      dashboard.title(&quot;Example Drawing Tools&quot;)
      dashboard.state(&quot;zoomed&quot;) #  Makes the window fully enlarged
      # Opening data source
      df = pd.read_csv(&quot;ATOS.csv&quot;, index_col=0, parse_dates=True)
      dates = df.index.to_pydatetime().tolist()
      # Create `marketcolors` to use with the `charles` style:
      mc = mpf.make_marketcolors(up='#008000',down='#FF0000', vcdopcod=True, inherit=True)
      # Create a new style based on `charles`.
      sm_style = mpf.make_mpf_style(base_mpf_style='charles',
                                   marketcolors=mc,
                                   facecolor='#FFFFFF',
                                   edgecolor='#999999',
                                   figcolor='#FFFFFF'
                                  )
  
      figax =  mpf.plot(df,
                      warn_too_much_data=6000,
                      panel_ratios=(3,1),
                      type=&quot;candle&quot;,
                      volume=True,
                      figsize=(12, 7),
                      main_panel=0,
                      volume_panel=1,
                      num_panels=2,
                      tight_layout=True,
                      scale_padding={'left': 0.02, 'top': 0, 'right': 1.2, 'bottom': 0.5},
                      ylabel=&quot;&quot;,
                      style=sm_style,
                      returnfig=True
                  )
      fig, ax = figax
      vol_ax = ax[2]
      vol_ax.set_xlabel(&quot;&quot;)
      vol_ax.set_ylabel(&quot;&quot;)
  
      canvasbar = FigureCanvasTkAgg(fig, master=dashboard)
      cursor = MultiCursor(canvasbar, ax, horizOn=True, vertOn=True, linewidth=0.75, color='#000000')
      canvasbar.draw()
  
      examp = Example(figax)
      canvasbar.get_tk_widget().grid(row=0, column=0, columnspan=5, padx=0, pady=(0,20))
      btn1 = Button(dashboard, text=&quot;Exit&quot;, command=quit)
      btn1.grid(row=0, column=6, padx=5, pady=10, sticky='n')
      dashboard.mainloop()
  </code></pre>
  <p><strong>Edit:</strong></p>
  <p>This is the function that allows the user to draw a line on the screen.</p>
  <pre><code>    def draw_trend_line(self, event):
          #print(&quot;cur_draw_id is:&quot;, str(self.cur_draw_id))
          #print(&quot;Begin draw_trend_line&quot;)
          self.event = event
          #print(&quot;Event (x,y) is:&quot;, self.event.x, self.event.y)
          if self.cur_draw_id is not None:
              self.remove()
  
              xMin = math.ceil(self.ax_bounds[self.cur_ax]['x0'])
              xMax = math.ceil(self.ax_bounds[self.cur_ax]['x1'])
              yMin = math.ceil(self.ax_bounds[self.cur_ax]['y0'])
              yMax = math.ceil(self.ax_bounds[self.cur_ax]['y1'])
              #print(&quot;yMax is:&quot;, yMax)
              if self.event.x &gt;= xMax:
                  x0 = xMax
  
              elif self.event.x &lt;= xMin:
                  x0 = xMin
  
              else:
                  x0 = self.event.x
  
              if self.event.y &gt;= yMax:
                  y0 = yMax
  
              elif self.event.y &lt;= yMin:
                  y0 = yMin
  
              else:
                  y0 = self.event.y
  
              #  Starting Position
              if self.x_start is None:
                  self.x_start = x0
  
              else:
                  x0 = self.x_start 
  
              if self.y_start is None:
                  self.y_start = y0
  
              else:
                  y0 = self.y_start
  
              #  Ending Position
              if self.event.x &gt;= xMax:
                  x1 = xMax
  
              elif self.event.x &lt;= xMin:
                  x1 = xMin
  
              else:
                  x1 = self.event.x
  
              if self.event.y &gt;= yMax:
                  y1 = yMax
  
              elif self.event.y &lt;= yMin:
                  y1 = yMin
  
              else:
                  y1 = self.event.y
  
              self.cur_draw_id = Line(self, x0, y0, x1, y1, self.color, self.width)
          #print(&quot;End draw_trend_line&quot;)
  </code></pre>
  <p>I want to be able to replicate the lines the user draws when they open the program the next time. I realize that I have to save the line in a database, which I have no problems with. I can retrieve the coordinates for the line from the database. The program just doesn't display it.</p>
  <p>The print statements show that the program is supposedly drawing the line. I have even tried forcing the canvas to redraw using <code>self.fig.canvas.draw()</code>.</p>
  <p>In the draw_trend_line function, I have a variable called <code>self.cur_ax</code>. In my full program, I am using panels, so there could be multiple axes. Please feel free to ask any questions about anything that you want me to elaborate on.</p>