如何在Tkinter中为事件启动动画循环?

时间:2019-06-01 13:50:21

标签: python animation plot tkinter

我在Python 3中使用Tkinter编写了一些代码,这些代码在画布上绘制图形。我还做到了这样,当我将鼠标移到画布上时,图形就会向左滚动。

问题是,例如,当我按下空格键时,我希望图形滚动。但是我不希望每次按空格键时都滚动1步,但是我希望它在按一次时无限期地开始滚动,而在再次按时停止其滚动。我希望空格键是播放/暂停键。

我该如何完成?我不想在任何地方使用matplotlib。

现在就输入我的密码:

from tkinter import *
import numpy as np

# The function of the graph
def f(x):
    return np.sin(x)+np.sin(3*x-1)+np.sin(0.5*(x+np.pi))+0.3*np.sin(10*x)

class GraphPlot():
    def __init__(self, master):
        self.master = master

        # Data for the graph and steps to move to the right
        self.data_x = np.linspace(0, 4*np.pi, 1000)
        self.data_y = f(self.data_x)
        self.step = 0.1

        # A switch to delete to clear the canvas each iteration before plotting the next frame
        self.gate = False

        # Setting the Tkinter window and the canvas in place
        self.ws = master.winfo_screenwidth()
        self.hs = master.winfo_screenheight()
        ww = self.ws*0.75
        hw = self.hs*0.50
        self.canvas = Canvas(self.master, width = ww, height = hw, bg = 'black')
        self.canvas.grid()
        self.master.update()
        self.w = self.canvas.winfo_width()
        self.h = self.canvas.winfo_height()
        self.canvas.focus_set()

        # Plot first frame
        self.drawData(self.data_x, self.data_y)

        # Plot next frames each time I press the space bar
        self.canvas.bind('<KeyPress-space>', self.updateData)

    def drawData(self, data_x, data_y):
        '''This is my function to plot a grpah in a canvas
        canvas without embedding any matplotlib figure'''

        # Setting the axis limits
        x_min, x_max = min(data_x), max(data_x)
        y_min, y_max = min(data_y), max(data_y)

        # Translating data to pixel positions inside the canvas
        pixel_x = (data_x-x_min)*self.w/(x_max-x_min)
        pixel_y = -(data_y-y_max)*self.h/(y_max-y_min)
        points = []
        for i in range(len(data_x)):
            points.append(pixel_x[i])
            points.append(pixel_y[i])
        points = tuple(points)

        # Deleting previous frame before plotting the next frame (except for the first frame)
        if self.gate:
            self.canvas.delete('curve')
        else:
            self.gate = True

        # Plotting
        self.canvas.create_line(points, fill = 'white', tag = 'curve')

    def updateData(self, event):
        # Changing data for the next frame
        self.data_x += self.step
        self.data_y = f(self.data_x)

        # Plot new frame
        self.drawData(self.data_x, self.data_y)

root = Tk()
GraphPlot(root)
root.mainloop()

我尝试了一些想法。例如,我使用了一个新函数PlayPause(),它带有一个while循环和一个新的开关self.go,但这并没有按预期工作。

我期望工作的代码却没有:

from tkinter import *
import numpy as np

def f(x):
    return np.sin(x)+np.sin(3*x-1)+np.sin(0.5*(x+np.pi))+0.3*np.sin(10*x)

class GraphPlot():
    def __init__(self, master):
        self.master = master
        self.data_x = np.linspace(0, 4*np.pi, 1000)
        self.data_y = f(self.data_x)
        self.step = 0.1
        self.go = False # The new switch
        self.gate = False
        self.ws = master.winfo_screenwidth()
        self.hs = master.winfo_screenheight()
        ww = self.ws*0.75
        hw = self.hs*0.50
        self.canvas = Canvas(self.master, width = ww, height = hw, bg = 'black')
        self.canvas.grid()
        self.master.update()
        self.w = self.canvas.winfo_width()
        self.h = self.canvas.winfo_height()
        self.canvas.focus_set()
        self.drawData(self.data_x, self.data_y)
        self.canvas.bind('<KeyPress-space>', self.PlayPause)

    def drawData(self, data_x, data_y):
        x_min, x_max = min(data_x), max(data_x)
        y_min, y_max = min(data_y), max(data_y)
        pixel_x = (data_x-x_min)*self.w/(x_max-x_min)
        pixel_y = -(data_y-y_max)*self.h/(y_max-y_min)
        points = []
        for i in range(len(data_x)):
            points.append(pixel_x[i])
            points.append(pixel_y[i])
        points = tuple(points)
        if self.gate:
            self.canvas.delete('curve')
        else:
            self.gate = True
        self.canvas.create_line(points, fill = 'white', tag = 'curve')

    def updateData(self):
        self.data_x += self.step
        self.data_y = f(self.data_x)
        self.drawData(self.data_x, self.data_y)

    def PlayPause(self, event):
        if self.go:
            self.go = False
        else:
            self.go = True
        while self.go:
            self.updateData()

root = Tk()
GraphPlot(root)
root.mainloop()

1 个答案:

答案 0 :(得分:1)

您可以向toggle_play_pause添加一个方法,并将空格键绑定到该方法。按下空格键时,此方法会切换布尔标志pause,该标志在关闭时将允许调用update

update将继续每10/1000秒调用一次,直到再次按下空格键并将pause标志设置为True。

import tkinter as tk
import numpy as np

def f(x):
    return np.sin(x)+np.sin(3*x-1)+np.sin(0.5*(x+np.pi))+0.3*np.sin(10*x)

class GraphPlot():
    def __init__(self, master):
        self.master = master

        # Data for the graph and steps to move to the right
        self.data_x = np.linspace(0, 4*np.pi, 1000)
        self.data_y = f(self.data_x)
        self.step = 0.1

        # A switch to delete to clear the canvas each iteration before plotting the next frame
        self.gate = False

        # Setting the Tkinter window and the canvas in place
        self.ws = master.winfo_screenwidth()
        self.hs = master.winfo_screenheight()
        ww = self.ws * 0.75
        hw = self.hs * 0.50
        self.canvas = tk.Canvas(self.master, width=ww, height=hw, bg='black')
        self.canvas.grid()
        self.master.update()
        self.w = self.canvas.winfo_width()
        self.h = self.canvas.winfo_height()
        self.canvas.focus_set()

        # Plot first frame
        self.drawData(self.data_x, self.data_y)

        # Plot next frames each time I press the space bar
        self.canvas.bind('<KeyPress-space>', self.toggle_play_pause)

        self.pause = True
        self._update_call_handle = None

    def drawData(self, data_x, data_y):
        '''This is my function to plot a grpah in a canvas
        canvas without embedding any matplotlib figure'''

        # Setting the axis limits
        x_min, x_max = min(data_x), max(data_x)
        y_min, y_max = min(data_y), max(data_y)

        # Translating data to pixel positions inside the canvas
        pixel_x = (data_x-x_min)*self.w/(x_max-x_min)
        pixel_y = -(data_y-y_max)*self.h/(y_max-y_min)
        points = []
        for i in range(len(data_x)):
            points.append(pixel_x[i])
            points.append(pixel_y[i])
        points = tuple(points)

        # Deleting previous frame before plotting the next frame (except for the first frame)
        if self.gate:
            self.canvas.delete('curve')
        else:
            self.gate = True

        # Plotting
        self.canvas.create_line(points, fill = 'white', tag = 'curve')

    def toggle_play_pause(self, dummy_event):
        self.pause = not self.pause
        if not self.pause:
            self.updateData()

    def updateData(self):
        # Changing data for the next frame
        self.data_x += self.step
        self.data_y = f(self.data_x)

        # Plot new frame
        self.drawData(self.data_x, self.data_y)
        if not self.pause:
            self._update_call_handle = root.after(10, self.updateData)
        else:
            root.after_cancel(self._update_call_handle)
            self._update_call_handle = None

root = tk.Tk()
GraphPlot(root)
root.mainloop()