Как запустить анимационный цикл в Tkinter для события? - PullRequest
0 голосов
/ 01 июня 2019

Я написал некоторый код с использованием Tkinter в Python 3, который строит график на холсте. Я также сделал так, чтобы при наведении мыши на холст график прокручивался влево.

Проблема в том, что я хочу, чтобы график прокручивался, когда я нажимаю пробел, например. Но я не хочу, чтобы он прокручивал 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 Ответ

1 голос
/ 01 июня 2019

Вы можете добавить метод к 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...