Генерация 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 до того, как ... Это весь параметр, который работал для меня: import 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, использовать call, а затем импортировать pyplot.
Seaotternerd

8
Приведенный выше комментарий объясняется больше этим ответом .
Иоаннис Филиппидис

2
Как вы «установите его в .matplotlibrc»?
tommy.carstensen

18
backend: aggв ~/.config/matplotlib'/matplotlibrc(например, см. http://matplotlib.org/faq/troublesho__faq.html#locating-matplotlib-config-dir). См. Также matplotlib.org/users/customizing.html , в котором пример файла конфигурации находится внизу страницы. Найдите «agg» на этой странице, и вы увидите нужный вам параметр конфигурации.
Рейноут Ван Рис

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

72

Так же, как дополнение к ответу Рейно.

Постоянный способ решить эту проблему - отредактировать файл .matplotlibrc. Найти через

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

Затем измените бэкэнд в этом файле на backend : Agg. Вот и все.


5
Pro совет: установите $MATPLOTLIBRCв каталог , где вы хотите , чтобы бросить свой собственный matplotlibrc в.
Кеннет Hoste

В некотором роде излишняя проблема, подобная этой, но я думаю, что если сервер всегда работает без головы, имеет смысл изменить файл конфигурации. Будет ли это иметь побочные эффекты от того, как будет работать matplotlib?
БрюсДжонДженнерЛавсо

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

42

Чистый ответ - потратить немного времени на правильную подготовку среды выполнения.

Первый метод , вы должны подготовить среду выполнения, чтобы использовать matplotlibrcфайл, так мудро , рекомендованный Chris Q. , установка

backend : Agg

в этом файле. Вы даже можете контролировать - без изменений кода - как и где matplotlib ищет и находит matplotlibrcфайл .

Второй метод, которым вы должны подготовить свою среду исполнения, - это использовать 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 вам приходилось ограничивать это для работы на головном узле или вы работали при работе на рабочих узлах?
Сака

Я использую это в проекте Django, и это был единственный способ заставить его работать.
HenryM

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 и no-X.

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

На мой взгляд, это лучшее решение, чем принятое, хотя оно не дает прямого ответа на вопрос и отвечает на не заданный вопрос.
Дайсуке Арамаки

14

При входе на сервер для выполнения кода используйте вместо этого:

ssh -X username@servername

-Xизбавится от без имени дисплея и без ошибок переменной окружения $ DISPLAY

:)


1
Мне нужно использовать '-X', чтобы сохранить изображение .png. Большое спасибо.

Это не удастся в течение длительного процесса, если время ожидания ssh истекло, или если вам необходимо отключиться по какой-либо причине. Обратите внимание, что тайм-аут может даже произойти, если подключающийся клиент переходит в спящий режим.
Посеф

Вы можете предотвратить тайм-ауты, добавив, -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


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 Machine Learning 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.