Есть ли способ отсоединить графики Matplotlib, чтобы вычисления могли продолжаться?

258

После этих инструкций в интерпретаторе Python открывается окно с графиком:

from matplotlib.pyplot import *
plot([1,2,3])
show()
# other code

К сожалению, я не знаю, как продолжать интерактивно исследовать фигуру, созданную в show()то время, когда программа выполняет дальнейшие вычисления.

Это вообще возможно? Иногда расчеты длинные, и было бы полезно, если бы они продолжались во время проверки промежуточных результатов.

meteore
источник
5
Не могу подтвердить, что выбранное решение из носкло в 16:52 работает. Для меня Draw не открывает окно для отображения графика, только блокировка шоу в конце отображает решение. Однако его ответ с 17:00 верен. Включение интерактивного режима через систему ion()устраняет проблему.
Х. Брандсмайер
если вы опытный программист, вы можете использовать, os.fork()но имейте в виду, что использование os.fork()может быть сложным, потому что вы создаете новый процесс путем копирования старого процесса.
Тревор Бойд Смит

Ответы:

214

использование matplotlib звонки, которые не будут блокировать:

Использование draw():

from matplotlib.pyplot import plot, draw, show
plot([1,2,3])
draw()
print 'continue computation'

# at the end call show to ensure window won't close.
show()

Использование интерактивного режима:

from matplotlib.pyplot import plot, ion, show
ion() # enables interactive mode
plot([1,2,3]) # result shows immediatelly (implicit draw())

print 'continue computation'

# at the end call show to ensure window won't close.
show()
nosklo
источник
28
С matplotlib 0.98.3 правильный импорт взят из сюжета импорта matplotlib.pyplot, draw, show
meteore
112
draw()у меня не работает, не открывается ни одно окно Однако использование show(block=False)вместо, draw()кажется, делает трюк в Matplotlib 1.1.
Румпель
4
@nosklo, ты видел? Вы сделали это в учебнике по питону
января
4
@noskolo, что если у меня есть несколько цифр, как построить график и показать рис. 1, продолжая фон? Мне бы хотелось, чтобы эта фигура была открыта до следующего генерирования фигуры, поэтому в конце у меня открыты все фигуры и код завершен. С вашим текущим решением, это заставляет меня ждать закрытия Fig1, а затем код продолжается. Спасибо!!
Physiker
9
draw()у меня тоже не сработало, только pause(0.001)
работало
133

Используйте ключевое слово «блок», чтобы переопределить поведение блокировки, например

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

продолжить ваш код.

январь
источник
17
но это немедленно закроет окно графика, не оставит его открытым.
LWZ
8
Да, это правда, если вы вызываете свой скрипт из командной строки. Если вы находитесь в оболочке Ipython, окно не будет закрыто.
января
1
проверьте ответ @Nico на хитрость, которая оставит окно открытым в общем случае.
Jan
2
Для меня это не закрывает окно сразу, только когда скрипт завершен (так что вы можете вручную заблокировать конец скрипта, если хотите, чтобы он оставался открытым).
Луатор
Да, неблокированные окна закроются при выходе из сценария . Вы можете (а) разрешить блокировку на последнем графике или (б) не выходить из сценария (возможно, запросить ввод: «нажмите <Enter> для выхода из графика» или что-то в этом роде).
Даниэль Голдфарб
29

Лучше всегда проверять библиотеку, которую вы используете, поддерживает ли она неблокирующее использование .

Но если вам нужно более общее решение, или если нет другого пути, вы можете запустить все, что блокирует отдельный процесс, используя multprocessingмодуль, включенный в python. Вычисление будет продолжено:

from multiprocessing import Process
from matplotlib.pyplot import plot, show

def plot_graph(*args):
    for data in args:
        plot(data)
    show()

p = Process(target=plot_graph, args=([1, 2, 3],))
p.start()

print 'yay'
print 'computation continues...'
print 'that rocks.'

print 'Now lets wait for the graph be closed to continue...:'
p.join()

То есть накладные расходы на запуск нового процесса, а иногда труднее отлаживать на сложных сценариях, так что я предпочел бы другое решение ( с помощью matplotlib«s неблокирующих вызовов API )

nosklo
источник
Спасибо! Поскольку у меня еще нет Python 2.6 в моей системе, я использовал threading.Thread вместо Process. Я заметил, что последующие операторы печати становятся невыносимо медленными (третья печать, я выпустил KeyboardInterrupt после 1 минуты ожидания). Это эффект использования многопоточности вместо многопоточности?
Метеор
@meteore: Да, многопоточность - отстой. Вы всегда можете получить многопроцессорность для Python <2.6 здесь: pyprocessing.berlios.de
nosklo
Это абсолютно отлично. У вас есть идея, почему операторы print не выполняются в Emacs (режим python), пока окно графика не будет закрыто?
Метеор
В Ubuntu 8.10 (Intrepid) пакет (для python <2.6) называется python-processing, и вы импортируете его с помощью «import processing»
meteore
1
Вы не пропустили if __name__ == '__main__':?
Wernight
25

Пытаться

import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.show(block=False)
# other code
# [...]

# Put
plt.show()
# at the very end of your script to make sure Python doesn't bail out
# before you finished examining.

show()Документация говорит:

В неинтерактивном режиме отобразите все фигуры и заблокируйте их, пока фигуры не будут закрыты; в интерактивном режиме это не действует, если рисунки не были созданы до перехода из неинтерактивного в интерактивный режим (не рекомендуется). В этом случае он отображает цифры, но не блокирует.

Один экспериментальный ключевой аргумент block может иметь значение True или False, чтобы переопределить поведение блокировки, описанное выше.

Нико Шлёмер
источник
почему бы не использовать draw (); [другой код]; шоу() ? Насколько я знаю, block = False устарела
Bogdan
Почему вы думаете, что это устарело? Я вижу это задокументировано здесь .
Нико Шлёмер
11

ВАЖНО : Просто чтобы прояснить что-то. Я предполагаю, что команды находятся внутри .pyскрипта и скрипт вызывается с помощью, например,python script.py из консоли.

Простой способ, который работает для меня:

  1. Используйте block = False внутри show: plt.show (block = False)
  2. Используйте другой show () в конце скрипта .py.

Пример script.py файла:

plt.imshow(*something*)                                                               
plt.colorbar()                                                                             
plt.xlabel("true ")                                                                   
plt.ylabel("predicted ")                                                              
plt.title(" the matrix")  

# Add block = False                                           
plt.show(block = False)

################################
# OTHER CALCULATIONS AND CODE HERE ! ! !
################################

# the next command is the last line of my script
plt.show()

seralouk
источник
8

В моем случае я хотел, чтобы несколько окон всплыли во время их вычисления. Для справки это так:

from matplotlib.pyplot import draw, figure, show
f1, f2 = figure(), figure()
af1 = f1.add_subplot(111)
af2 = f2.add_subplot(111)
af1.plot([1,2,3])
af2.plot([6,5,4])
draw() 
print 'continuing computation'
show()

PS. Очень полезное руководство по OO-интерфейсу matplotlib .

meteore
источник
6

Ну, у меня были большие проблемы с выяснением неблокирующих команд ... Но, наконец, мне удалось переработать пример " Cookbook / Matplotlib / Animations - Animating selected plot elements ", чтобы он работал с потоками ( и передавал данные между потоками либо через глобальные переменные или через многопроцессорныйPipe ) на Python 2.6.5 на Ubuntu 10.04.

Сценарий можно найти здесь: Animating_selected_plot_elements-thread.py - в противном случае вставьте ниже ( с меньшим количеством комментариев ) для справки:

import sys
import gtk, gobject
import matplotlib
matplotlib.use('GTKAgg')
import pylab as p
import numpy as nx 
import time

import threading 



ax = p.subplot(111)
canvas = ax.figure.canvas

# for profiling
tstart = time.time()

# create the initial line
x = nx.arange(0,2*nx.pi,0.01)
line, = ax.plot(x, nx.sin(x), animated=True)

# save the clean slate background -- everything but the animated line
# is drawn and saved in the pixel buffer background
background = canvas.copy_from_bbox(ax.bbox)


# just a plain global var to pass data (from main, to plot update thread)
global mypass

# http://docs.python.org/library/multiprocessing.html#pipes-and-queues
from multiprocessing import Pipe
global pipe1main, pipe1upd
pipe1main, pipe1upd = Pipe()


# the kind of processing we might want to do in a main() function,
# will now be done in a "main thread" - so it can run in
# parallel with gobject.idle_add(update_line)
def threadMainTest():
    global mypass
    global runthread
    global pipe1main

    print "tt"

    interncount = 1

    while runthread: 
        mypass += 1
        if mypass > 100: # start "speeding up" animation, only after 100 counts have passed
            interncount *= 1.03
        pipe1main.send(interncount)
        time.sleep(0.01)
    return


# main plot / GUI update
def update_line(*args):
    global mypass
    global t0
    global runthread
    global pipe1upd

    if not runthread:
        return False 

    if pipe1upd.poll(): # check first if there is anything to receive
        myinterncount = pipe1upd.recv()

    update_line.cnt = mypass

    # restore the clean slate background
    canvas.restore_region(background)
    # update the data
    line.set_ydata(nx.sin(x+(update_line.cnt+myinterncount)/10.0))
    # just draw the animated artist
    ax.draw_artist(line)
    # just redraw the axes rectangle
    canvas.blit(ax.bbox)

    if update_line.cnt>=500:
        # print the timing info and quit
        print 'FPS:' , update_line.cnt/(time.time()-tstart)

        runthread=0
        t0.join(1)   
        print "exiting"
        sys.exit(0)

    return True



global runthread

update_line.cnt = 0
mypass = 0

runthread=1

gobject.idle_add(update_line)

global t0
t0 = threading.Thread(target=threadMainTest)
t0.start() 

# start the graphics update thread
p.show()

print "out" # will never print - show() blocks indefinitely! 

Надеюсь, это поможет кому-то,
ура!

sdaau
источник
5

Во многих случаях удобнее сохранить изображение в виде файла .png на жестком диске. Вот почему:

Преимущества:

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

недостаток:

  • Единственное, о чем я могу думать, - это то, что вам придется пойти и найти папку и открыть изображение самостоятельно.
elgehelge
источник
Если вы пытаетесь создать много изображений, я от всего сердца согласен.
Фантабол
6
Png ничьи не являются интерактивными: \
обратный
5

Если вы работаете в консоли, то есть IPythonвы можете использовать, plt.show(block=False)как указано в других ответах. Но если вы ленивы, вы можете просто набрать:

plt.show(0)

Который будет таким же.

Антон Протопопов
источник
5

Мне также пришлось добавить plt.pause(0.001)в свой код, чтобы он действительно работал внутри цикла for (иначе он будет показывать только первый и последний график):

import matplotlib.pyplot as plt

plt.scatter([0], [1])
plt.draw()
plt.show(block=False)

for i in range(10):
    plt.scatter([i], [i+1])
    plt.draw()
    plt.pause(0.001)
Мартин Пека
источник
Это сработало для меня с matplotlib3 на macOS. Большой!
Джерри Ма
4

В моей системе show () не блокируется, хотя я хотел, чтобы скрипт ждал, пока пользователь не будет взаимодействовать с графиком (и собирает данные с помощью обратных вызовов 'pick_event'), прежде чем продолжить.

Чтобы заблокировать выполнение до закрытия окна графика, я использовал следующее:

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)

# set processing to continue when window closed
def onclose(event):
    fig.canvas.stop_event_loop()
fig.canvas.mpl_connect('close_event', onclose)

fig.show() # this call does not block on my system
fig.canvas.start_event_loop_default() # block here until window closed

# continue with further processing, perhaps using result from callbacks

Однако обратите внимание, что canvas.start_event_loop_default () выдает следующее предупреждение:

C:\Python26\lib\site-packages\matplotlib\backend_bases.py:2051: DeprecationWarning: Using default event loop until function specific to this GUI is implemented
  warnings.warn(str,DeprecationWarning)

хотя сценарий все еще выполняется.

Андрей
источник
Спасибо! Spyder импортирует -pylab при запуске, что обычно полезно, но означает, что show () не будет блокироваться при ioff () - это позволяет вам исправить это поведение!
проиграл
3

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

Это, вероятно, слишком нестандартно и не рекомендуется для производственного кода. В этом коде, вероятно, много скрытых ошибок.

from contextlib import contextmanager

@contextmanager
def keep_plots_open(keep_show_open_on_exit=True, even_when_error=True):
    '''
    To continue excecuting code when plt.show() is called
    and keep the plot on displaying before this contex manager exits
    (even if an error caused the exit).
    '''
    import matplotlib.pyplot
    show_original = matplotlib.pyplot.show
    def show_replacement(*args, **kwargs):
        kwargs['block'] = False
        show_original(*args, **kwargs)
    matplotlib.pyplot.show = show_replacement

    pylab_exists = True
    try:
        import pylab
    except ImportError: 
        pylab_exists = False
    if pylab_exists:
        pylab.show = show_replacement

    try:
        yield
    except Exception, err:
        if keep_show_open_on_exit and even_when_error:
            print "*********************************************"
            print "Error early edition while waiting for show():" 
            print "*********************************************"
            import traceback
            print traceback.format_exc()
            show_original()
            print "*********************************************"
            raise
    finally:
        matplotlib.pyplot.show = show_original
        if pylab_exists:
            pylab.show = show_original
    if keep_show_open_on_exit:
        show_original()

# ***********************
# Running example
# ***********************
import pylab as pl
import time
if __name__ == '__main__':
    with keep_plots_open():
        pl.figure('a')
        pl.plot([1,2,3], [4,5,6])     
        pl.plot([3,2,1], [4,5,6])
        pl.show()

        pl.figure('b')
        pl.plot([1,2,3], [4,5,6])
        pl.show()

        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        time.sleep(1)
        print '...'
        this_will_surely_cause_an_error

Если / когда я реализую правильное «держать графики открытыми (даже если возникает ошибка) и разрешать показ новых графиков», я бы хотел, чтобы скрипт корректно завершился, если никакое вмешательство пользователя не говорит об этом иначе (для целей пакетного выполнения).

Я могу использовать что-то вроде вопроса об истечении времени ожидания «Конец сценария! \ NНажмите p, если вы хотите приостановить вывод печати (у вас есть 5 секунд):» по адресу /programming/26704840/corner -cases-for-my-wait-for-user-прерывание-ввода-реализации .

Саймон Стрейхер
источник
2
plt.figure(1)
plt.imshow(your_first_image)

plt.figure(2)
plt.imshow(your_second_image)

plt.show(block=False) # That's important 

raw_input("Press ENTER to exist") # Useful when you run your Python script from the terminal and you want to hold the running to see your figures until you press Enter
thanhtang
источник
16
Как одним нажатием войти до существующего?
Гровина
2

ОП спрашивает об отстранении matplotlibучастков. Большинство ответов предполагают выполнение команды из интерпретатора Python. Вариант использования, представленный здесь, является моим предпочтением для тестирования кода в терминале (например, bash), где file.pyзапускается a, и вы хотите, чтобы график (ы) появлялся, а скрипт python завершался и возвращался в командную строку.

Этот автономный файл используется multiprocessingдля запуска отдельного процесса отображения данных с помощью matplotlib. Основной поток выходит с использованием os._exit(1)упомянутого в этом посте. В os._exit()силах основные для выхода , но оставляет matplotlibпроцесс ребенка живой и отзывчивой , пока окно участка не будет закрыто. Это отдельный процесс полностью.

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

multiprocessingпредназначен для выполнения кода только на Python, что делает его, возможно, лучше, чем subprocess. multiprocessingявляется кроссплатформенным, так что это должно хорошо работать в Windows или Mac без каких-либо настроек. Нет необходимости проверять основную операционную систему. Это было проверено на Linux, Ubuntu 18.04LTS.

#!/usr/bin/python3

import time
import multiprocessing
import os

def plot_graph(data):
    from matplotlib.pyplot import plot, draw, show
    print("entered plot_graph()")
    plot(data)
    show() # this will block and remain a viable process as long as the figure window is open
    print("exiting plot_graph() process")

if __name__ == "__main__":
    print("starting __main__")
    multiprocessing.Process(target=plot_graph, args=([1, 2, 3],)).start()
    time.sleep(5)
    print("exiting main")
    os._exit(0) # this exits immediately with no cleanup or buffer flushing

Запуск file.pyвызывает окно фигуры, затем __main__выходит, ноmultiprocessingmatplotlib окно с рисунком + реагирует на кнопки масштабирования, панорамирования и других операций, потому что это независимый процесс.

Проверьте процессы в командной строке bash:

ps ax|grep -v grep |grep file.py

Марк Компере
источник
Я пытался использовать ваше решение, но оно не работает для меня, и я пытаюсь выяснить, почему. Я не запускаю код через терминал, а из Pycharm IDE, если это имеет какое-то значение, хотя это не должно быть.
Цецм
1
Хорошо, что в конечном итоге мне помогло установить дочерний процесс с помощью .daemon=Falseописанного здесь stackoverflow.com/a/49607287/1476932 Однако, sys.exit()родительский процесс не был завершен, как описано там, пока я не закрыл дочернее окно. С другой стороны, использование os._exit(0)из приведенного выше примера сработало.
Цецм
1

На мой взгляд, ответы в этой теме предоставляют методы, которые не работают для каждой системы и в более сложных ситуациях, таких как анимация. Я предлагаю взглянуть на ответ MiKTeX в следующей ветке, где был найден надежный метод: как дождаться окончания анимации matplotlib?

MikeTeX
источник
0

Если вы хотите открыть несколько фигур, оставив их открытыми, этот код работает для меня:

show(block=False)
draw()
DomDev
источник
show (block = False) устарела и больше не работает
Богдан,
0

Не отвечая непосредственно на запрос OPs, я публикую этот обходной путь, поскольку он может помочь кому-то в этой ситуации:

  • Я создаю .exe с pyinstaller, так как я не могу установить python, где мне нужно генерировать графики, поэтому мне нужен скрипт python для генерации графика, сохранения его как .png, закрытия его и продолжения со следующим, реализованного в виде нескольких графиков в цикл или использование функции.

Для этого я использую:

import matplotlib.pyplot as plt
#code generating the plot in a loop or function
#saving the plot
plt.savefig(var+'_plot.png',bbox_inches='tight', dpi=250) 
#you can allways reopen the plot using
os.system(var+'_plot.png') # unfortunately .png allows no interaction.
#the following avoids plot blocking the execution while in non-interactive mode
plt.show(block=False) 
#and the following closes the plot while next iteration will generate new instance.
plt.close() 

Где «var» определяет график в цикле, поэтому он не будет перезаписан.

patowski1981
источник
-1

Используйте plt.show(block=False)и в конце вашего скрипта вызова plt.show().

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

Кен Мюллер
источник
Смотрите ответ @ nico-schlömer
Джош Вольф