Как манипулировать цифрами, пока в Python работает script?
Введение
Как я исхожу из matlab, Я привык к интерактивному интерфейсу, где script может обновлять цифры во время работы. Во время обработки каждая цифра может быть изменена или даже закрыта. Вероятно, это означает, что каждая фигура работает в своем собственном потоке, что, очевидно, не относится к matplotlib.
IPython может имитировать поведение Matlab с помощью волшебной команды %pylab
или %matplotlib
, которая делает то, что я еще не понимаю, и что является самой точкой моего вопроса.
Моя цель заключается в том, чтобы позволить автономным скриптам Python работать, как это делает Matlab (или как IPython с %matplotlib
). Другими словами, я хотел бы, чтобы этот script выполнялся из командной строки. Я ожидаю появления новой фигуры каждые 3 секунды. Во время выполнения я мог бы увеличить, изменить размер или даже закрыть фигуру.
#!/usr/bin/python
import matplotlib.pyplot as plt
import time
def do_some_work():
time.sleep(3)
for i in range(10):
plt.plot([1,2,3,4])
plt.show() # this is way too boilerplate, I'd like to avoid it too.
do_some_work()
Какую альтернативу %matplotlib
я могу использовать для управления фигурами, а script работает в Python (не IPython)?
Какие решения я уже исследовал?
В настоящее время я нашел 3 способа получить сюжетное шоу.
1. %pylab
/%matplotlib
Как tom, следует избегать использования %pylab
, чтобы предотвратить загрязнение пространства имен.
>>> %pylab
>>> plot([1,2,3,4])
Это решение сладко, график не блокируется, нет необходимости в дополнительном show()
, я могу добавить сетку с grid()
после этого, и я могу закрыть, изменить размер или увеличить масштаб фигуры без дополнительные проблемы.
К сожалению, команда %matplotlib
доступна только на IPython.
2. from pylab import *
или from matplotlib.pyplot import plt
>>> from pylab import *
>>> plot([1,2,3,4])
Здесь все совсем другое. Мне нужно добавить команду show()
, чтобы отобразить мою фигуру, которая блокирует. Я не могу ничего сделать, кроме закрытия фигуры, чтобы выполнить следующую команду, такую как grid()
, которая не будет иметь никакого эффекта, поскольку фигура теперь закрыта...
** 3. from pylab import *
или from matplotlib.pyplot import plt
+ ion()
**
В некоторых предложениях рекомендуется использовать команду ion()
следующим образом:
>>> from pylab import *
>>> ion()
>>> plot([1,2,3,4])
>>> draw()
>>> pause(0.0001)
К сожалению, даже если график показывает, я не могу закрыть фигуру вручную. Мне нужно выполнить close()
на терминале, что не очень удобно. Кроме того, необходимость в двух дополнительных командах, таких как draw(); pause(0.0001)
, не то, что я ожидаю.
Резюме
С %pylab
все замечательно, но я не могу использовать его за пределами IPython
С from pylab import *
, за которым следует plot
, я получаю поведение блокировки, и вся мощность IPython теряется.
from pylab import *
, за которым следует ion
, предлагает хорошую альтернативу предыдущей, но я должен использовать команду weird pause(0.0001)
, которая приводит к тому, что я не могу закрыть окно вручную (я знаю, что pause
не нужен с некоторыми backend.Я использую WxAgg
, который является единственным, который хорошо работает на Cygwin x64
.
В этом question рекомендуется использовать matplotlib.interactive(True)
. К сожалению, он не работает и дает то же поведение, что и ion()
.
Ответы
Ответ 1
Измените функцию do_some_work
на следующую и она должна работать.
def do_some_work():
plt.pause(3)
Для интерактивных бэкэнд plt.pause(3)
запускает цикл событий в течение 3 секунд, чтобы он мог обрабатывать события изменения размера. Обратите внимание, что документация говорит, что это экспериментальная функция, и что для сложных анимаций вы должны использовать модуль анимации.
Магические команды, %pylab
и %matplotlib
также запускают цикл событий, поэтому возможно взаимодействие с пользователями. Кроме того, вы можете запустить цикл событий с помощью %gui wx
и отключить его с помощью %gui
. Вы можете использовать функцию IPython.lib.guisupport.is_event_loop_running_wx(), чтобы проверить, запущена ли она.
Причина использования ion()
или ioff()
очень хорошо объясняется на странице 'What is interactive mode'. В принципе, взаимодействие с пользователем возможно без IPython. Однако я не смог получить интерактивный пример с этой страницы для работы с бэкэнд Qt4Agg
, только с бэкэнд MacOSX
(на моем Mac). Я не пытался использовать бэкэнд WX
.
Edit
Мне удалось заставить интерактивный пример работать с бэкендом Qt4Agg, используя PyQt4 вместо PySide (поэтому, установив backend.qt4 : PyQt4
в мой файл ~/.config/matplotlibrc
). Я думаю, что пример не работает со всеми бэкэндами. Я отправил вопрос здесь.
Изменить 2
Я боюсь, что не могу придумать способ манипулирования фигурой, пока выполняется длинный расчет без использования потоков. Как вы упомянули: Matplotlib не запускает нить, и IPython тоже. Команды% pylab и% matplotlib чередуются между командами обработки из цикла read-eval-print и позволяют графическим интерфейсам обрабатывать события на короткое время. Они делают это последовательно.
На самом деле, я не могу воспроизвести ваше поведение, даже с помощью маты matplotlib или% pylab. (Просто чтобы быть ясным: в ipython
я сначала вызываю %matplotlib
, а затем %run yourscript.py
). Матрица% matplotlib помещает Matplotlib в интерактивный режим, что делает вызов plt.show()
неблокирующим, так что функция do_some_work
выполняется немедленно. Однако во время вызова time.sleep(3)
фигура не реагирует (это становится еще более очевидным, если я увеличиваю период сна). Я не понимаю, как это может работать с вашей стороны.
Если я ошибаюсь, вам придется разбить ваш расчет на более мелкие части и использовать plt.pause
(или даже лучше, модуль анимации), чтобы обновить цифры.
Ответ 2
Мой совет будет заключаться в том, чтобы продолжать использовать IPython, поскольку он управляет контуром событий GUI для вас (что делает pylab/pylot).
Я пробовал интерактивный график в обычном интерпретаторе, и он работал так, как ожидается, даже без вызова ion()
(Debian unstable, Python 3.4.3+, Matplotlib 1.4.2-3.1). Если я помню это правильно, это довольно новая функция в Matplotlib.
В качестве альтернативы вы также можете использовать возможности анимации Matplotlib для периодического обновления графика:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
plt.ion()
tt = np.linspace(0, 1, 200)
freq = 1 # parameter for sine
t0 = time.time() # for measuring ellapsed time
fig, ax = plt.subplots()
def draw_func(i):
""" This function gets called repeated times """
global freq # needed because freq is changed in this function
xx = np.sin(2*np.pi*freq*tt)/freq
ax.set_title("Passed Time: %.1f s, " % (time.time()-t0) +
"Parameter i=%d" % i)
ax.plot(tt, xx, label="$f=%d$ Hz" % freq)
ax.legend()
freq += 1
# call draw_func every 3 seconds 1 + 4 times (first time is initialization):
ani = animation.FuncAnimation(fig, draw_func, np.arange(4), interval=3000,
repeat=False)
# plt.show()
Оформить заказ matplotlib.animation.FuncAnimation для деталей. Дальнейшие примеры вы найдете в разделе .