Чи є спосіб відокремити графіки matplotlib, щоб розрахунки могли продовжуватися?


258

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

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

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

Це взагалі можливо? Іноді розрахунки довгі, і це допоможе, якщо вони будуть продовжуватись під час вивчення проміжних результатів.


5
Я не можу підтвердити, що вибране рішення з nosklo о 16:52 працює. Для мене малюнок не відкриває вікно для відображення сюжету, лише блокувальне шоу в кінці відображає рішення. Однак його відповідь з 17:00 правильна. Увімкнення інтерактивного режиму за допомогою ion()виправляє проблему.
H. Brandsmeier

якщо ви досвідчений програміст, ви можете використовувати, 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()

28
З matplotlib 0.98.3 право імпорту відбувається з сюжету імпорту matplotlib.pyplot, малювати, показувати
метеоре

112
draw()не працює для мене, воно не відкриває жодного вікна. Однак використання show(block=False)замість цього, draw()здається, зробити трюк у matplotlib 1.1.
румпель

4
@nosklo, ти бачив? Ви зробили це підручником
січня

4
@noskolo Що робити, якщо у мене є кілька фігур, як побудувати та показати Fig1, продовжуючи тло, щоб продовжувати роботу? Мені б хотілося, щоб ця цифра була відкритою до наступної генерації фігури, тому в кінці я відкриваю всі фігури і код закінчується. З вашим поточним рішенням, я змушує мене чекати закриття Fig1, а потім код продовжується. Дякую!!
фізик

9
draw()не працює для мене теж, тільки pause(0.001)зробив: stackoverflow.com/questions/28269157 / ...
NumesSanguis

133

Використовуйте ключове слово "блок", щоб змінити поведінку блокування, наприклад

from matplotlib.pyplot import show, plot

plot(1)  
show(block=False)

# your code

щоб продовжити свій код.


17
але це негайно закриє вікно сюжету, не буде тримати сюжет відкритим.
LWZ

8
Так, це правда, якщо ви викликаєте свій скрипт із командного рядка. Якщо ви знаходитесь в оболонці Ipython, вікно не закриється.
Jan

1
Перевірте відповідь @Nico на хитрість, яка залишить вікно відкритим у загальному випадку.
Jan

2
Для мене це вікно не закриється одразу, лише після завершення сценарію (тому ви можете вручну заблокувати його в кінці сценарію, якщо ви хочете, щоб він залишався відкритим).
луатор

Так, незаблоковані вікна закриються, коли сценарій завершиться . Ви можете (а) дозволити блокування на останньому сюжеті, або (b) не виходити зі скрипту (можливо, попросити введення: "натисніть <Ввести>, щоб вийти з сюжету" або щось подібне).
Даніель Голдфарб

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 )


Дякую! Оскільки в моїй системі ще немає Python 2.6, я використовував threading.Thread як заміну для Process. Я помітив, що наступні заяви про друк стають нестерпно повільними (третій друк, я видав KeyboardInterrupt через 1 хв очікування). Це ефект від використання нарізки замість багатообробної обробки?
метеорит

@meteore: Так, різання різання. Ви завжди можете отримати мультиобробку
nosklo

Це абсолютно чудово. Чи маєте ви уявлення, чому оператори друку не виконуються в режимі Emacs (пітон), поки вікно сюжету не закриється?
метеорит

В Ubuntu 8.10 (Intrepid) пакет (для python <2.6) називається python-обробка, і ви імпортуєте його з 'імпортувати обробку'
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()Документація каже:

У неінтерактивному режимі відображайте всі фігури та блокуйте, поки фігури не будуть закриті; в інтерактивному режимі це не має ефекту, якщо фігури не були створені до переходу з неінтерактивного в інтерактивний режим (не рекомендується). У такому випадку він відображає фігури, але не блокує.

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


чому б не використовувати draw (); [.інший код]; показати ()? Наскільки я знаю, блок = False був устареним
Богдан

Чому ви вважаєте, що це застаріло? Я бачу це документально зафіксоване тут .
Ніко Шльомер

11

ВАЖЛИВО : Просто зробити щось зрозумілим. Я припускаю, що команди знаходяться всередині .pyскрипту, і сценарій викликається за допомогою, наприклад, python script.pyз консолі.

Простий спосіб, який працює для мене:

  1. Використовуйте блок = False inside show: plt.show (block = Неправильне)
  2. Скористайтеся іншим шоу () в кінці сценарію .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()



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 .


6

Що ж, у мене виникли великі проблеми з з'ясуванням команд, що не блокують ... Але, нарешті, мені вдалося переробити приклад " Cookbook / Matplotlib / Animations - Анімація вибраних елементів сюжету ", тому він працює з потоками ( і передає дані між потоками або через глобальні змінні або через багатопроцесорний процес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! 

Сподіваюся, це допоможе комусь,
ура!


5

У багатьох випадках зручніше зберігати зображення у форматі .png на жорсткому диску. Ось чому:

Переваги:

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

Недолік:

  • Єдине, про що я можу придумати - це вам доведеться зайти в пошуки папки та відкрити зображення самостійно.

Якщо ви намагаєтеся створити багато зображень, я щиро погоджуюся.
фантастичний

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 -причини-для-мого-чекаю-для-користувача-введення-переривання-реалізація .


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

16
Як ввести одну пресу перед існуючою?
grovina

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__виходить, але вікно фігури multiprocessing+ matplotlibзалишається чутливим до масштабування, панорамування та інших кнопок, оскільки це незалежний процес.

Перевірте процеси в командному рядку 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 не закінчиться?


0

Якщо ви хочете відкрити кілька фігур, зберігаючи їх усі відкриті, цей код працював для мене:

show(block=False)
draw()

show (block = False) застаріло і зараз більше не працює
Богдан

0

Не відповідаючи безпосередньо на запит ОП, я публікую це рішення, оскільки це може допомогти комусь у цій ситуації:

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


-1

Скористайтеся plt.show(block=False)і наприкінці сценарію plt.show().

Це забезпечить, що вікно не буде закрито, коли сценарій закінчений.


Дивіться відповідь @ nico-schlömer
Josh Wolff
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.