不断更新 tkinter 中的图形

Posted

技术标签:

【中文标题】不断更新 tkinter 中的图形【英文标题】:continuously updating graph in tkinter 【发布时间】:2017-12-25 16:01:40 【问题描述】:

我有以下 python 代码(在 PyCharm 中),用于从 Arduino 板上读取读数。读数本身很好。我的代码的 tkinter 部分有以下两个问题:

    代码开始从 Arduino 读取值 已启动,而我想在单击按钮时启动它 ('read_data');只要我不按“read_data”按钮, 不显示图表,但会读取读数;我可以看到,当 我在开始运行代码几秒钟后打开图表; 当我关闭绘图close_plot时,图形窗口确实是 已关闭,不久后将重新打开。

我认为问题在于Top.after,因为它在mainloop() 内不断运行,因此read_data 函数不断更新。我该如何解决这个问题?

import serial
from tkinter import *
from matplotlib import pyplot as plt

Top = Tk()

ser = serial.Serial('COM3', baudrate=9600, timeout=1)

x = []
y = []

def read_data():
    plt.ion()
    new_value = ser.readline().decode('ascii')
    if new_value == '':
        pass
    else:
        y.append(eval(new_value[:-2]))
        x.append(len(y) - 1)
        plt.plot(x, y, 'r-')
        plt.show()
        plt.pause(0.0001)
        Top.after(100, read_data)

def close_plot():
    plt.close()
    global x, y
    x = []
    y = []

def quit():
    Top.destroy()

Button(Top, text='Read', command=read_data).pack()
Button(Top, text='Close plot', command=close_plot).pack()
Button(Top, text='Quit', command=quit).pack()

Top.after(100, read_data)
mainloop()

编辑:按下read_data 按钮时,我收到以下警告:

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\backend_bases.py:2445: MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented warnings.warn(str, mplDeprecation)

【问题讨论】:

1.在 mainloop() 之前删除 Top.after(100, read_data) - 它在程序启动后 100 毫秒开始读取。 顺便说一句:阅读PEP 8 -- Style Guide for Python Code。我们只对ButtonTkSerial 等类名使用“大写名称”,但对Top 等变量不使用。它使代码更具可读性。就连*** 都知道这个规则,并且在课堂上使用浅蓝色。 @furas:恐怕你的建议没有帮助,代码一运行就开始阅读 您是否删除了正确的after() - 在mainloop() 之前。如果您仍然有问题,那么也许您使用的代码与您在问题中显示的代码不同 - 即。你可能在command=read_data()中有() 尝试在没有 PyCharm 的情况下运行它 - 也许 PyCharm 存在一些问题。或者问题出在 Anaconda - 我仅使用标准 Python 安装进行了测试,没有收到错误 "MatplotlibDeprecationWarning" 【参考方案1】:

首先,删除该行:

Top.after(100, read_data)

mainloop() 之前立即出现,就像furas 建议的那样。

然后添加 after_cancel 方法以停止每 100 毫秒调用一次 read_data,但要使其正常工作,我们需要先将我们在方法内部使用的 after 分配给全局变量:

func_id = Top.after(100, read_data)

然后最后在close_plot中调用after_cancel

Top.after_cancel(func_id)

你的代码应该和下面完全一样:

import serial
from tkinter import *
from matplotlib import pyplot as plt

Top = Tk()

ser = serial.Serial('COM3', baudrate=9600, timeout=1)

x = []
y = []
func_id = None

def read_data():
    global func_id
    plt.ion()
    new_value = ser.readline().decode('ascii')
    if new_value == '':
        pass
    else:
        y.append(eval(new_value[:-2]))
        x.append(len(y) - 1)
        plt.plot(x, y, 'r-')
        plt.show()
        plt.pause(0.0001)
    func_id = Top.after(100, read_data)

def close_plot():
    global func_id
    #to no longer update the plot
    Top.after_cancel(func_id)
    plt.close()
    global x, y
    del x[:]
    del y[:]

def quit():
    Top.destroy()

Button(Top, text='Read', command=read_data).pack()
Button(Top, text='Close plot', command=close_plot).pack()
Button(Top, text='Quit', command=quit).pack()

mainloop()

【讨论】:

我会将after() 放在if/else 之外,这样它就可以一直读取 - 但我无法对其进行测试。 顺便说一句:我尝试在 Linux 上使用socat 来测试它以创建假串行端口 - 就像在回答 ***.com/a/23255001/1832058 中一样 感谢您的帮助,但即使我复制了您的代码,我也无法正常工作。错误重复出现。此外,我收到以下警告: MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented warnings.warn(str, mplDeprecation) @DenGor 请附加导致该错误的 MCVE 代码片段,并在您的问题中发布完整的错误消息。 @DenGor 在这里更新了答案,现在应该可以了。

以上是关于不断更新 tkinter 中的图形的主要内容,如果未能解决你的问题,请参考以下文章

将matplotlib嵌入到tkinter应用程序中

使用带有 pylab/matplotlib 嵌入的 Tkinter 播放、暂停、停止功能的彩色绘图动画:无法更新图形/画布?

python的Tkinter图形界面,怎样将输入控件Entry中的内容保存到一个变量中

python--tkinter(图形开发界面)

python tkinter Label 图形

Tkinter 嵌入图形子进程