简体   繁体   English

带有pylab / matplotlib嵌入的Tkinter的带有播放,暂停,停止可布线性的彩色图动画:无法更新图形/画布?

[英]color plot animation with play, pause, stop cabability using Tkinter with pylab/matplotlib embedding: can't update figure/canvas?

I've looked but didn't find previous questions specific enough, so sorry if this is repeated. 我已经查看过,但是没有找到足够具体的先前问题,对不起,如果再重复一次。 Goal: GUI to continuously update figure with different matrix data plotted by pylab's pcolor such that there is a running animation. 目标:GUI用pylab的pcolor绘制的具有不同矩阵数据的图形不断更新图形,从而使动画运行。 But user should be able to play, pause, stop animation by Tkinter widget buttons. 但是用户应该能够通过Tkinter小部件按钮播放,暂停和停止动画。

Before I get an answer for matplotlib using set_array(), draw(), and canvas.manager.after() ..., I have working code that enables me to start animation, but i can't figure out how to stop or pause it, when just using matplotlib capabilities, so I decided to use Tkinter straigt up instead of matplotlib's Tcl/Tk wrapper. 在我使用set_array(),draw()和canvas.manager.after()...得到matplotlib的答案之前,我有能使我开始动画的有效代码,但我不知道该如何停止或仅在使用matplotlib功能时暂停它,所以我决定使用Tkinter straigt代替matplotlib的Tcl / Tk包装器。 Here is working code just for kicks though in case someone has any ideas. 这是一些有用的代码,尽管万一有人有任何想法。 But continue for real question. 但是继续提出真正的问题。

# mouse click the "Play" button widget to play animation
# PROBLEMS:
# 1. can't pause or stop animation. once loop starts it cant be broken
# 2. set_array attribute for pcolor PolyCollection object only updates the matrix
# C of pcolor, however for my actual application, I will be using pcolor(x,y,C)
# and will have new x,y, and C per plot. Unlike line object, where set_xdata and
# set_ydata are used, I can't find an analogy to pcolor. If I were updating an
# image I could use set_data(x,y,C), but i am not importing an image. I assume
# pcolor is still my best bet unless (as in Matlab) there is an equivalent
# image(x,y,C) function?

import time as t
from pylab import *
from matplotlib.widgets import Button
ion()
def pressPlay(event):
    #fig=figure()
    ax = subplot(111)
    subplots_adjust(bottom=0.2)
    c=rand(5,5)
    cplot=pcolor(c)
    draw()
    for i in range(5):
        c=rand(5,5)
        cplot.set_array(c.ravel())
        cplot.autoscale()
        title('Ionosphere '+str(i+1))
        t.sleep(3)
        draw()
axPlay = axes([0.7, 0.05, 0.1, 0.075])
bPlay = Button(axPlay, 'Play')
bPlay.on_clicked(pressPlay)

Btw: in importing pylab the TkAgg backend is automatically set for use in matplotlib... i think. 顺便说一句:在导入pylab时,TkAgg后端会自动设置为在matplotlib中使用...我认为。 Or somehow I automatically use TkAgg. 或以某种方式我自动使用TkAgg。 I am running Linux, Python 2.6.4, Ipython 0.10. 我正在运行Linux,Python 2.6.4,Ipython 0.10。

I manipulated code found from Daniweb IT Discussion Community so that using Tkinter and the update_idletasks() function I can play, plause, stop the changing colors of a label widget. 我操纵了从Daniweb IT讨论社区中找到的代码,以便使用Tkinter和update_idletasks()函数可以播放,显示,停止更改标签小部件的颜色。 This can be run on python alone as long as Tkinter is installed. 只要安装了Tkinter,就可以单独在python上运行。 No matplotlib or pylab used. 不使用matplotlib或pylab。 This is working code and the backbone of the final code I question. 这是工作代码,也是我提出的最终代码的基础。

# This is meant to draw Start and Stop buttons and a label
# The Start button should start a loop in which the label
# is configured to change color by looping through a color list.
# At each pass through the loop the variable self.stop is checked:
# if True the loop terminates.
# The Stop button terminates the loop by setting the
# variable self.stop to True.
# The Start button restarts the animation from the beginning
# if Stop was hit last, or restarts the animation from where it left off
# if pause was hit last.
# The loop also terminates on the last color of the list, as if stop were hit


from Tkinter import *
colors = ['red','green','blue','orange','brown','black','white','purple','violet']
numcol=len(colors)
class SGWidget(Frame):
    def __init__(self, parent=None):
        Frame.__init__(self, parent)
        self.top_frame = Frame(bg='green')
        self.top_frame.grid()
        # enter event loop until all idle callbacks have been called.
        self.top_frame.update_idletasks()
        self.makeToolbar()
        # construct a label widget with the parent frame top_frame
        self.label = Label(self.top_frame,text = 'Text',bg='orange')
        self.label.grid()
 # initialize (time index t)
        self.t=0

    def makeToolbar(self):
        self.toolbar_text = ['Play','Pause','Stop']
        self.toolbar_length = len(self.toolbar_text)
        self.toolbar_buttons = [None] * self.toolbar_length

        for toolbar_index in range(self.toolbar_length):
            text = self.toolbar_text[toolbar_index]
            bg = 'yellow'
            button_id = Button(self.top_frame,text=text,background=bg)
            button_id.grid(row=0, column=toolbar_index)
            self.toolbar_buttons[toolbar_index] = button_id

            def toolbar_button_handler(event, self=self, button=toolbar_index):
                return self.service_toolbar(button)

            # bind mouse click on start or stop to the toolbar_button_handler
            button_id.bind("<Button-1>", toolbar_button_handler)

    # call blink() if start and set stop when stop            
    def service_toolbar(self, toolbar_index):
        if toolbar_index == 0:
            self.stop = False
            print self.stop
            self.blink()
        elif toolbar_index == 1:
            self.stop = True
            print self.stop
        elif toolbar_index == 2:
            self.stop = True
            print self.stop
            self.t=0

    # while in start, check if stop is clicked, if not, call blink recursivly
    def blink(self):
        if not self.stop:
            print 'looping',self.stop
            self.label.configure(bg=colors[self.t])
            self.t += 1
            if self.t == numcol: # push stop button
                self.service_toolbar(2)
            self.label.update_idletasks()
            self.after(500, self.blink)

if __name__ == '__main__':
    SGWidget().mainloop()

Then, with help from matplotlib example embedding_in_tk.html, http://matplotlib.sourceforge.net/examples/user_interfaces/embedding_in_tk.html , I manipulated the previous code to animate a canvas connected to the pcolor figure. 然后,在matplotlib示例embedding_in_tk.html, http: //matplotlib.sourceforge.net/examples/user_interfaces/embedding_in_tk.html的帮助下,我操纵了先前的代码来为连接到pcolor图形的画布设置动画。 However, updating the canvas with canvas.get_tk_widget() doesn't do the trick I assume because of the command before it that re-plots pcolor(). 但是,使用canvas.get_tk_widget()更新canvas并不能达到我想像的效果,因为之前的命令会重新绘制pcolor()。 So I'm guessing I have to reconnect the canvas with the figure every time I re-plot? 所以我想我每次重新绘图时都必须重新将画布与图形连接起来吗? But I don't know how. 但是我不知道如何。 I hope I'm even on the right track using update_idletasks()??? 我希望我使用update_idletasks()都能走上正确的道路?

So, with the following code, all I see is the same plot when the code is looping through Play, instead of an updated figure? 因此,使用以下代码,当代码在Play中循环播放时,我看到的只是一个相同的图,而不是更新的图形? This is my main problem and question. 这是我的主要问题。

from pylab import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from Tkinter import *

colors=[None]*10
for i in range(len(colors)):
    colors[i]=rand(5,5)
    #colors = ['red','green','blue','orange','brown','black','white','purple','violet']
numcol=len(colors)

class App(Frame):
    def __init__(self,parent=None):
        Frame.__init__(self,parent)
        self.top=Frame()
        self.top.grid()
        self.top.update_idletasks()

        self.makeWidgets()
        self.makeToolbar()

    def makeWidgets(self):
        # figsize (w,h tuple in inches) dpi (dots per inch)
        #f = Figure(figsize=(5,4), dpi=100)
        self.f = Figure()
        self.a = self.f.add_subplot(111)
        self.a.pcolor(rand(5,5))
        # a tk.DrawingArea
        self.canvas = FigureCanvasTkAgg(self.f, master=self.top)
        self.canvas.get_tk_widget().grid(row=3,column=0,columnspan=3)
        self.bClose = Button(self.top, text='Close',command=self.top.destroy)
        self.bClose.grid()
        #self.label = Label(self.top, text = 'Text',bg='orange')
        #self.label.grid()
        # initialize (time index t)
        self.t=0

    def makeToolbar(self):
        self.toolbar_text = ['Play','Pause','Stop']
        self.toolbar_length = len(self.toolbar_text)
        self.toolbar_buttons = [None] * self.toolbar_length

        for toolbar_index in range(self.toolbar_length):
            text = self.toolbar_text[toolbar_index]
            bg = 'yellow'
            button_id = Button(self.top,text=text,background=bg)
            button_id.grid(row=0, column=toolbar_index)
            self.toolbar_buttons[toolbar_index] = button_id

            def toolbar_button_handler(event, self=self, button=toolbar_index):
                return self.service_toolbar(button)

            button_id.bind("<Button-1>", toolbar_button_handler)

    # call blink() if start and set stop when stop            
    def service_toolbar(self, toolbar_index):
        if toolbar_index == 0:
            self.stop = False
            print self.stop
            self.blink()
        elif toolbar_index == 1:
            self.stop = True
            print self.stop
        elif toolbar_index == 2:
            self.stop = True
            print self.stop
            self.t=0

    # while in start, check if stop is clicked, if not, call blink recursivly
    def blink(self):
        if not self.stop:
            print 'looping',self.stop
            self.a.pcolor(colors[self.t])
            #draw()
            #self.label.configure(bg=colors[self.t])
            self.t += 1
            if self.t == numcol: # push stop button
                self.service_toolbar(2)
            self.canvas.get_tk_widget().update_idletasks()
            #self.label.update_idletasks()
            self.after(500, self.blink)

#root = Tk()
app=App()
app.mainloop()

Thanks for the help! 谢谢您的帮助!

In your blink function, add a self.canvas.show() before calling idle tasks: 在您的眨眼函数中,在调用空闲任务之前添加一个self.canvas.show()

self.canvas.show() # insert this line
self.canvas.get_tk_widget().update_idletasks()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM