将matplotlib图从绘图转换为Tkinter canvas小部件嵌入图

时间:2014-12-22 13:33:04

标签: matplotlib tkinter embed

我正在尝试在Tkinter画布小部件中嵌入股票价格图表。我有一个用matplotlib编写的功能齐全的股票价格图表。在尝试将我的图形嵌入Tkinter时,我发现(已被告知)作为绘图编写的matplotlib图形无法在Tkinter中正确嵌入,并且图形必须采用图形的形式才能使嵌入正常工作。为了便于说明,这里是一个绘图形式的matplotlib样本图。

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

这是相同的图,但是以图的形式。

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

schart21代码可以成功嵌入到Tkinter中,但schart20代码不能。我的股票价格图表代码是一个情节的形式,我需要将其转换为数字的形式。我对matplotlib的经验很少,我不确定如何根据需要修改我的代码。这是我的股票价格图表代码。此代码功能齐全,但需要访问基础文件才能工作。

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)

如果有人知道如何将这个从情节转换为数字我会很感激帮助。真诚的,乔治

1 个答案:

答案 0 :(得分:2)

要了解Matplotlib最重要的事情之一是它以三种模式之一运行(缺少更好的词)。 1)它根深蒂固在 pylab ,2)它有一个 pyplot 接口,3)它有它的底层对象接口。我会尝试解开它们,如果有人比我更熟悉我看到下面的内容有什么问题,请告诉我,我会解决它!

第一个应该在几乎所有时间都应该被避免 - 它也会出现numpy和scipy,并且永远不会用于编写脚本或编码。第二个(或应该)主要用于交互式绘图,旨在模仿Matlab的功能。到目前为止,最后一个是编码最强大的,对于你想要的嵌入类型是必不可少的。

你的代码目前正在使用后两者的混搭,这实际上可能会导致问题 - pyplot在幕后做的比做绘图更多。 Matplotlib本质上是面向对象的,为了方便起见,pyplot只是一个巨大的包装器,可以帮助Matlab用户过渡。它使用默认后端进行显示(在我的情况下,大多数情况下它使用Qt4Agg),而你实际上想要强制它使用TkAgg,因此Tkinter知道如何处理它。如果你不小心,Pyplot会干扰它。

你应该保留的唯一一个pyplot调用是数字创建fig = plt.figure()和你的subplot2grid调用。实际上,如果直接导入Figure对象(from matplotlib.figure import Figure),也可以修改数字调用。获得轴后,放弃pyplot并直接使用对象方法(即ax.plot())。在线阅读documentation以了解如何使用它们,它们的通话要求有时会有所不同!

Tkinter嵌入使用了一个FigureCanvasTkAgg对象,它需要一个Figure对象。因此,您的绘图函数必须返回该Figure对象,以便可以使用它来构造FigureCanvasTkAgg。您也不需要任何plt.show() - 所有这一切都会弹出当前的 pyplot 数字,您实际上并不想要,因为您的数字嵌入在GUI中

因此,手头问题的答案与以前相同 - 尽可能多地消除plt.个命令,return fig。然后在GUI代码中你可以做

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

这是一个最小的例子,它运行时没有以类似于你的方式构造错误,而根本没有使用pyplot。

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()