通过按钮推送在tkinter GUI中放置动画图形?

时间:2017-05-05 18:57:37

标签: python user-interface matplotlib tkinter

我想要一个"开始按钮"开始将数据收集到图表中。无论是否存在空白图表,都需要在GUI中。

此代码给我一个错误

(_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack)

当我没有按钮和单独的" def事件"时,它会运行并创建两个窗口。

我尝试的其他代码在按下按钮时在单独的窗口中创建图形。我是否遗漏了一些相当简单的东西,使图表自动进入GUI而不是单独的窗口?

from tkinter import *  #import everything from tkinter
import tkinter as tk
from PIL import ImageTk, Image
import time
import serial
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import numpy as np

fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
root = Tk()

root.configure(background='gray26')
topFrame = tk.Frame(root)  #insvisible rectangle that you can put stuff in (widgets).  This goes in the main window (root)
topFrame.pack()  #pack actually displays it in the window
middleFrame = Frame(root)
middleFrame.pack()


ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.stopbits = 2

def event():

    def animate(i):

        f = open('serial.txt', 'w+')
        data=ser.readline()
        print(data, file = f)

        f.close()
        lines = open('serial.txt', 'r+')
        read_serial=lines.readline()
        mylist = [int(x) for x in read_serial.split(',') if x.strip().isdigit()]
        x = np.linspace(340, 850, num=len(mylist))
        ax1.clear()
        ax1.plot(x, mylist)
        plt.ylim([0, 1000])

   canvas = FigureCanvasTkAgg(fig, master=root)
    canvas.get_tk_widget().grid(column=0, row=1)
    ani = animation.FuncAnimation(fig, animate)
    plt.show()

startbutton = Button(middleFrame, text="Start Graph", command=event)
startbutton.pack()


bottomFrame = Frame(root)
bottomFrame.pack() 
time1 = ' '
clock = Label(topFrame, font=('times', 20, 'bold'), fg = 'snow', bg='gray26')
clock.pack(side="left")

def tick():

    global time1
# get the current local time from the PC
    time2 = time.strftime('%H:%M:%S')

    if time2 != time1:
        time1 = time2
        clock.config(text=time2)


    clock.after(200,tick)

tick()



root.mainloop()

以下是单击按钮运行的代码!

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk
from PIL import ImageTk, Image
import time
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import numpy as np

import random

fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.stopbits = 2



def get_data():
    while True:

        f = open('serial.txt', 'w+')
        data=ser.readline()
        print(data, file = f)
        f.close() 
        lines = open('serial.txt', 'r+')
        read_serial=lines.readline()
        mylist = [int(x) for x in read_serial.split(',') if         x.strip().isdigit()]
        x = np.linspace(340, 850, num=len(mylist))
        ax1.clear()
        ax1.plot(x, mylist)
        plt.ylim([0, 1000])
        return x, mylist

class App(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        self.running = False
        self.ani = None

        btns = tk.Frame(self)
        btns.pack()

        lbl = tk.Label(btns, text="Number of times to run")
        lbl.pack(side=tk.LEFT)

        self.points_ent = tk.Entry(btns, width=5)
        self.points_ent.insert(0, '50')
        self.points_ent.pack(side=tk.LEFT)

        lbl = tk.Label(btns, text="update interval (ms)")
        lbl.pack(side=tk.LEFT)

        self.interval = tk.Entry(btns, width=5)
        self.interval.insert(0, '100')
        self.interval.pack(side=tk.LEFT)

self.btn = tk.Button(btns, text='Start', command=self.on_click)
        self.btn.pack(side=tk.LEFT)

        self.fig = plt.Figure()
        self.ax1 = self.fig.add_subplot(111)
        self.line, = self.ax1.plot([], [], lw=2)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas.show()
        self.canvas.get_tk_widget().pack()

        self.ax1.set_ylim(0,1000)
        self.ax1.set_xlim(340,850)

    def on_click(self):

        if self.ani is None:

           return self.start()

        if self.running:

            self.ani.event_source.stop()
            self.btn.config(text='Un-Pause')
        else:

            self.ani.event_source.start()
            self.btn.config(text='Pause')
        self.running = not self.running

    def start(self):
        self.points = int(self.points_ent.get()) + 1
        self.ani = animation.FuncAnimation(
            self.fig,
            self.update_graph,
            frames=self.points,
            interval=int(self.interval.get()),
            repeat=False)
        self.running = True
        self.btn.config(text='Pause')
        self.ani._start()
        print('started animation')

    def update_graph(self, i):
        self.line.set_data(*get_data()) # update graph

        if i >= self.points - 1:

            self.btn.config(text='Start')
            self.running = False
            self.ani = None
        return self.line,

def main():
    root = tk.Tk()
    app = App(root)
    app.pack()
    root.mainloop()

if __name__ == '__main__':
    main()

1 个答案:

答案 0 :(得分:1)

这是我之前处理过的棘手问题。这是一个模板,其中包含一些未记录的功能,可帮助您入门。

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation

import random

def get_data():
    '''replace this function with whatever you want to provide the data
    for now, we just return soem random data'''
    rand_x = list(range(100))
    rand_y = [random.randrange(100) for _ in range(100)]
    return rand_x, rand_y

class App(tk.Frame):
    def __init__(self, master=None, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

        self.running = False
        self.ani = None

        btns = tk.Frame(self)
        btns.pack()

        lbl = tk.Label(btns, text="Number of times to run")
        lbl.pack(side=tk.LEFT)

        self.points_ent = tk.Entry(btns, width=5)
        self.points_ent.insert(0, '50')
        self.points_ent.pack(side=tk.LEFT)

        lbl = tk.Label(btns, text="update interval (ms)")
        lbl.pack(side=tk.LEFT)

        self.interval = tk.Entry(btns, width=5)
        self.interval.insert(0, '100')
        self.interval.pack(side=tk.LEFT)

        self.btn = tk.Button(btns, text='Start', command=self.on_click)
        self.btn.pack(side=tk.LEFT)

        self.fig = plt.Figure()
        self.ax1 = self.fig.add_subplot(111)
        self.line, = self.ax1.plot([], [], lw=2)
        self.canvas = FigureCanvasTkAgg(self.fig, master=self)
        self.canvas.show()
        self.canvas.get_tk_widget().pack()

        self.ax1.set_ylim(0,100)
        self.ax1.set_xlim(0,100)

    def on_click(self):
        '''the button is a start, pause and unpause button all in one
        this method sorts out which of those actions to take'''
        if self.ani is None:
            # animation is not running; start it
            return self.start()

        if self.running:
            # animation is running; pause it
            self.ani.event_source.stop()
            self.btn.config(text='Un-Pause')
        else:
            # animation is paused; unpause it
            self.ani.event_source.start()
            self.btn.config(text='Pause')
        self.running = not self.running

    def start(self):
        self.points = int(self.points_ent.get()) + 1
        self.ani = animation.FuncAnimation(
            self.fig,
            self.update_graph,
            frames=self.points,
            interval=int(self.interval.get()),
            repeat=False)
        self.running = True
        self.btn.config(text='Pause')
        self.ani._start()
        print('started animation')

    def update_graph(self, i):
        self.line.set_data(*get_data()) # update graph

        if i >= self.points - 1:
            # code to limit the number of run times; could be left out
            self.btn.config(text='Start')
            self.running = False
            self.ani = None
        return self.line,

def main():
    root = tk.Tk()
    app = App(root)
    app.pack()
    root.mainloop()

if __name__ == '__main__':
    main()