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


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


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.


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.


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')

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


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

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

:)


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

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.


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


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


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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.