0

Thanks in advance for your precious help !!! I fail to embed matplotlib in tkinter. Can you guide me?

I have imported all the correct modules that are to say matplotlib.pyplot, matplotlib.dates, FigureCanvasTkAgg, NavigationToolbar2Tk, key_press_handler, Figure, etc..

and then...

root = tk.Tk()
root.wm_title("Embedding in Tk")

def bytespdate2num(fmt, encoding ='utf-8'):
    strconverter = mdates.strpdate2num(fmt)
    def bytesconverter(b):
        s = b.decode(encoding)
        return strconverter(s)
    return bytesconverter 

def graph_data(stock):
    fig = plt.figure()
    ax1 = plt.subplot2grid((1,1), (0,0))
    url_stock = 'https://pythonprogramming.net/yahoo_finance_replacement'
    source_code = urllib.request.urlopen(url_stock).read().decode()
    stock_data = []
    source_split = source_code.split('\n')

    for line in source_split[1:]:
        line_split = line.split(',')
        if len(line_split) == 7:
            if 'values' not in line and 'labels' not in line:
                stock_data.append(line)
    date, closep, highp, lowp, openp, adj_closep, volume = np.loadtxt(stock_data, delimiter =',', unpack= True, converters={0: bytespdate2num('%Y-%m-%d')})

    ax1.plot_date(date, closep, '-', label ='closing price')
    ax1.axhline(closep[0], color='k', linewidth = 2)
    ax1.fill_between(date, closep, closep[0], where=(closep > closep[0]), facecolor='g', alpha=0.5)
    ax1.fill_between(date, closep, closep[0], where=(closep < closep[0]), facecolor ='r', alpha = 0.5)
    ax1.xaxis.label.set_color('c')
    ax1.yaxis.label.set_color('r')
    ax1.set_yticks([0,100,200,300,400,500,600,700,800,900,1000])

    for label in ax1.xaxis.get_ticklabels():
        label.set_rotation(45)
    ax1.grid(True, color= 'r', linestyle='-', linewidth=0.5)

    plt.subplots_adjust(left = 0.09, bottom =0.18, right= 0.94, top= 0.95, wspace=0.2, hspace=0)
    plt.title('stock')
    plt.xlabel('dates')
    plt.ylabel('price')
    plt.legend()
    plt.show()

here is where things block I think

    canvas = FigureCanvasTkAgg(fig, master= root)  # A tk.DrawingArea.
    canvas.draw()
    canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    toolbar = NavigationToolbar2Tk(canvas, root)
    toolbar.update()
    canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

graph_data('EBAY')
tk.mainloop()

Thank you again ;)

Moshe Slavin
  • 5,127
  • 5
  • 23
  • 38
Lily H.
  • 164
  • 2
  • 10
  • 1
    In what way does it fail? Please explain your problem fully. Do you get an Error? If so, provide the full trace. It looks like the whole graph_data() code is superfluous. Have you tried to do a very simple plot first? – Diziet Asahi Oct 15 '18 at 07:29

1 Answers1

1

It's hard to completely understand the problem from the code you provided. If you can be more accurate about the nature of the error/problem or post the complete code it would be probably easier to help.

The basic idea is that when you embed into tkinter is not enough to show the image using the matplotlib way (plt.show) but you also need to create a canvas element and draw the image on it. So I guess the last part of the method graph_data(stock): should be modified, including the method draw_figure from matplotlib (code here) for example:

def draw_figure(canvas, figure, loc=(0, 0)):
""" Draw a matplotlib figure onto a Tk canvas

loc: location of top-left corner of figure on canvas in pixels.
Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
"""
  figure_canvas_agg = FigureCanvasAgg(figure)
  figure_canvas_agg.draw()
  figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
  figure_w, figure_h = int(figure_w), int(figure_h)
  photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)

  # Position: convert from top-left anchor to center anchor
  canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)

  # Unfortunately, there's no accessor for the pointer to the native renderer
  tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)

  # Return a handle which contains a reference to the photo object
  # which must be kept live or else the picture disappears
  return photo

def graph_data(stock, canvas): 
  # do you really need stock parameter? it is not used
  fig = plt.figure()
  ax1 = plt.subplot2grid((1,1), (0,0))
  url_stock = 'https://pythonprogramming.net/yahoo_finance_replacement'
  source_code = urllib.request.urlopen(url_stock).read().decode()
  stock_data = []
  source_split = source_code.split('\n')

  for line in source_split[1:]:
    line_split = line.split(',')
    if len(line_split) == 7:
        if 'values' not in line and 'labels' not in line:
            stock_data.append(line)
  date, closep, highp, lowp, openp, adj_closep, volume = np.loadtxt(stock_data, delimiter =',', unpack= True, converters={0: bytespdate2num('%Y-%m-%d')})

  ax1.plot_date(date, closep, '-', label ='closing price')
  ax1.axhline(closep[0], color='k', linewidth = 2)
  ax1.fill_between(date, closep, closep[0], where=(closep > closep[0]), facecolor='g', alpha=0.5)
  ax1.fill_between(date, closep, closep[0], where=(closep < closep[0]), facecolor ='r', alpha = 0.5)
  ax1.xaxis.label.set_color('c')
  ax1.yaxis.label.set_color('r')
  ax1.set_yticks([0,100,200,300,400,500,600,700,800,900,1000])

  for label in ax1.xaxis.get_ticklabels():
    label.set_rotation(45)
  ax1.grid(True, color= 'r', linestyle='-', linewidth=0.5)

  plt.subplots_adjust(left = 0.09, bottom =0.18, right= 0.94, top= 0.95, wspace=0.2, hspace=0)
  plt.title('stock')
  plt.xlabel('dates')
  plt.ylabel('price')
  plt.legend()
  plt.show()

  fig_x, fig_y = 100, 100
  fig_photo = draw_figure(canvas, fig, loc=(fig_x, fig_y))
  fig_w, fig_h = fig_photo.width(), fig_photo.height()

So it just uses the canvas that you created and draw on it the image you plotted in matplotlib. It is hard to tell if it would work already like this or if it would need a small edit because I don't see the whole code, but this should give you a hint.

I can point out to you the full documentation that gives a simple example on how to embed one image in tkinter, https://matplotlib.org/gallery/user_interfaces/embedding_in_tk_canvas_sgskip.html and you could try using this as a test

or another way to do it by converting the image using the PIL library (that's the solution I used) and then using into Tkinter (that's more straightforward after the conversion) https://solarianprogrammer.com/2018/04/20/python-opencv-show-image-tkinter-window/

freerafiki
  • 539
  • 3
  • 10