【问题标题】:Tkinter Matplotlib and ThreadTkinter Matplotlib 和线程
【发布时间】:2020-05-18 22:26:39
【问题描述】:

在主循环中,创建了一个 2 x 2 tkinter 网格 第一行的每个单元格中都有一个标签。 在第二行中,两个 Matplotlib 图形与子图一起装箱 两个函数负责动态刷新网格。 他们在一个线程中运行每一个。 第一行(两个标签)很好地被两个函数刷新。 但是,在第二行中什么都没有……没有情节!

from tkinter import *
import threading
import time
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import style

def read_api1():
    n = 0
    while 1:
        n = n + 1
        texte1.config(text="fig1 " + str(n))
        ax1.plot([1,2], [12,14])
        time.sleep(2)
        ax1.cla()

def read_api2():
    m = 0
    while 1:
        m = m + 1
        texte2.config(text="fig2 " + str(m))
        ax2.plot([.1,.2,.3], [2,4,3])
        time.sleep(1)

main = Tk()
style.use("ggplot")

texte1 = Label(main, text="fig1")
texte1.grid(row=0,column=0)
fig1 = Figure(figsize=(2, 2), dpi=112)
ax1 = fig1.add_subplot()
fig1.set_tight_layout(True)
graph = FigureCanvasTkAgg(fig1, master=main)
canvas = graph.get_tk_widget()
canvas.grid(row=1, column=0)

texte2 = Label(main, text="fig2")
texte2.grid(row=0,column=1)
fig2 = Figure(figsize=(2, 2), dpi=112)
ax2 = fig2.add_subplot()
fig2.set_tight_layout(True)
graph = FigureCanvasTkAgg(fig2, master=main)
canvas = graph.get_tk_widget()
canvas.grid(row=1, column=1)

t = threading.Thread(target=read_api1)
t.start()
t = threading.Thread(target=read_api2)
t.start()

main.mainloop()

任何帮助将不胜感激:)


编辑:

更多细节@furas:

  • read_api2 应该在特定时间从WEB API 获取数据。所以你推荐的(after 方法)应该有效。

  • read_api1 应该从串行端口 (GPIO UART) 获取数据。所以线程将等待可读取的数据。

那样的话,我看不出怎么用after方法

换句话说,问题是:如何在基于异步输入的 tkinter 环境中刷新 matplotlib 图?异步串行数据读取不能在主循环中,所以我把它放在线程中但即使使用graph.draw(),它也不起作用。有什么建议吗?

【问题讨论】:

  • 通常 GUI 不喜欢在线程中工作,只有主线程可以更新小部件。
  • 我在您的问题中添加了您的新信息。
  • 如果您有新信息,请添加到问题中,而不是添加到答案中。在开始时,您应该写下您将从 GPIO 读取数据——它会改变一切。或者现在您必须在新页面上创建新问题,因为这是新问题。
  • 现在你必须在线程中运行GPIO并使用队列向主线程发送数据,主线程必须使用after()定期检查Queue,如果有新数据则它应该显示新的情节。我想我很久以前在 Stackoverflow 上做过这样的事情,也许我在 examples on GitHub
  • 对于WEB API - 您可以计算(specific_time - current_time) 并将其转换为毫秒并使用after(millisecond, ...) 中的结果在特定时间运行函数。

标签: python multithreading matplotlib tkinter


【解决方案1】:

有两个问题:

  1. 需要graph.draw() 来更新/重绘绘图

  2. 通常 GUI 不喜欢在线程中运行,而且似乎 graph.draw() 在线程中不起作用(至少在我的 Linux 上)。

您可能必须使用main.after(1000, main_api1)1000ms (1s) 之后运行相同的功能,而不使用thread 并且不阻塞mainloop()


import tkinter as tk # PEP8: `import *` is not preferred
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import style
import random

# --- functions ---

def read_api1():
    global n

    n = n + 1
    texte1.config(text="fig1 " + str(n))

    ax1.cla()
    ax1.plot([1,2], [random.randint(0,10),random.randint(0,10)])
    graph1.draw()

    main.after(1000, read_api1)

def read_api2():
    global m

    m = m + 1
    texte2.config(text="fig2 " + str(m))

    ax2.cla()
    ax2.plot([.1,.2,.3], [random.randint(0,10),random.randint(0,10),random.randint(0,10)])
    graph2.draw()

    main.after(1000, read_api2)

# --- main ---

m = 0
n = 0

main = tk.Tk()
style.use("ggplot")

texte1 = tk.Label(main, text="fig1")
texte1.grid(row=0, column=0)

fig1 = Figure(figsize=(2, 2), dpi=112)
ax1 = fig1.add_subplot()
fig1.set_tight_layout(True)
graph1 = FigureCanvasTkAgg(fig1, master=main)
canvas1 = graph1.get_tk_widget()
canvas1.grid(row=1, column=0)
#graph1.draw()

texte2 = tk.Label(main, text="fig2")
texte2.grid(row=0,column=1)
fig2 = Figure(figsize=(2, 2), dpi=112)
ax2 = fig2.add_subplot()
fig2.set_tight_layout(True)
graph2 = FigureCanvasTkAgg(fig2, master=main)
canvas2 = graph2.get_tk_widget()
canvas2.grid(row=1, column=1)
#graph2.draw()

read_api1()
read_api2()

main.mainloop()

编辑: 运行两个线程的示例。每个线程以不同的速度生成数据并使用两个queues 将数据发送到主线程。并且主线程使用两个after()来检查两个队列并更新两个绘图。

import tkinter as tk # PEP8: `import *` is not preferred
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import style
import random

import threading
import queue
import time

# --- functions ---

def WEB_API(queue):
    # it will run in thread

    print('WEB_API: start')

    while web_api_running:
        value = random.randint(1, 3)
        time.sleep(.1)
        print('WEB_API:', value)
        queue.put(value)


def GPIO_API(queue):
    # it will run in thread

    print('GPIO_API: start')

    while gpio_api_running:
        value = random.randint(1, 3)
        time.sleep(value)
        print('GPIO_API:', value)
        queue.put(value)


def read_api1():
    global n
    global data1

    if not queue1.empty():
        value = queue1.get()

        # remove first item and add new item at the the end
        data1 = data1[1:] + [value]

        n += 1
        texte1.config(text="fig1 " + str(n))

        ax1.cla()
        ax1.plot(range(10), data1)
        graph1.draw()

    main.after(100, read_api1)

def read_api2():
    global m
    global data2

    if not queue2.empty():
        value = queue2.get()

        # remove first item and add new item at the the end
        data2 = data2[1:] + [value]

        m = m + 1
        texte2.config(text="fig2 " + str(m))

        ax2.cla()
        ax2.plot([.1,.2,.3], data2)
        graph2.draw()

    main.after(100, read_api2)

# --- before GUI ---

# default data at start (to add new value at the end and remove first value)
data1 = [0,0,0,0,0,0,0,0,0,0]
data2 = [0,0,0]

m = 0
n = 0

# queues to communicate with threads
queue1 = queue.Queue()
queue2 = queue.Queue()

# global variables to control loops in thread    
web_api_running = True 
gpio_api_running = True

# start threads and send queues as arguments
thread1 = threading.Thread(target=WEB_API,  args=(queue1,))
thread1.start()

thread2 = threading.Thread(target=GPIO_API, args=(queue2,))
thread2.start()

# --- GUI ---

main = tk.Tk()
style.use("ggplot")

texte1 = tk.Label(main, text="fig1")
texte1.grid(row=0, column=0)

fig1 = Figure(figsize=(2, 2), dpi=112)
ax1 = fig1.add_subplot()
fig1.set_tight_layout(True)
graph1 = FigureCanvasTkAgg(fig1, master=main)
canvas1 = graph1.get_tk_widget()
canvas1.grid(row=1, column=0)

texte2 = tk.Label(main, text="fig2")
texte2.grid(row=0,column=1)
fig2 = Figure(figsize=(2, 2), dpi=112)
ax2 = fig2.add_subplot()
fig2.set_tight_layout(True)
graph2 = FigureCanvasTkAgg(fig2, master=main)
canvas2 = graph2.get_tk_widget()
canvas2.grid(row=1, column=1)

# draw plots first time
ax1.plot(range(10),  data1)
ax2.plot([.1,.2,.3], data2)

# run after which will update data and redraw plots
read_api1()
read_api2()

main.mainloop()

# --- after GUI ---

# stop loops in threads
web_api_running = False
gpio_api_running = False

# wait for end of theads
thread1.join()
thread2.join()

【讨论】:

    猜你喜欢
    • 2014-12-29
    • 2014-07-30
    • 2013-10-14
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 1970-01-01
    相关资源
    最近更新 更多