Правильно выключая Tkinter, который использует после вызова функции - PullRequest
0 голосов
/ 22 октября 2019

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

В цикле используется функция Tkinters after, которая вызывает определенную функцию через n миллисекунд. У меня такое ощущение, что при вызове уничтожения или просто закрытии окна этот цикл останавливается? Причинение некоторого внутреннего процесса неудовлетворительным.

Я разместил код ниже, так как вы пропустите абстрактный класс. Удаление (Updater) и супер в init удовлетворит его, так как абстрактный класс больше похож на интерфейс.

Воспроизведение проблемы: Во время выполнениявремя выполнения сценария, если окно Tkinter закрыто любым доступным способом, прежде чем выводить все данные из очереди на график. Скрипт никогда не возвращается в командную строку и навсегда застрял. Эта ситуация неблагоприятна, так как я могу выйти из окна и ожидать, что процесс будет уничтожен.

Дальнейшее отслеживание: При неудачной передаче oscy.run () программа быстро завершает работу, но не возвращается в командную строку;как видно из запуска и быстрого выхода из программы до завершения. Это начинает намекать на то, что происходит в init ?

from __future__ import division

import logging

import atexit
import matplotlib
import numpy as np
matplotlib.use('TkAgg')

from functools import wraps
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from Tkinter import Scale, Button, Tk, TOP, BOTTOM, BOTH, HORIZONTAL


class Oscilloscope():
    """ Displays the Oscilloscope provided the data streaming in. """
    def __init__(
        self, 
        data_queue,
        closed_callback,
        title="DRP",
        xlabel="Range Cells",
        ylabel="Magnitude"
    ):
        """
        Initialization function for the Osc oscilloscope.
        :param data_queue: List data representative of magnitudes per update.
        :type data_queue: Queue
        :param closed_callback: When the figure is closed, callback should be used to remove the figure.
        :type closed_callback: Function
        :param title: Title of the plot being drawn for the Oscilloscope.
        :type title: String
        :param xlabel: X-axis of the plot being drawn, should either be Range or Doppler.
        :type xlabel: String
        :param ylabel: Y-axis of the plot being drawn, should always be Magnitude.
        :type ylabel: String
        """

        self.data_queue = data_queue
        self.closed_callback = closed_callback

        self.window = Tk()
        atexit.register(self.closed_callback)

        self.title = title
        self.xlabel = xlabel
        self.ylabel = ylabel

        self.y_limits = np.array([0, np.finfo(np.float).eps])      

    def _adjust_ylim_if_req(self, magnitude):
        """
        Changes the limits based on magnitudes. 
        :param magnitude: Size of the magnitude being plotted.
        :type magnitude: Float
        """
        if magnitude < self.y_limits[0]:
            self.y_limits[0] = magnitude
        elif magnitude > self.y_limits[1]:
            self.y_limits[1] = magnitude
        self.ax.set_ylim(self.y_limits[0], self.y_limits[1])

    def draw(self):
        """
        Draws the main line plot.
        """ 
        try:
            magnitudes = self.data_queue.get_nowait()
        except:
            pass
        else:
            # Adjust y limits
            for magnitude in magnitudes:
                self._adjust_ylim_if_req(magnitude)
            # Plot the graph
            self.ax.cla()
            self.ax.set_title(self.title, fontdict={'fontsize': 16, 'fontweight': 'medium'})
            self.ax.set_xlabel(self.xlabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
            self.ax.set_ylabel(self.ylabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
            self.ax.plot([n for n in range(len(magnitudes))], magnitudes, '-bo')

    def run(self):
        """
        Sets up and runs the main logic of the Window
        """
        self.plot()
        self.updateplot()
        self.window.mainloop()

    def plot(self):
        """
        Creates the initial base plot
        """
        figure = matplotlib.figure.Figure()
        self.ax = figure.add_subplot(1,1,1)
        self.ax.set_title(self.title, fontdict={'fontsize': 16, 'fontweight': 'medium'})
        self.ax.set_xlabel(self.xlabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
        self.ax.set_ylabel(self.ylabel, fontdict={'fontsize': 12, 'fontweight': 'medium'})
        self.canvas = FigureCanvasTkAgg(figure, master=self.window)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)

        self.draw()

        def close_fig():
            self.window.destroy()
            self.closed_callback
        button = Button(self.window, text='Close', command=close_fig)
        button.pack()

    def updateplot(self):
        """
        Updates the plot by gathering more data from the Queue.
        """
        print('Start')
        self.draw()
        self.canvas.draw()
        self.window.after(1, self.updateplot)
        print('End')

if __name__ == '__main__':
    from threading import Thread
    from multiprocessing import Queue
    q = Queue()
    for i in xrange(100):
        l = []
        for i in xrange(1000):
            l.append(np.random.randint(0, 100))
        q.put(l)
    def cb():
        print('Closed')
    oscy = Oscilloscope(q, cb)
    oscy.run()

enter image description here

1 Ответ

0 голосов
/ 22 октября 2019

Это была очень странная проблема и никогда бы не пришла мне в голову.

Решение

Использование многопроцессорной обработки. Проблема была в проблеме, и чтобы исправить это, используйте обычную очередь.

Что происходит

Многопроцессорная очередь, кажется, передает свои данные через канал. Когда программа завершается и область действия main теряется, эти данные все еще хранятся в ожидании удаления. Это тогда вызывает «зависание», что ничего не происходит, но что-то происходит в фоновом режиме.

Как очистить многопроцессорную очередь в python

Вызов close () перед выполнением программы решает проблему. Поскольку многопроцессорный модуль сам по себе является основанным на процессах «многопоточным» интерфейсом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...