Generare un PNG con matplotlib quando DISPLAY non è definito


319

Sto cercando di utilizzare networkx con Python. Quando eseguo questo programma viene visualizzato questo errore. C'è qualcosa che manca?

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

Ricevo un errore diverso ora:

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

Ricevo un errore diverso ora:

#!/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
Sposta la chiamata su matplotlib.use ('Agg') sopra le altre tue importazioni, in particolare dovrebbe essere prima dell'importazione di matplotlib.pyplot
Ivo Bosticky,

@IvoBosticky ha risolto il problema anche per me: l'unica cosa fuorviante è "al di sopra delle altre importazioni". Dovrebbe essere ovvio che devi importare matplotlib prima ... Questa è l'intera impostazione che ha funzionato per me: import matplotlib // matplotlib.use ('Agg') // import matplotlib.pyplot as plt
mrk

Risposte:


518

Il problema principale è che (sul tuo sistema) matplotlib sceglie di default un backend x-using. Ho avuto lo stesso problema su uno dei miei server. La soluzione per me era quella di aggiungere il seguente codice in un posto che viene letto prima di qualsiasi altra importazione pylab / matplotlib / pyplot :

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

L'alternativa è di impostarlo nel tuo .matplotlibrc


182
Nota importante: .use deve essere chiamato prima di importare pyplot. Quindi, se, ad esempio, stai solo cercando di importare pyplot, devi prima importare matplotlib, chiamare use e poi importare pyplot.
seaotternerd

8
Il commento sopra è spiegato più da questa risposta .
Ioannis Filippidis,

2
Come si "imposta nel tuo .matplotlibrc"?
tommy.carstensen,

18
backend: aggin ~/.config/matplotlib'/matplotlibrc(ad esempio, vedere http://matplotlib.org/faq/tro troubleshooting_faq.html#locating-matplotlib-config-dir). Vedi anche matplotlib.org/users/customizing.html , che ha un file di configurazione di esempio nella parte inferiore della pagina. Trova "agg" su quella pagina e vedrai l'opzione di configurazione di cui hai bisogno.
Reinout van Rees,

4
Per riferimento, ecco il link alla documentazione matplotlib che spiega questo. (+1, ottima risposta, mi ha aiutato perfettamente!)
Tim S.

72

Proprio come un complemento della risposta di Reinout.

Il modo permanente per risolvere questo tipo di problema è modificare il file .matplotlibrc. Trovalo tramite

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

Quindi modificare il backend in quel file in backend : Agg. Questo è tutto.


5
Suggerimento professionale: imposta $MATPLOTLIBRCla directory in cui vuoi inserire il tuo matplotlibrc.
Kenneth Hoste,

Un po 'eccessivo per un problema come questo, ma immagino che se il server funziona sempre senza testa ha senso modificare un file di configurazione. Ciò avrebbe effetti collaterali su come funzionerà matplotlib?
BruceJohnJennerLawso,

Sto eseguendo matplotlib su un server Web, quindi questa è stata la risposta per me. Non ho notato effetti collaterali.
Spitz

42

La risposta chiara è impiegare un po 'di tempo a preparare correttamente l'ambiente di esecuzione.

La prima tecnica che devi preparare il tuo ambiente di esecuzione è usare un matplotlibrcfile, come saggiamente raccomandato da Chris Q. , impostazione

backend : Agg

in quel file. Puoi persino controllare - senza modifiche al codice - come e dove cerca e trova il matplotlibrcfile matplotlib .

La seconda tecnica che devi preparare per il tuo ambiente di esecuzione è usare la MPLBACKENDvariabile d'ambiente (e informare i tuoi utenti per usarla):

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

Questo è utile perché non è nemmeno necessario fornire un altro file sul disco per farlo funzionare. Ho adottato questo approccio con, ad esempio, test in integrazione continua e in esecuzione su macchine remote che non dispongono di display.

Codificare a fondo il tuo backend matplotlib su "Agg" nel tuo codice Python è come colpire un piolo quadrato in un buco rotondo con un grosso martello, quando invece avresti potuto dire a matplotlib che doveva essere un buco quadrato.


La seconda tecnica sembra la più elegante in questa situazione.
Dmitry Kabanov,

L'uso di MPLBACKEND ha risolto il problema per me. Sicuramente il modo più elegante!
SaturnFitTitan

41

Ho riscontrato l'errore durante l'utilizzo di matplotlib tramite Spark. matplotlib.use('Agg')non funziona per me. Alla fine, il seguente codice funziona per me. Più qui

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

Funziona alla grande, senza le restrizioni sull'ordine utilizzato per importare matplotlib e altre librerie.
PabTorre,

Durante l'esecuzione su Spark, è stato necessario limitarlo per l'esecuzione sul nodo head o è riuscito a farlo funzionare quando si esegue su nodi di lavoro?
Saca,

Lo sto usando in un progetto Django e questo era l'unico modo per farlo funzionare.
HenryM,

31

Devo solo ripetere ciò che ha detto @Ivo Bosticky che può essere trascurato. Metti queste righe all'inizio MOLTO del file PY.

import matplotlib
matplotlib.use('Agg') 

O si otterrebbe un errore

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: UserWarning: questa chiamata a matplotlib.use () non ha alcun effetto
perché il backend è già stato scelto;
matplotlib.use () deve essere chiamato * prima di * pylab, matplotlib.pyplot, *

Questo risolverà tutti i problemi di visualizzazione


15

Ho trovato che questo frammento funziona bene quando si passa da un ambiente X a un altro 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

A mio avviso, questa è una soluzione superiore a quella accettata, sebbene non risponda direttamente alla domanda e risponda a una domanda non posta.
Daisuke Aramaki,

14

Quando si accede al server per eseguire il codice, utilizzare invece questo:

ssh -X username@servername

la -Xsi sbarazzarsi del nome senza display e senza $ DISPLAY errore variabile d'ambiente

:)


1
Devo usare '-X' per salvare l'immagine .png. Grazie molto.
nn.

Questo fallirà per un lungo processo se ssh scade o se devi disconnetterti per qualsiasi motivo. Si noti che può verificarsi anche un timeout se il client di connessione entra in modalità di sospensione.
posdef,

È possibile prevenire i timeout aggiungendo -o ServerAliveCountMax=120 -o ServerAliveInterval=30che farà sì che il client ssh invii un pacchetto vuoto ogni 30 secondi per un massimo di 1 ora.
Alex,

5

Su quale sistema sei? Sembra che tu abbia un sistema con X11, ma la variabile di ambiente DISPLAY non è stata impostata correttamente. Prova a eseguire il comando seguente e quindi a rieseguire il programma:

export DISPLAY=localhost:0

ma perché è necessario impostare una variabile di visualizzazione, sono connesso in remoto a questo server, tutto ciò che dovrebbe fare è generare un file PNG ???
krisdigitx,

1
@krisdigitx, se si è connessi in remoto, non impostare una variabile di visualizzazione; usa invece il flag "-XY" quando ti connetti. Per poterlo visualizzare, deve sapere a quale Xserver inviare l'immagine; in questo caso, sarebbe il display del tuo computer, anziché del computer remoto. L'uso del flag "-XY" fa sì che SSH imposti automaticamente la variabile DISPLAY in modo che punti al display del computer collegato.
Michael Aaron Safyan,

@krisdigitx, sono d'accordo, è molto strano che lo faccia; la mia ipotesi, tuttavia, è che dipinge l'immagine usando X11, e quindi salva il risultato usando X11.
Michael Aaron Safyan,

L'uso di questa impostazione per $ DISPLAY non funziona su EC2 con Ubuntu 16 - impossibile connettersi per visualizzare "localhost: 0"
PabTorre,

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

Per me funziona.


3

Un'altra cosa da verificare è se l'utente corrente è autorizzato a connettersi al display X. Nel mio caso, a root non è stato permesso di farlo e matplotlib si lamentava dello stesso errore.

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

fonte: http://www.debian-administration.org/articles/494 https://debian-administration.org/article/494/Getting_X11_forwarding_through_ssh_working_after_running_su


2

Per essere sicuro che il tuo codice sia portatile su Windows, Linux e OSX e per i sistemi con e senza display, suggerirei il seguente frammento:

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

Credito: https://stackoverflow.com/a/45756291/207661


1

Per Google Cloud Machine Learning Engine:

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

E quindi per stampare su file:

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

e per creare il PDF:

multi_page(report_name)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.