1

I am trying to embed a stock price chart in a Tkinter canvas widget. I have a fully functional stock price graph written in matplotlib. While trying to embed my graph in Tkinter I have discovered (been informed) that a matplotlib graph that is written as a plot can not be properly embeded in Tkinter and that the graph must be in the form of a figure for the embedding to properly work. For illustration purposes here is a matplotlib sample graph in the form of a plot.

def schart20(stock_sym):
    x = [1,2,3,4]
    y = [20,21,20.5, 20.8]
    plt.plot(x,y)
    plt.show()

Here is the same graph but in the form of a figure.

def schart21(stock_sym):
    x = [1,2,3,4]
    y = [20,21,20.5, 20.8]
    fig = Figure()
    axes = fig.add_subplot(111)
    axes.plot(x,y)
    return fig

The schart21 code can be sucessfully embedded in Tkinter but the schart20 code cannot. My stock price graph code is in the form of a plot and I need to convert it to the form of a figure. I have very little experience with matplotlib and am not sure how to modify my code as required. Here is my stock price chart code. This code is fully functional but requires access to an underlying file to work.

def graphData(stock, MA1, MA2):

    try:
        stockFile = '/Users/BioASys/BioasysDB/CompanyStockPriceData/'+stock+'.txt'



        date, closep, highp, lowp, openp,volume=np.loadtxt(stockFile,delimiter=',',unpack=True,
converters={ 0: mdates.strpdate2num('%Y%m%d')})

        x = 0
        y = len(date)
        candleAr = []
        while x < y:
            appendLine = date[x], openp[x], closep[x], highp[x], lowp[x], volume[x]
            candleAr.append(appendLine)
            x+=1


        Av1 = movingaverage(closep, MA1)
        Av2 = movingaverage(closep, MA2)

        SP = len(date[MA2-1:])

        label1 = str(MA1) + ' SMA'
        label2 = str(MA2) + ' SMA'


        fig = plt.figure()


        ax1 = plt.subplot2grid((5,4),(0,0), rowspan=4, colspan=4)
        candlestick(ax1, candleAr[-SP:], width=0.6, colorup='g', colordown='r')

        ax1.plot(date[-SP:],Av1[-SP:],'#5998ff', label=label1, linewidth=1.5)
        ax1.plot(date[-SP:],Av2[-SP:],'blue', label=label2, linewidth=1.5)


        plt.ylabel('Stock price')
        ax1.grid(True)
        plt.legend(loc=3,prop={'size':7},fancybox=True,)

        ax2 = plt.subplot2grid((5,4), (4,0), sharex=ax1, rowspan=1, colspan=4)
        #ax2 = plt.subplot2grid((5,4), (4,0), rowspan=1, colspan=4)
        ax2.bar(date, volume)
        ax2.axes.yaxis.set_ticklabels([])
        plt.ylabel('Volume')
        ax2.grid(True)


        ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))




        for label in ax1.xaxis.get_ticklabels():
            label.set_rotation(90)

        for label in ax2.xaxis.get_ticklabels():
            label.set_rotation(45)


        plt.xlabel('Date')
        #plt.ylabel('Stock Price')
        plt.suptitle(stock+' Stock Price')

        plt.setp(ax1.get_xticklabels(), visible=False)

        plt.subplots_adjust(left=.09, bottom=.18, right=.94, top=.94, wspace=.20, hspace=0)

        plt.show()
        #fig.savefig('example.png') # saves a copy of the sticok chart to example.png


    except Exception, e:
        print 'failed main loop',str(e)

If anybody knows how to convert this from a plot to a figure I would appreciate the help. Sincerely, George

4
  • Since I answered your previous question and I can see where you are confused, I have a clarification question which may affect my answer. How familiar are you with object-oriented programming in general? Commented Dec 22, 2014 at 21:33
  • Hi Ajean, Thanks for responding. I've been programming Python for about 3 - 4 years. I've worked through Lutz's Learning Python and Programming Python as well as Bird's Natural Language Processing with Python, as well as a couple of other Python oriented computer science books. I've written dozens of object oriented class code modules during my studies, the majority of them geared towards natural language processing. Commented Dec 22, 2014 at 23:48
  • This is my first attempt at a GUI and I have virtually no matplolib experience. So to directly answer your question I'd say I have an intermediate level understanding of object oriented coding and an intermediate to advanced level understanding of Python itself. Commented Dec 22, 2014 at 23:48
  • Ah ok, so just new to matplotlib. I'll see if I can disentangle it for you - in my opinion a lot of the current official examples are written in a way that isn't the best. Commented Dec 23, 2014 at 0:25

1 Answer 1

3

One of the most important things to learn about Matplotlib is that it operates in one of three modes (for lack of a better word). 1) It is ingrained in pylab, 2) it has a pyplot interface, and 3) it has its underlying object interface. I'll try to disentangle them, and if anybody more familiar than I sees anything wrong with what I say below, please let me know and I'll fix it!

The first should really be avoided almost all of the time - it also bleeds in numpy and scipy, and should never be used for scripting or coding. The second is (or should be) primarily used for interactive plotting and is designed to mimic Matlab's functionality. The last one is by far the most robust for coding and is essential to the kind of embedding you want to do.

Your code currently uses a mishmash of the latter two, which can actually cause problems - pyplot does more behind the scenes than just make the plots. Matplotlib is inherently object-oriented, and pyplot is just a giant wrapper around it for convenience and to help Matlab users transition. It uses a default backend for display (in my case most of the time it uses Qt4Agg), whereas you actually want to force it to use TkAgg so Tkinter knows what to do with it. Pyplot can interfere with that if you aren't very careful.

The only pyplot calls you really should keep are the figure creation fig = plt.figure(), and your subplot2grid calls. Actually you can nix the figure call too if you import the Figure object directly (from matplotlib.figure import Figure). Once you have your axes, abandon pyplot and use the object methods directly (i.e. ax.plot()). Read up on the documentation online to see how to use them, the calling requirements for them are sometimes different!

The Tkinter embedding uses a FigureCanvasTkAgg object, which requires a Figure object. Therefore your plotting function has to return that Figure object so that it can be used to construct the FigureCanvasTkAgg. You also don't need any plt.show() - all that does is pop up the current pyplot figure, which you actually don't want because your figure is embedded in your GUI.

Therefore the answer to the problem at hand is the same as before - eliminate as many of the plt. commands as you can, and return fig. Then in the GUI code you can do

fig = graphData(stock, MA1, MA2)
canvas = FigureCanvasTkAgg(fig)

Here is a minimum example that runs with no errors constructed in a way similar to yours, without using pyplot at all.

import Tkinter as Tk
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

def myplotcode():
    x = np.linspace(0,2*np.pi)
    fig = Figure()
    ax = fig.add_subplot(111)
    ax.plot(x, x**2)

    return fig

class mygui(Tk.Frame):
    def __init__(self, parent):
        Tk.Frame.__init__(self, parent)
        self.parent = parent

        self.fig = myplotcode()
        self.canvas = FigureCanvasTkAgg(self.fig, master=parent)
        self.canvas.show()

        self.canvas.get_tk_widget().pack()
        self.pack(fill=Tk.BOTH, expand=1)

root = Tk.Tk()
app = mygui(root)

root.mainloop()
Sign up to request clarification or add additional context in comments.

5 Comments

Hi, Thanks again for your response. I really appreciate all the information you are providing. The information in your first four paragraphs was all new to me. I understand the first three paragraphs without problem but the fourth will require a fair amount of effort to work through. I have looked at several examples of code very similar to your "def myploycode()" over the past three days and I have not been able to see how to effect the changes you describe in paragraph four on my existing code.
Unfortunately with my limited matplotlib experience I can not tell what part of the code is pyplot and which part is an object method. So I am unable to modify the code. For insatnce when I look at a piece of code like this:
for label in ax1.xaxis.get_ticklabels(): label.set_rotation(90) I have no idea what to do with it. Is this part of pyplot or is it an object method? I suspect I am not going to gain a solution to this issue until I get the opportunity to learn matplotlib in much greater detail. Thanks again for all your help, George
Pyplot can pretty easily be identified by the plt., because it is usually imported like import matplotlib.pyplot as plt. Any method that stems from a variable (such as fig or ax) is likely an object method. Your last comment example is an object method of an attribute. I definitely do recommend spending some time learning the API, that's the best thing for you.
Hi Ajean, Thanks again for all your help. Learning matplotlib is on my list but with my current workload I estimate I'm about a year away from when I can begin to study matplot in earnest. Take Care, George

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.