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

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

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

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

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


person meteore    schedule 19.01.2009    source источник
comment
Не могу подтвердить, что выбранное решение из носкло на 16:52 работает. Для меня draw не открывает окно для отображения графика, только блокирующее шоу в конце отображает решение. Однако его ответ с 17:00 верен. Включение интерактивного режима с помощью ion() устраняет проблему.   -  person H. Brandsmeier    schedule 25.09.2011
comment
если вы опытный программист, вы можете использовать os.fork(), но имейте в виду, что использование os.fork() может быть непростым, потому что вы создаете новый процесс, копируя старый.   -  person Trevor Boyd Smith    schedule 09.08.2019


Ответы (20)


Используйте вызовы 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()
person nosklo    schedule 19.01.2009
comment
С matplotlib 0.98.3 правильный импорт из matplotlib.pyplot import plot, draw, show - person meteore; 21.01.2009
comment
Спасибо! но в неинтерактивном режиме в чем преимущество вызова draw () где-нибудь в коде перед финальным show ()? - person meteore; 21.01.2009
comment
@meteore: обновляет экран, если вы используете скрипт. (В интерактивном приглашении python интерактивный режим включен по умолчанию) - person nosklo; 21.01.2009
comment
Я не осознавал этого раньше из-за того, что режим Python Emacs не печатает, пока вы не закроете окно графика ... :( - person meteore; 21.01.2009
comment
draw() у меня не работает, не открывает ни одного окна. Однако использование show(block=False) вместо draw(), похоже, помогает в matplotlib 1.1. - person rumpel; 07.03.2013
comment
По состоянию на март 2013 года в Windows Spyder и iPython ›= 0.11 не работают, а рабочий процесс pylab-mode 'run, посмотрите на рисунок во время интерактивного исследования', больше не работает: spyder-ide.blogspot.co.uk/2011/08 / - person xenoclast; 24.04.2013
comment
Как ни странно, matplotlib запускается в ipython в неинтерактивном режиме. Кроме того, show() не очищает сюжет, а накладывает его. Есть ли способ очистить график перед печатью нового? - person naught101; 02.10.2013
comment
@ naught101 Думаю, пора задать еще один вопрос. Вы можете использовать clf() или, может быть, хотите close() и открыть другое окно. - person nosklo; 17.10.2013
comment
Я хочу, чтобы изображение сюжета обновлялось с определенной частотой кадров. Если я использую решение выше, окно графика даже не появится. - person João Abrantes; 19.02.2015
comment
@noskolo, что, если у меня есть несколько фигур, как построить и показать Fig1, продолжая при этом фон? Я бы хотел, чтобы эта фигура была открыта до тех пор, пока не будет сгенерирована следующая фигура, поэтому в конце я открываю все фиг и код готов. С вашим текущим решением мне приходится ждать, чтобы закрыть Fig1, а затем код продолжится. Спасибо!! - person physiker; 07.03.2016
comment
draw() у меня тоже не сработал, только pause(0.001): stackoverflow.com/questions/28269157/ - person NumesSanguis; 25.05.2017
comment
ion() помогает мне! Код Visual Studio для Macos .. - person Tapper; 13.10.2020

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

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

чтобы продолжить свой код.

person Jan    schedule 13.11.2012
comment
но это немедленно закроет окно графика, а не останется открытым. - person LWZ; 09.02.2013
comment
Да, это правда, если вы вызываете свой скрипт из командной строки. Если вы находитесь в оболочке Ipython, окно не закроется. - person Jan; 11.02.2013
comment
проверьте ответ @Nico на предмет трюка, который в общем случае оставит окно открытым. - person Jan; 20.01.2014
comment
Для меня это не закрывает окно сразу, а только когда скрипт завершен (так что вы можете вручную заблокировать в конце скрипта, если хотите, чтобы оно оставалось открытым). - person luator; 28.03.2017
comment
Да, неблокированные окна закроются при выходе из скрипта. Вы можете либо (а) разрешить блокировку на вашем последнем сюжете, либо (б) не выходить из скрипта (возможно, запросите ввод: нажмите ‹Enter›, чтобы выйти из сюжета, или что-то в этом роде). - person Daniel Goldfarb; 29.01.2020

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

Но если вам нужно более общее решение или нет другого пути, вы можете запустить все, что блокирует, в отдельном процессе, используя _ 1_ включен в 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)

person nosklo    schedule 19.01.2009
comment
Спасибо! Поскольку в моей системе еще нет Python 2.6, я использовал threading.Thread вместо Process. Я заметил, что последующие операторы печати становятся невыносимо медленными (третья печать, я выдал KeyboardInterrupt после 1 минуты ожидания). Это результат использования потоковой обработки вместо многопроцессорной обработки? - person meteore; 21.01.2009
comment
@meteore: Да, многопоточность - отстой. Вы всегда можете получить многопроцессорность для python ‹2.6 отсюда: pyprocessing.berlios.de - person nosklo; 21.01.2009
comment
Это просто отлично. У вас есть идея, почему операторы печати не выполняются в Emacs (режим Python) до тех пор, пока окно графика не будет закрыто? - person meteore; 21.01.2009
comment
В Ubuntu 8.10 (Intrepid) пакет (для python ‹2.6) называется обработкой Python, и вы импортируете его с помощью« обработки импорта ». - person meteore; 23.01.2009
comment
Разве вы не пропустили if __name__ == '__main__':? - person Wernight; 19.03.2013
comment
Это решение слишком сложное и вносит слишком много дополнительных сложностей. Вместо этого вы должны использовать draw, если вы действительно не боретесь с ограничениями производительности. - person Peter; 22.06.2016

Пытаться

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, чтобы переопределить поведение блокировки, описанное выше.

person Nico Schlömer    schedule 18.01.2013
comment
почему бы не использовать draw (); [. другой код]; Показать() ? Насколько я знаю, block = False устарел - person Bogdan; 18.09.2016
comment
Как вы думаете, почему он устарел? Я вижу, что это задокументировано здесь. - person Nico Schlömer; 19.09.2016

ВАЖНО: просто чтобы прояснить ситуацию. Я предполагаю, что команды находятся внутри сценария .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()

person seralouk    schedule 08.03.2017

Вы можете прочитать этот документ в документации matplotlib под названием:

Использование matplotlib в оболочке Python

person nosklo    schedule 19.01.2009

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

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. Довольно полезное руководство по объектно-ориентированному интерфейсу matplotlib .

person meteore    schedule 21.01.2009

Что ж, у меня были большие проблемы с определением неблокирующих команд ... Но, наконец, мне удалось переработать "Cookbook / Matplotlib / Animations - Пример анимации выбранных элементов графика ", поэтому он работает с потоками (и передает данные между потоками либо через глобальные переменные, либо через многопроцессор _1 _ ) на 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! 

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

person sdaau    schedule 10.11.2010

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

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

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

Недостаток:

  • Единственное, что я могу придумать, это то, что вам нужно будет найти папку и открыть изображение самостоятельно.
person elgehelge    schedule 18.12.2013
comment
Если вы пытаетесь создать много изображений, я полностью согласен. - person fantabolous; 15.10.2014
comment
Обратные png-файлы не являются интерактивными: \ - person Inverse; 26.04.2015

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

plt.show(0)

Что будет таким же.

person Anton Protopopov    schedule 18.01.2016

Мне также пришлось добавить 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)
person Martin Pecka    schedule 09.11.2016
comment
Это сработало для меня с matplotlib3 на macOS. Большой! - person Jerry Ma; 18.06.2019

В моей системе 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)

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

person Andrew    schedule 17.03.2011
comment
Спасибо! Spyder импортирует -pylab при запуске, что обычно полезно, но означает, что show () не будет блокироваться, когда ioff () - это позволяет исправить такое поведение! - person lost; 26.11.2012

Я также хотел, чтобы мои графики отображали выполнение остальной части кода (а затем продолжали отображать), даже если есть ошибка (я иногда использую графики для отладки). Я закодировал этот небольшой прием, чтобы любые графики внутри этого оператора 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 секунд):» from https://stackoverflow.com/questions/26704840/corner-cases-for-my-wait-for-user-input-interruption-implementation.

person Simon Streicher    schedule 04.11.2014

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

Этот автономный файл использует multiprocessing для запуска отдельного процесса построения данных с помощью matplotlib. Основной поток завершается с использованием os._exit(1), упомянутого в this сообщение. os._exit() заставляет main выйти, но оставляет 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__ завершает работу, но окно multiprocessing + matplotlib рисунка остается отзывчивым с кнопками масштабирования, панорамирования и другими кнопками, поскольку это независимый процесс.

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

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

person Marc Compere    schedule 11.07.2019
comment
Я пытался использовать ваше решение, но оно не работает для меня, и я пытаюсь понять, почему. Я запускаю код не через терминал, а из Pycharm IDE, если это имеет значение, хотя и не должно. - person ttsesm; 23.05.2020
comment
хорошо, что наконец сработало для меня, так это установка дочернего процесса с .daemon=False, как описано здесь stackoverflow.com/a/49607287/1476932 Однако sys.exit() не завершил родительский процесс, как описано там, пока я не закрыл дочернее окно. С другой стороны, использование os._exit(0) из приведенного выше примера действительно сработало. - person ttsesm; 25.05.2020

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

person MikeTeX    schedule 27.12.2015

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

show(block=False)
draw()
person DomDev    schedule 16.09.2016
comment
show (block = False) устарел и больше не работает - person Bogdan; 18.09.2016

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

  • Я создаю .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» определяет график в цикле, поэтому он не будет перезаписан.

person patowski1981    schedule 10.01.2020

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

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

person Ken Mueller    schedule 18.08.2019
comment
См. Ответ @nico-schlömer - person Josh Wolff; 07.01.2020

Мой ответ немного не по теме, но я просто решил уйти от matplotlib в своем приложении из-за проблемы, указанной в этом вопросе.

Кроме того, вы можете использовать плотно. Это не блокирует процесс.

person Alexander Rakhmaev    schedule 15.12.2020

person    schedule
comment
Как нажать клавишу ВВОД, прежде чем существовать? - person grovina; 04.06.2017