Переносимый метод создания интерактивных фигур matplotlib в Windows, Linux, Spyder, командной строке - PullRequest
0 голосов
/ 16 ноября 2018

Я ищу способ генерирования интерактивных фигур, который работает в Windows и Linux, командной строке или Spyder.Например, следующий скрипт:

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, tight_layout=True)
#plt.ion() # doesn't seem to do much
ax.plot([0,1,2],[0,3,2])
fig.show()
#plt.show(block=False) # similar behavior as fig.show()
#plt.pause(1)
input('Press enter to quit.')

Поведение для различных сред:

  • Командная строка Linux: окно графика отображается и реагирует, пока скрипт ожидает пользователявход.Окно остается, даже если программа продолжает работать (не в этом коротком примере), хотя кнопки масштабирования больше не работают.Это желаемое поведение.

  • Командная строка Windows: появляется пустое окно не отвечающего графика, которое исчезает при завершении программы.Добавление plt.pause() приводит к интерактивному графику, но он реагирует только на указанное количество секунд.

  • Linux / Windows Spyder с iPython, настроенный для автоматических графиков: отображаются цифры иявляются адаптивными, но только после завершения сценария.

  • Linux / Windows Spyder, настроенный для встроенных графиков: графики отображаются после завершения сценария, но с предупреждениями из-за параметра tight_layout=True: UserWarning: на этом рисунке представлены оси, которые не совместимы с тесной связью, поэтому результаты могут быть неверными. и matplotlib в настоящее время использует не-GUI бэкэнд .(Обратите внимание, что мне нужно tight_layout, потому что в противном случае часто метки осей обрезаются или у рисунков с несколькими вспомогательными участками возникают плохие поля.)

  • Linux / Windows Spyder, встроенные графики: с plt.plot(...); plt.show() (вместо fig.plot(...); fig.show() объектно-ориентированного способа), встроенные графики отображаются во время выполнения программы.

Как мне написать код, который генерирует интерактивные графики во время выполнения программы (возможно, во время ожидания нажатия клавиши), который будет корректно работать из командной строки Windows, командной строки Linux и Spyder?

Редактировать: plt.show() вместо fig.show() приведет к корректному отображению графика, но за пределами IPython блокирует выполнение скрипта до тех пор, пока я не нажму кнопку закрытия окна рисунка.Это довольно громоздко, когда есть несколько цифр или когда расчет еще не закончен.И plt.show(block=False) ведет себя так же, как и fig.show().

Я использую ненастроенную среду Anaconda 5.1 с Python 3.6.4, matplotlib 2.1.2, spyder 3.2.6.В Spyder: «Инструменты»> «Настройки»> «IPython»> «Backend» установите «inline» или «автоматический» (и перезапустите ядро ​​после изменения этого параметра.)

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Вот такой подход работает, но не очень хорошо для всех сред:

import matplotlib.pyplot as plt
import sys
import matplotlib

def plt_show_interactive():
    """Show plot windows, with interaction if possible; prompt to continue.""" 
    in_ipython = ('get_ipython' in globals())
    inline_backend = ('inline' in matplotlib.get_backend())
    in_linux = (sys.platform == 'linux')

    if inline_backend:
        plt.show()
    elif not in_linux and in_ipython:
        print("Press Ctrl-C to continue.")
        try:    
            while True:
                plt.pause(0.5)
        except KeyboardInterrupt:
            print("Continuing.")
    elif in_linux and not in_ipython:
        # Command line: plots are interactive during wait for input.
        plt.show(block=False)
        input("Press ENTER to continue.")
    elif in_linux and in_ipython:
        # Loop with plt.pause(1) causes repeated focus stealing.
        plt.pause(1)
        print("Sorry, plots are not interactive until program has finished.")
    elif not in_linux and not in_ipython:
        # Ctrl-C is handled differently here.
        plt.pause(1)
        input("Sorry, not interactive. Press ENTER to continue.")


def prompt_if_not_ipython(verb="end"):
    """Ask user to press ENTER if not we're not inside IPython."""
    if ('get_ipython' not in globals()):
        input("Press ENTER to %s." % verb)


fig, ax = plt.subplots(1, 1, tight_layout=True)
ax.plot([0,1,2],[0,3,2])

plt_show_interactive()
prompt_if_not_ipython()
0 голосов
/ 19 ноября 2018

Похоже, вам нужно следующее:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1, tight_layout=True)

plt.ion()
ax.plot([0,1,2],[0,3,2])

plt.show()
plt.pause(0.1)

input('Press enter to quit.')
plt.close()

В общем, программа на Python оценивается линейно. Это противоречит желанию иметь блокирующую командную строку (input), но неблокирующий графический интерфейс.
То, что происходит в вышеупомянутом, - то, что GUI создан, но без какого-либо запущенного цикла обработки событий. Поэтому код может продолжать выполняться даже после отображения графического интерфейса. Недостатком этого является то, что без цикла обработки событий вы не можете взаимодействовать с графическим интерфейсом.

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

import matplotlib.pyplot as plt

def run_after(callback, pause=10, figure=None):
    figure = figure or plt.gcf()
    timer = figure.canvas.new_timer(interval=pause)
    timer.single_shot = True
    timer.add_callback(callback)
    timer.start()
    return timer


fig, ax = plt.subplots(1, 1, tight_layout=True)
ax.plot([0,1,2],[0,3,2])

# This function contains the code to be executed within the GUI event loop
def mycallback():
    import tkinter
    from tkinter import simpledialog
    root = tkinter.Tk()
    root.withdraw()
    w = simpledialog.askinteger("Title", "How many numbers do you want to print")

    for i in range(w):
        print(i)


cb = run_after(mycallback)

plt.show()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...