Generowanie PNG z matplotlib, gdy DISPLAY jest niezdefiniowany

319

Próbuję używać Networkx z Pythonem. Po uruchomieniu tego programu pojawia się ten błąd. Czy czegoś brakuje?

#!/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

Otrzymuję teraz inny błąd:

#!/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

Otrzymuję teraz inny błąd:

#!/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
krisdigitx
źródło
3
możliwy duplikat generowania grafów matplotlib bez działającego serwera X
Jouni K. Seppänen
9
Przenieś połączenie do matplotlib.use („Agg”) ponad inne importowane elementy, w szczególności powinno to nastąpić przed importem matplotlib.pyplot
Ivo Bosticky
@IvoBosticky komentarz również rozwiązał to dla mnie: jedyną mylącą kwestią jest „ponad twoje inne importowanie”. Powinno być oczywiste, że musisz zaimportować matplotlib przed ... Oto całe ustawienie, które działało dla mnie: import matplotlib // matplotlib.use ('Agg') // import matplotlib.pyplot as plt
mrk

Odpowiedzi:

518

Głównym problemem jest to, że (w twoim systemie) matplotlib domyślnie wybiera backend wykorzystujący x. Właśnie miałem ten sam problem na jednym z moich serwerów. Rozwiązaniem było dla mnie dodanie następującego kodu w miejscu, które jest czytane przed jakimkolwiek innym importem pylab / matplotlib / pyplot :

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

Alternatywą jest ustawienie go w pliku .matplotlibrc

Reinout van Rees
źródło
182
Ważna uwaga: .use należy wywołać przed importem pyplot. Na przykład, jeśli próbujesz na przykład zaimportować pyplot, najpierw musisz zaimportować matplotlib, użyć połączenia, a następnie zaimportować pyplot.
seaotternerd
8
Powyższy komentarz został wyjaśniony bardziej przez tę odpowiedź .
Ioannis Filippidis
2
Jak „ustawić to w swoim .matplotlibrc”?
tommy.carstensen
18
backend: aggw ~/.config/matplotlib'/matplotlibrc(jako przykład, patrzhttp: //matplotlib.org/faq/troublesh_faq.html#locating-matplotlib-config-dir). Zobacz także matplotlib.org/users/customizing.html , który ma przykładowy plik konfiguracyjny na dole strony. Znajdź „agg” na tej stronie, a zobaczysz potrzebną opcję konfiguracji.
Reinout van Rees,
4
Dla odniesienia, oto link do dokumentacji matplotlib, która to wyjaśnia. (+1, świetna odpowiedź, pomogło mi doskonale!)
Tim S.
72

Tylko jako uzupełnienie odpowiedzi Reinouta.

Stałym sposobem rozwiązania tego rodzaju problemu jest edycja pliku .matplotlibrc. Znajdź to przez

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

Następnie zmodyfikuj backend w tym pliku na backend : Agg. To jest to.

Chris.Q
źródło
5
Pro wskazówka: zestaw $MATPLOTLIBRCdo katalogu , w którym chcesz, aby rzucić swój własny matplotlibrc w.
Kenneth Hoste
Niby przesada w przypadku takiego problemu, ale myślę, że jeśli serwer zawsze działa bezgłowo, sensowne jest zmodyfikowanie pliku konfiguracyjnego. Czy miałoby to jakieś skutki uboczne dla działania matplotlibu?
BruceJohnJennerLawso
Używam matplotlib na serwerze WWW, więc to była dla mnie odpowiedź. Nie zauważyłem żadnych skutków ubocznych.
szpic
42

Czystą odpowiedzią jest prawidłowe przygotowanie środowiska wykonawczego.

Pierwszą techniką, którą musisz przygotować środowisko wykonawcze, jest użycie matplotlibrcpliku, jak mądrze zalecił Chris Q. , ustawienie

backend : Agg

w tym pliku. Możesz nawet kontrolować - bez zmian kodu - jak i gdzie matplotlib szuka i znajduje matplotlibrcplik .

Drugą techniką, którą musisz przygotować środowisko wykonawcze, jest użycie MPLBACKENDzmiennej środowiskowej (i poinformowanie użytkowników, aby z niej skorzystali):

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

Jest to przydatne, ponieważ nie trzeba nawet udostępniać innego pliku na dysku, aby to działało. Zastosowałem to podejście, na przykład testując w ciągłej integracji i działając na zdalnych komputerach, które nie mają wyświetlaczy.

Twarde kodowanie backendu matplotlib do „Agg” w kodzie Python jest jak walenie kwadratowego kołka w okrągły otwór dużym młotkiem, kiedy zamiast tego mogłeś po prostu powiedzieć matplotlib, że musi to być kwadratowy otwór.

gotgenes
źródło
Druga technika wygląda jak najbardziej elegancka w tej sytuacji.
Dmitry Kabanov
Korzystanie z MPLBACKEND rozwiązało to dla mnie. Zdecydowanie najbardziej elegancki sposób!
SaturnFromTitan
41

Wystąpił błąd podczas używania matplotlib przez Spark. matplotlib.use('Agg')nie działa dla mnie Ostatecznie poniższy kod działa dla mnie. Więcej tutaj

import matplotlib.pyplot as plt.
plt.switch_backend('agg')
użytkownik3282611
źródło
Działa to świetnie, bez ograniczeń dotyczących kolejności importowania matplotlib i innych bibliotek.
PabTorre,
Czy działając na platformie Spark musiałeś to ograniczyć, aby działało na węźle głównym, czy też działało to podczas pracy na węzłach roboczych?
Saca
Używam tego w projekcie django i to był jedyny sposób, w jaki mogłem go uruchomić.
HenryM
31

Powtórzę tylko to, co powiedział @Ivo Bosticky, co można przeoczyć. Umieść te linie na BARDZO początku pliku py.

import matplotlib
matplotlib.use('Agg') 

Lub ktoś dostanie błąd

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: UserWarning: To wywołanie metody matplotlib.use () nie działa
ponieważ backend został już wybrany;
matplotlib.use () należy wywołać * przed * pylab, matplotlib.pyplot, *

To rozwiąże wszystkie problemy z wyświetlaniem

Somum
źródło
15

Odkryłem, że ten fragment kodu działa dobrze podczas przełączania między środowiskami X i 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
Matthias123
źródło
Moim zdaniem jest to lepsze rozwiązanie niż przyjęte, chociaż nie odpowiada bezpośrednio na pytanie i odpowiada na pytanie, które nie zostało zadane.
Daisuke Aramaki
14

Podczas logowania do serwera w celu wykonania kodu użyj tego zamiast tego:

ssh -X username@servername

-Xbędzie pozbyć się bez imienia i bez błędu wyświetlania zmiennej środowiska $ DISPLAY

:)

rajol kochlashvili
źródło
1
Muszę użyć „-X”, aby zapisać obraz .png. Wielkie dzięki.
nos
To nie powiedzie się przez długi proces, jeśli ssh przekroczy limit czasu lub jeśli musisz się rozłączyć z jakiegokolwiek powodu. Pamiętaj, że może dojść do przekroczenia limitu czasu, jeśli połączony klient przejdzie w tryb uśpienia.
posdef
Możesz zapobiec przekroczeniu limitu czasu, dodając, -o ServerAliveCountMax=120 -o ServerAliveInterval=30co spowoduje, że klient ssh będzie wysyłać pusty pakiet co 30 sekund przez maksymalnie 1 godzinę.
Alex
5

W jakim systemie jesteś? Wygląda na to, że masz system z X11, ale zmienna środowiskowa DISPLAY nie została poprawnie ustawiona. Spróbuj wykonać następujące polecenie, a następnie uruchom ponownie program:

export DISPLAY=localhost:0
Michael Aaron Safyan
źródło
ale dlaczego trzeba ustawić zmienną wyświetlającą, jestem zdalnie zalogowany na tym serwerze, wszystko co powinien zrobić to wygenerować plik PNG ???
krisdigitx
1
@ krisdigitx, jeśli jesteś podłączony zdalnie, nie ustawiaj zmiennej wyświetlanej; zamiast tego używaj flagi „-XY” podczas łączenia. Aby wyświetlić, musi wiedzieć, do którego Xserver wysłać obraz; w takim przypadku byłby to wyświetlacz komputera zamiast komputera zdalnego. Użycie flagi „-XY” powoduje, że SSH automatycznie ustawia zmienną DISPLAY, tak aby wskazywała na wyświetlacz podłączanego komputera.
Michael Aaron Safyan
@ krisdigitx, zgadzam się, to bardzo dziwne, że to robi; przypuszczam jednak, że maluje obraz za pomocą X11, a następnie zapisuje wynik za pomocą X11.
Michael Aaron Safyan
Użycie tego ustawienia dla $ DISPLAY nie działa na EC2 z systemem Ubuntu 16 - nie można połączyć się w celu wyświetlenia „localhost: 0”
PabTorre
5
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

Mi to pasuje.

Qing En
źródło
3

Inną sprawą do sprawdzenia jest to, czy aktualny użytkownik jest uprawniony do połączenia z wyświetlaczem X. W moim przypadku root nie mógł tego zrobić i matplotlib narzekał z tym samym błędem.

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

źródło: http://www.debian-administration.org/articles/494 https://debian-administration.org/article/494/Getting_X11_forwarding_through_ssh_working_after_running_su

Alex
źródło
2

Aby mieć pewność, że Twój kod jest przenośny w systemach Windows, Linux i OSX oraz w systemach z wyświetlaczami i bez nich, sugerowałbym następujący fragment:

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

Źródło: https://stackoverflow.com/a/45756291/207661

Shital Shah
źródło
1

W przypadku Google Cloud Machine Learning Engine:

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

A następnie, aby wydrukować do pliku:

#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()

i aby utworzyć plik PDF:

multi_page(report_name)
Kim Miller
źródło