Створення 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.