Ошибка Tcl_AsyncDelete при отображении графиков обновления в режиме реального времени - PullRequest
0 голосов
/ 04 декабря 2018

Я хочу отображать график, который обновляется каждую секунду (наряду с другими вещами) в окне Tkinter.Мне просто нужно получить строку из матрицы данных и построить ее, затем перейти к следующей строке и т. Д.

Поскольку мне нужна кнопка Пуск / Стоп, я использую threading.

Для этого я следовал этому посту , который в основном делает то, что мне нужно.

Однако через некоторое время Python падает, и Spyder отображает эту ошибку:

An error occurred while starting the kernel
Tcl_AsyncDelete: async handler deleted by the wrong thread

Я пытался прочитать об этом, но я не нашел решения или объяснения этому.

Вот пример кода:

import tkinter as tk
import numpy as np

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading

continuePlotting = False
line = 0
data = np.random.rand(100, 500)

def change_state():
    global continuePlotting
    if continuePlotting == True:
        continuePlotting = False
    else:
        continuePlotting = True

def data_points():
    global line
    global data

    l = line % len(data) - 1
    r = data[l]

    line = line+1

    return r

def app():
    root = tk.Tk()
    root.configure(background='white')
    # First Plot
    top = tk.Frame(root)
    top.pack(fill='both')

    fig = Figure()
    ax = fig.add_subplot(111)

    graph = FigureCanvasTkAgg(fig, master=top)
    graph.get_tk_widget().pack(fill='both')

    def plotter():
        while continuePlotting:
            ax.cla()
            dpts = data_points()
            y = dpts[0:-1]

            x = np.linspace(0,len(y),len(y))

            ax.plot(x, y)
            ax.grid(True)

            graph.draw()

            time.sleep(1)

    def gui_handler():
        change_state()
        threading.Thread(target=plotter).start()

    b = tk.Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
    b.pack()

    root.mainloop()

if __name__ == '__main__':
    app()

Любая помощь будетвысоко ценится

1 Ответ

0 голосов
/ 06 декабря 2018

Основная проблема заключается в том, что вы вызываете функции Tk из потока без графического интерфейса.Не делай этого.Tk не предназначен для вызова из случайных потоков.Общее решение описывается как ответ на вопрос о связи по ниткам tkinter на этом сайте.Короче говоря, поместите свои вычисленные данные в очередь и вызовите событие Tk, чтобы поток пользовательского интерфейса узнал, что готово больше данных.Затем обработчик событий может извлечь новое значение из очереди и выполнить с ним пользовательский интерфейс.

В приложении приведена измененная версия вашего сценария с использованием этого механизма.

import tkinter as tk
import numpy as np

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
from queue import Queue

DATA_READY_EVENT = '<<DataReadyEvent>>'

continuePlotting = False
line = 0
data = np.random.rand(100, 500)

def change_state():
    global continuePlotting
    if continuePlotting == True:
        continuePlotting = False
    else:
        continuePlotting = True

def data_points():
    global line
    global data

    l = line % len(data) - 1
    r = data[l]

    line = line+1

    return r

def app():
    root = tk.Tk()
    root.configure(background='white')
    queue = Queue()
    # First Plot
    top = tk.Frame(root)
    top.pack(fill='both')

    fig = Figure()
    ax = fig.add_subplot(111)

    graph = FigureCanvasTkAgg(fig, master=top)
    graph.get_tk_widget().pack(fill='both')

    def plot(ev):
        x,y = queue.get()
        ax.plot(x, y)
        ax.grid(True)
        graph.draw()

    def plotter():
        global continuePlotting
        while continuePlotting:
            ax.cla()
            dpts = data_points()
            y = dpts[0:-1]
            x = np.linspace(0,len(y),len(y))
            queue.put((x,y))
            graph.get_tk_widget().event_generate(DATA_READY_EVENT)
            time.sleep(1)

    def gui_handler():
        change_state()
        threading.Thread(target=plotter).start()

    graph.get_tk_widget().bind(DATA_READY_EVENT, plot)
    b = tk.Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
    b.pack()

    root.mainloop()

if __name__ == '__main__':
    app()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...