Створення PNG з matplotlib, коли DISPLAY не визначено


319

Я намагаюся використовувати networkx з Python. Коли я запускаю цю програму, вона отримує цю помилку. Що-небудь бракує?

#!/usr/bin/env python

import networkx as nx
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt

G=nx.Graph()
G.add_node(1)
G.add_nodes_from([2,3,4,5,6,7,8,9,10])
#nx.draw_graphviz(G)
#nx_write_dot(G, 'node.png')
nx.draw(G)
plt.savefig("/var/www/node.png")


Traceback (most recent call last):
  File "graph.py", line 13, in <module>
    nx.draw(G)
  File "/usr/lib/pymodules/python2.5/networkx/drawing/nx_pylab.py", line 124, in draw
    cf=pylab.gcf()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 276, in gcf
    return figure()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 254, in figure
    **kwargs)
  File "/usr/lib/pymodules/python2.5/matplotlib/backends/backend_tkagg.py", line 90, in new_figure_manager
    window = Tk.Tk()
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1650, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable

Зараз я отримую іншу помилку:

#!/usr/bin/env python

import networkx as nx
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt

matplotlib.use('Agg')

G=nx.Graph()
G.add_node(1)
G.add_nodes_from([2,3,4,5,6,7,8,9,10])
#nx.draw_graphviz(G)
#nx_write_dot(G, 'node.png')
nx.draw(G)
plt.savefig("/var/www/node.png")

/usr/lib/pymodules/python2.5/matplotlib/__init__.py:835: UserWarning:  This call to matplotlib.use() has no effect
because the the backend has already been chosen;
matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
or matplotlib.backends is imported for the first time.

  if warn: warnings.warn(_use_error_msg)
Traceback (most recent call last):
  File "graph.py", line 15, in <module>
    nx.draw(G)
  File "/usr/lib/python2.5/site-packages/networkx-1.2.dev-py2.5.egg/networkx/drawing/nx_pylab.py", line 124, in draw
    cf=pylab.gcf()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 276, in gcf
    return figure()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 254, in figure
    **kwargs)
  File "/usr/lib/pymodules/python2.5/matplotlib/backends/backend_tkagg.py", line 90, in new_figure_manager
    window = Tk.Tk()
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1650, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable

Зараз я отримую іншу помилку:

#!/usr/bin/env python

import networkx as nx
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt

matplotlib.use('Agg')

G=nx.Graph()
G.add_node(1)
G.add_nodes_from([2,3,4,5,6,7,8,9,10])
#nx.draw_graphviz(G)
#nx_write_dot(G, 'node.png')
nx.draw(G)
plt.savefig("/var/www/node.png")

/usr/lib/pymodules/python2.5/matplotlib/__init__.py:835: UserWarning:  This call to matplotlib.use() has no effect
because the the backend has already been chosen;
matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
or matplotlib.backends is imported for the first time.

  if warn: warnings.warn(_use_error_msg)
Traceback (most recent call last):
  File "graph.py", line 15, in <module>
    nx.draw(G)
  File "/usr/lib/python2.5/site-packages/networkx-1.2.dev-py2.5.egg/networkx/drawing/nx_pylab.py", line 124, in draw
    cf=pylab.gcf()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 276, in gcf
    return figure()
  File "/usr/lib/pymodules/python2.5/matplotlib/pyplot.py", line 254, in figure
    **kwargs)
  File "/usr/lib/pymodules/python2.5/matplotlib/backends/backend_tkagg.py", line 90, in new_figure_manager
    window = Tk.Tk()
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 1650, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable


9
Перемістіть дзвінок на matplotlib.use ("Agg") над вашим іншим імпортом, зокрема, це повинно бути перед імпортом matplotlib.pyplot
Іво Бостік,

@IvoBosticky коментар вирішив це і для мене: Єдине, що вводить в оману - це "вище вашого іншого імпорту". Має бути очевидним, що вам потрібно імпортувати matplotlib раніше ... Це вся настройка, яка працювала для мене: імпортувати matplotlib // matplotlib.use ('Agg') // імпортувати matplotlib.pyplot як plt
mrk

Відповіді:


518

Основна проблема полягає в тому, що (у вашій системі) matplotlib за замовчуванням вибирає хенкенд, що використовує x. У мене просто була одна і та ж проблема на одному з моїх серверів. Для мене рішення було додати наступний код у місце, яке читається перед будь-яким іншим імпортом pylab / matplotlib / pyplot :

import matplotlib
# Force matplotlib to not use any Xwindows backend.
matplotlib.use('Agg')

Альтернативою є встановити його у своєму .matplotlibrc


182
Важлива примітка: .use потрібно викликати, перш ніж імпортувати pyplot. Отже, якщо ви, наприклад, просто намагаєтеся імпортувати pyplot, вам потрібно спочатку імпортувати matplotlib, зателефонувати на використання, а потім імпортувати pyplot.
seaotternerd

8
Наведений коментар більше пояснюється цією відповіддю .
Іоанніс Філіппідіс

2
Як ви "встановите його у своєму .matplotlibrc"?
tommy.carstensen

18
backend: aggв ~/.config/matplotlib'/matplotlibrc(як приклад, див. https: //matplotlib.org/faq/troubleshooting_faq.html#locating-matplotlib-config-dir). Дивіться також matplotlib.org/users/customizing.html , який містить приклад конфігураційного файла внизу сторінки. Знайдіть "agg" на цій сторінці, і ви побачите потрібний варіант конфігурації.
Reinout van Rees

4
Для довідки, ось посилання на документацію про matplotlib, яка пояснює це. (+1, чудова відповідь. Допомогли мені чудово!)
Тім С.

72

Так само як доповнення відповіді Рейнута.

Постійний спосіб вирішити подібну проблему - це редагувати файл .matplotlibrc. Знайдіть його через

>>> import matplotlib
>>> matplotlib.matplotlib_fname() # This is the file location in Ubuntu '/etc/matplotlibrc'

Потім змініть бекенд у цьому файлі на backend : Agg. Це все.


5
Підказка: встановіть $MATPLOTLIBRCу каталог, куди ви хочете кинути свій власний matplotlibrc.
Кеннет Хосте

Якийсь надмірний рівень для подібної проблеми, але я думаю, якщо сервер завжди працює без голови, має сенс змінювати файл конфігурації. Чи матиме це побічні ефекти від того, як функціонує matplotlib?
BruceJohnJennerLawso

Я запускаю matplotlib на веб-сервері, тому це була відповідь для мене. Я не помітив жодних побічних ефектів.
шпіц

42

Чітка відповідь полягає в тому, щоб витратити трохи часу, щоб правильно підготувати середовище виконання.

Перший прийом, який вам потрібно підготувати до середовища виконання, - це використовувати matplotlibrcфайл, як мудро рекомендував Кріс Q. , налаштовуючи

backend : Agg

у цьому файлі. Ви навіть можете контролювати - без змін коду - як і де шукає matplotlibrcфайл matplotlib та знаходить файл .

Другий прийом, який потрібно підготувати до середовища виконання, - це використовувати MPLBACKENDзмінну середовища (та повідомити своїх користувачів, щоб ними скористатися):

export MPLBACKEND="agg"
python <program_using_matplotlib.py>

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

Жорстке кодування вашого Matplotlib бекенда на "Agg" у вашому коді Python - це як бити квадратний кілочок у круглий отвір великим молотком, коли, замість цього, ви могли б просто сказати matplotlib, що це має бути квадратний отвір.


Друга техніка виглядає як найелегантніша в цій ситуації.
Дмитро Кабанов

Використання MPLBACKEND вирішило це для мене. Однозначно найелегантніший спосіб!
SaturnFromTitan

41

Я отримав помилку під час використання matplotlib через Spark. matplotlib.use('Agg')не працює для мене. Зрештою, наступний код працює для мене. Більше тут

import matplotlib.pyplot as plt.
plt.switch_backend('agg')

Це чудово працює, без обмежень на замовлення, яке використовується для імпорту matplotlib та інших бібліотек.
PabTorre

Якщо ви працюєте на Spark, вам доводилося обмежувати це на головному вузлі, або ви працювали над робочими вузлами?
Сака

Я використовую це в проекті джанго, і це був єдиний спосіб, коли я міг би його працювати.
ГенріМ

31

Я просто повторю те, що сказав @Ivo Bosticky, що можна не помітити. Покладіть ці рядки на ДУЖЕ початок файлу py.

import matplotlib
matplotlib.use('Agg') 

Або можна отримати помилку

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: UserWarning: Цей виклик до matplotlib.use () не впливає
тому що вихідний сервер вже обраний;
matplotlib.use () повинен бути викликаний * до * pylab, matplotlib.pyplot, *

Це вирішить усі проблеми з Медіа


15

Я виявив, що цей фрагмент добре працює при переключенні між середовищами X і X-no.

import os
import matplotlib as mpl
if os.environ.get('DISPLAY','') == '':
    print('no display found. Using non-interactive Agg backend')
    mpl.use('Agg')
import matplotlib.pyplot as plt

На мою думку, це чудове рішення, ніж прийняте, хоча воно не відповідає безпосередньо на питання, а відповідає на запитання, яке не задається.
Daisuke Aramaki

14

Під час входу на сервер для виконання коду скористайтеся цим:

ssh -X username@servername

-Xпозбудеться без імені дисплея і без помилок змінної оточення $ DISPLAY

:)


1
Мені потрібно використовувати "-X", щоб зберегти .png зображення. Дуже дякую.
ніс

Це не вдасться тривалий процес, якщо час з ssh закінчиться, або якщо вам потрібно відключитись з будь-якої причини. Зауважте, що може виникнути навіть час очікування, якщо підключений клієнт переходить у сон.
posdef

Ви можете запобігти затримці часу, додавши, -o ServerAliveCountMax=120 -o ServerAliveInterval=30що змусить ssh-клієнт відправляти порожній пакет кожні 30 секунд протягом максимум 1 години.
Олексій

5

На якій системі ви працюєте? Схоже, у вас система з X11, але змінна середовища DISPLAY була неправильно встановлена. Спробуйте виконати таку команду, а потім відновіть програму:

export DISPLAY=localhost:0

але чому він повинен встановлювати змінну дисплея, я віддалено увійшов на цей сервер, все, що він повинен зробити, це створити файл PNG ???
krisdigitx

1
@krisdigitx, якщо ви віддалено підключені, не встановлюйте змінну відображення; замість цього під час підключення використовуйте прапор "-XY". Для відображення необхідно знати, до якого Xserver надіслати зображення; у цьому випадку це буде відображення вашого комп'ютера, а не віддаленого комп'ютера. Використання прапора "-XY" змушує SSH автоматично встановлювати змінну DISPLAY, щоб вказувати на дисплей підключення комп'ютера.
Майкл Аарон Сафян

@krisdigitx, я згоден, дуже дивно, що це робить; я гадаю, що він малює зображення за допомогою X11, а потім зберігає результат за допомогою X11.
Майкл Аарон Саф’ян

Використання цього параметра для $ DISPLAY не працює на EC2 під керуванням Ubuntu 16 - не вдалося підключитися до відображення "localhost: 0"
PabTorre

5
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

Це працює для мене.


3

Ще одна річ, щоб перевірити, чи має ваш поточний користувач підключений до дисплея X. У моєму випадку, root не дозволяв цього робити, і matplotlib скаржився на ту ж помилку.

user@debian:~$ xauth list         
debian/unix:10  MIT-MAGIC-COOKIE-1  ae921efd0026c6fc9d62a8963acdcca0
root@debian:~# xauth add debian/unix:10  MIT-MAGIC-COOKIE-1 ae921efd0026c6fc9d62a8963acdcca0
root@debian:~# xterm

джерело: http://www.debian-administration.org/articles/494 https://debian-administration.org/article/494/Getting_X11_forwarding_through_ssh_working_after_running_su


2

Щоб переконатися, що ваш код є портативним для Windows, Linux та OSX, а також для систем з дисплеями та без них, я б запропонував такий фрагмент:

import matplotlib
import os
# must be before importing matplotlib.pyplot or pylab!
if os.name == 'posix' and "DISPLAY" not in os.environ:
    matplotlib.use('Agg')

# now import other things from matplotlib
import matplotlib.pyplot as plt

Кредит: https://stackoverflow.com/a/45756291/207661


1

Для Google Cloud Engine Engine Engine:

import matplotlib as mpl
mpl.use('Agg')
from matplotlib.backends.backend_pdf import PdfPages

А потім роздрукувати у файл:

#PDF build and save
    def multi_page(filename, figs=None, dpi=200):
        pp = PdfPages(filename)
        if figs is None:
            figs = [mpl.pyplot.figure(n) for n in mpl.pyplot.get_fignums()]
        for fig in figs:
            fig.savefig(pp, format='pdf', bbox_inches='tight', fig_size=(10, 8))
        pp.close()

і створити PDF:

multi_page(report_name)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.