用Tkinter打造GUI开发工具(49)在Tkinter窗口上动态显示matplotlib.pyplot图形

Posted 荷蒲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Tkinter打造GUI开发工具(49)在Tkinter窗口上动态显示matplotlib.pyplot图形相关的知识,希望对你有一定的参考价值。

用Tkinter打造GUI开发工具(49)在Tkinter窗口上动态显示matplotlib.pyplot图形
因为要在Tkinter窗口上动态显示matplotlib.pyplot图形,我首先采用了最先能想到的Tkinter的布局方法。
画图,显示到Tkinter窗口,删除掉绘图,重新画图,并显示到Tkinter窗口。
实现的程序如下。

import random
import numpy as np
import tkinter as tk
from tkinter import *
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)    ##matplotlib 3.0.2 
from matplotlib.figure import Figure
#独狼荷蒲qq:2775205
#通通小白python量化群:524949939
#电话微信:18578755056
#通通小白python量化群:524949939
#tkinter,pyqt,gui,Python学习群:647866213

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
global c,a,root

#----------------------------------------------------------------------
def drawPic():
    global c,a,root
    """
    获取GUI界面设置的参数,利用该参数绘制图片
    """
       
    #获取GUI界面上的参数
    try:
        sampleCount=int(inputEntry.get())
        r=random.randint(0,1000)
        inputEntry.delete(0,END)
        inputEntry.insert(0,str(r))
    except:
        sampleCount=50
        print( '请输入整数')
        inputEntry.delete(0,END)
        inputEntry.insert(0,'50')
           
    #清空图像,以使得前后两次绘制的图像不会重叠
    f.clf()
    a=f.add_subplot(111)
    
    #在[0,100]范围内随机生成sampleCount个数据点
    x=np.random.randint(0,100,size=sampleCount)
    y=np.random.randint(0,100,size=sampleCount)
    color=['b','r','y','g']

    #绘制这些随机点的散点图,颜色随机选取
    a.scatter(x,y,s=3,color=color[np.random.randint(len(color))])
    a.set_title('画图演示')
    plt.show()

def drawPic2():
    global c,a,root
    """
    获取GUI界面设置的参数,利用该参数绘制图片
    """
       
    #获取GUI界面上的参数
    try:
        sampleCount=int(inputEntry.get())
        r=random.randint(0,1000)
        inputEntry.delete(0,END)
        inputEntry.insert(0,str(r))
    except:
        sampleCount=50
        print( '请输入整数')
        inputEntry.delete(0,END)
        inputEntry.insert(0,'50')
           
    #清空图像,以使得前后两次绘制的图像不会重叠
    f.clf()
    a=f.add_subplot(111)
    
    #在[0,100]范围内随机生成sampleCount个数据点
    x=np.random.randint(0,100,size=sampleCount)
    y=np.random.randint(0,100,size=sampleCount)
    color=['b','r','y','g']

    #绘制这些随机点的散点图,颜色随机选取
    a.scatter(x,y,s=3,color=color[np.random.randint(len(color))])
    a.set_title('画图演示 %d'%sampleCount)
    c.grid_forget()
    c=Canvas(root)
    c.grid(row=0, columnspan=3)    
    canvas = FigureCanvasTkAgg(f, master=c)
    canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    #canvas.show()
    
    
if __name__ == '__main__':
    global c,a,root

    root=Tk()
    #放置标签、文本框和按钮等部件,并设置文本框的默认值和按钮的事件函数
    Label(root,text='请输入样本数量:').grid(row=1,column=0)
    inputEntry=Entry(root)
    inputEntry.grid(row=1,column=1)
    inputEntry.insert(0,'50')
    
    c=Canvas(root)
    c.grid(row=0, columnspan=3)
    
    #在Tk的GUI上放置一个画布,并用.grid()来调整布局
    f = plt.Figure(figsize=(5,4), dpi=100) 
    drawPic()
    canvas = FigureCanvasTkAgg(f, master=c)
    canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    
    Button(root,text='画图',command=drawPic2).grid(row=1,column=2,columnspan=3)
     
    #启动事件循环
    root.mainloop()

程序运行结果如下图。

我们可以看到,快速更换图形,会引起画面的闪烁。这样的话,对用户体验不好。
这个需要如何解决问题呢?
我们可以使用matplotlib.animation的模块,来动态显示图形。
在matplotlib作图中,用的是matplotlib.pyplot模块,这个模块有非常多的属性和方法,简要列举下这次用到的方法:

matplotlib.pyplot.subplots(nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, **fig_kw)

返回fig和ax对象!

下面我们直接给出示例代码,供读者参考。

# -*- coding: utf-8 -*-
import random
import numpy as np
import tkinter as tk
from tkinter import *
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg, NavigationToolbar2Tk)    ##matplotlib 3.0.2 
from matplotlib.figure import Figure

#独狼荷蒲qq:2775205
#通通小白python量化群:524949939
#电话微信:18578755056
#通通小白python量化群:524949939
#tkinter,pyqt,gui,Python学习群:647866213

plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
global c,a,root

#----------------------------------------------------------------------
def drawPic():
    global c,a,root
    """
    获取GUI界面设置的参数,利用该参数绘制图片
    """
       
    #获取GUI界面上的参数
    try:
        sampleCount=int(inputEntry.get())
        r=random.randint(0,1000)
        inputEntry.delete(0,END)
        inputEntry.insert(0,str(r))
    except:
        sampleCount=50
        print( '请输入整数')
        inputEntry.delete(0,END)
        inputEntry.insert(0,'50')
           
    #清空图像,以使得前后两次绘制的图像不会重叠
    f.clf()
    a=f.add_subplot(111)
    
    #在[0,100]范围内随机生成sampleCount个数据点
    x=np.random.randint(0,100,size=sampleCount)
    y=np.random.randint(0,100,size=sampleCount)
    color=['b','r','y','g']

    #绘制这些随机点的散点图,颜色随机选取
    a.scatter(x,y,s=3,color=color[np.random.randint(len(color))])
    a.set_title('画图演示')
    plt.show()

def drawPic2():
    global c,a,root
    """
    获取GUI界面设置的参数,利用该参数绘制图片
    """
       
    #获取GUI界面上的参数
    try:
        sampleCount=int(inputEntry.get())
        r=random.randint(0,1000)
        inputEntry.delete(0,END)
        inputEntry.insert(0,str(r))
    except:
        sampleCount=50
        print( '请输入整数')
        inputEntry.delete(0,END)
        inputEntry.insert(0,'50')
           
    #清空图像,以使得前后两次绘制的图像不会重叠
    f.clf()

    #在[0,100]范围内随机生成sampleCount个数据点
    x=np.random.randint(0,100,size=sampleCount)
    y=np.random.randint(0,100,size=sampleCount)
    color=['b','r','y','g']

    #绘制这些随机点的散点图,颜色随机选取
    a.scatter(x,y,s=3,color=color[np.random.randint(len(color))])
    a.set_title('画图演示 %d'%sampleCount)
    canvas = FigureCanvasTkAgg(f, master=c)
    c.update()


import matplotlib.animation as animation
def plt01():
    f = plt.figure(figsize=(6, 6))
    ax = plt.gca()
    ax.grid()
    ln1, = ax.plot([], [], '-', lw=2)
    ln2, = ax.plot([], [], '-', color='r', lw=2)
    theta = np.linspace(0, 2*np.pi, 100)
    r_out = 1
    r_in = 0.5
    time_template = 'time = %.1fs'
    time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)
    
    def init():
        ax.set_xlim(-2, 2)
        ax.set_ylim(-2, 2)
        x_out = [r_out*np.cos(theta[i]) for i in range(len(theta))]
        y_out = [r_out*np.sin(theta[i]) for i in range(len(theta))]
        ln1.set_data(x_out, y_out)
        return ln1,
    
    def update(i):
        x_in = [(r_out-r_in)*np.cos(theta[i])+r_in*np.cos(theta[j]) for j in range(len(theta))]
        y_in = [(r_out-r_in)*np.sin(theta[i])+r_in*np.sin(theta[j]) for j in range(len(theta))]
        ln2.set_data(x_in, y_in)
        time_text.set_text(time_template %(0.1*i))
        return ln2,
    
    c=Canvas(root)
    c.grid(row=0, columnspan=3)
    canvas = FigureCanvasTkAgg(f, master=c)
    canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    ani = animation.FuncAnimation(f, update, range(len(theta)), init_func=init, interval=10)

    #ani.save('ls.gif', writer='imagemagick', fps=100)
    plt.show()
    plt.close() # 关窗口

if __name__ == '__main__':
    global c,a,root
    #matplotlib.use('TkAgg')
    root=Tk()
    plt01()
     
    #启动事件循环
    root.mainloop()

下面是程序运行的结果。

我们可以看到,现在窗口和图形不再闪烁了。一些朋友说直接用plt窗口就可以直接显示动态图形了,没必要套在一起使用。
我们这样使用是为了第三代小白量化软件的设计,第三代图将实现实时数据K线或 分时图,需要比较连贯的图形显示方法,下来我们就用这个技术尝试一下。
下面是我们第三代小白量化软件的截图,还没有开发完成。

可以通过下面方式与我交流:
#独狼荷蒲qq:2775205
#通通小白python量化群:524949939
#电话微信:18578755056
#通通小白python量化群:524949939
#tkinter,pyqt,gui,Python学习群:647866213

欢迎继续关注我的博客。
超越自己是我的每一步!我的进步就是你的进步!

以上是关于用Tkinter打造GUI开发工具(49)在Tkinter窗口上动态显示matplotlib.pyplot图形的主要内容,如果未能解决你的问题,请参考以下文章

用Tkinter打造自己的Python IDE开发工具利用HP_tk模块设计自己的代码编辑器

用Tkinter打造自己的Python IDE开发工具利用HP_tk模块设计自己的代码编辑器

用Tkinter打造自己的Python IDE开发工具利用HP_tk模块设计自己的代码编辑器

用tkinter实现的gui小工具

用Tkinter打造自己的Python IDE开发工具Python多文件共享变量与智能插件设计

用Tkinter打造自己的Python IDE开发工具Python多文件共享变量与智能插件设计