Фигура Matplotlib висит в потоке. Используется Thread.join () - PullRequest
2 голосов
/ 04 марта 2020

Рассмотрим следующий пример

from time import sleep
import matplotlib.pyplot as plt
from threading import Thread
import numpy as np

class Monitor(Thread):
    def __init__(self):
        Thread.__init__(self)

        _, self.ax = plt.subplots()
        self.data = []

    def add_point(self, pt):
        self.data.append(pt)

        self.ax.cla()
        self.ax.plot(self.data)
        plt.draw()


class Main_job(Thread):

    def __init__(self, monitor):
        Thread.__init__(self)

        self.monitor = monitor

        self.output = []

    def run(self):
        for i in range(20):
            print(i)
            pt = np.random.rand()
            self.output.append(pt)
            self.monitor.add_point(pt)
            sleep(1)


monitor = Monitor()
monitor.start()

main = Main_job(monitor)
main.start()

У меня есть поток Main_job, который выполняет длительную операцию, и я хочу отслеживать его время выполнения.

Поэтому я определил Thread для цифры matplotlib и еще один для моего процесса.

Код работает нормально

Предположим теперь, что я хочу что-то сделать с выводом потока Main_job перед закрытием программы (например, распечатать ее или сохранить в файл).

Я добавляю следующие строки в конце моего code

main.join()
print(main.output)

Однако это как-то мешает matplotlib, что, несмотря на то, что поток monitor вообще не изменяется, теперь зависает до завершения main.

Как мне дождаться окончания main до sh, но в то же время избегать зависания monitor?

РЕДАКТИРОВАТЬ 1 - Однопоточная (нерабочая) версия

По запросу FiddleStix приведена однопотоковая версия кода. Несмотря на то, что эта версия намного проще, в этой версии фигура зависает и отображается только в конце процесса

from time import sleep
import matplotlib.pyplot as plt
import numpy as np

_, ax = plt.subplots()

output = []

for i in range(20):
    print(i)
    pt = np.random.rand()
    output.append(pt)

    ax.cla()
    ax.plot(output)

    sleep(1)

Ситуацию можно немного улучшить, добавив plt.pause(0.01) после построения графика. Таким образом, однако, фигура отображается, но пользователь может взаимодействовать только в течение этих 0,01 с.

ПРИМЕЧАНИЕ. Я знаю, что проблему можно решить, заменив sleep(1) на plt.pause(1), но здесь sleep - это только прокси для длительной длительной операции в реальном коде, а не просто пауза .

Ответы [ 2 ]

0 голосов
/ 15 марта 2020

GUI mainl oop должен работать в основной ветке. Запустите вашу работу в отдельном потоке, чтобы она не блокировала GUI.

Простая анимация графика при изменении output достигается путем перерисовки с использованием matplotlib.pyplot. Нарисуйте в потоке.

Рассмотрите возможность использования Animation API для плавной анимации живого сюжета.

from time import sleep
import matplotlib.pyplot as plt
from threading import Thread
import numpy as np


_, ax = plt.subplots()

output = []

class Job(Thread):
    def run(self):
        for i in range(10):
            print(i)
            pt = np.random.rand()
            output.append(pt)
            sleep(1)
            ax.cla()
            ax.plot(output)
            plt.draw()

job = Job()
job.start()

plt.show()
0 голосов
/ 10 марта 2020

Я думаю, что вы хотите сделать поток Монитора потоком daemon ( docs ). Поток демона автоматически завершится, если основной поток остановится. Таким образом, вы можете запустить что-то в фоновом режиме без явной остановки второго потока.

Вы можете объявить поток как демон, просто добавив аргумент класса:

class Monitor(Thread):
    daemon = True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...