Generar un PNG con matplotlib cuando DISPLAY no está definido


319

Estoy tratando de usar networkx con Python. Cuando ejecuto este programa, aparece este error. ¿Falta algo?

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

Me sale un error diferente ahora:

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

Me sale un error diferente ahora:

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


99
Mueva la llamada a matplotlib.use ('Agg') por encima de sus otras importaciones, en particular debería ser antes de la importación de matplotlib.pyplot
Ivo Bosticky

El comentario de @IvoBosticky también lo resolvió para mí: lo único que es engañoso es "por encima de sus otras importaciones". Debería ser obvio que necesita importar matplotlib antes ... Esta es la configuración completa que funcionó para mí: import matplotlib // matplotlib.use ('Agg') // import matplotlib.pyplot as plt
mrk

Respuestas:


518

El principal problema es que (en su sistema) matplotlib elige un backend que usa x por defecto. Acabo de tener el mismo problema en uno de mis servidores. La solución para mí fue agregar el siguiente código en un lugar que se lee antes de cualquier otra importación de pylab / matplotlib / pyplot :

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

La alternativa es configurarlo en su .matplotlibrc


182
Nota importante: es necesario llamar a .use antes de importar pyplot. Entonces, si, por ejemplo, solo está tratando de importar pyplot, primero debe importar matplotlib, llamar al uso y luego importar pyplot.
seaotternerd

8
El comentario anterior se explica más en esta respuesta .
Ioannis Filippidis

2
¿Cómo se "configura en su .matplotlibrc"?
tommy.carstensen

18
backend: aggen ~/.config/matplotlib'/matplotlibrc(como ejemplo, consulte http://matplotlib.org/faq/troubleshooting_faq.html#locating-matplotlib-config-dir). Consulte también matplotlib.org/users/customizing.html , que tiene un archivo de configuración de ejemplo en la parte inferior de la página. Busque "agg" en esa página y verá la opción de configuración que necesita.
Reinout van Rees

44
Como referencia, aquí está el enlace a la documentación de matplotlib que explica esto. (+1, gran respuesta, ¡me ayudó perfectamente!)
Tim S.

72

Solo como un complemento de la respuesta de Reinout.

La forma permanente de resolver este tipo de problema es editar el archivo .matplotlibrc. Encuéntralo a través de

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

Luego modifique el backend en ese archivo a backend : Agg. Eso es.


55
Pro consejo: establece $MATPLOTLIBRCque el directorio en el que desea lanzar su propia matplotlibrc en.
Kenneth Hoste

Es un poco excesivo para un problema como este, pero supongo que si el servidor siempre funciona sin cabeza, tiene sentido modificar un archivo de configuración. ¿Tendría esto algún efecto secundario sobre cómo funcionará matplotlib?
BruceJohnJennerLawso

Estoy ejecutando matplotlib en un servidor web, así que esta fue la respuesta para mí. No he notado ningún efecto secundario.
Spitz

42

La respuesta clara es tomarse un poco de tiempo para preparar correctamente su entorno de ejecución.

La primera técnica que tiene para preparar su entorno de ejecución es usar un matplotlibrcarchivo, como lo recomienda sabiamente Chris Q. , estableciendo

backend : Agg

en ese archivo Incluso puede controlar, sin cambios de código, cómo y dónde busca y encuentra el matplotlibrcarchivo matplotlib .

La segunda técnica que tiene para preparar su entorno de ejecución es utilizar la MPLBACKENDvariable de entorno (e informar a sus usuarios para que la utilicen):

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

Esto es útil porque ni siquiera tiene que proporcionar otro archivo en el disco para que esto funcione. He empleado este enfoque con, por ejemplo, pruebas en integración continua y ejecución en máquinas remotas que no tienen pantallas.

Codificar su back-end matplotlib a "Agg" en su código Python es como golpear una clavija cuadrada en un agujero redondo con un gran martillo, cuando, en cambio, podría haberle dicho a matplotlib que debe ser un agujero cuadrado.


La segunda técnica parece la más elegante en esta situación.
Dmitry Kabanov

Usar MPLBACKEND lo resolvió por mí. ¡Definitivamente la forma más elegante!
SaturnFromTitan

41

Recibí el error al usar matplotlib a través de Spark. matplotlib.use('Agg')no funciona para mi Al final, el siguiente código funciona para mí. Más aquí

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

Esto funciona muy bien, sin las restricciones en el orden utilizado para importar matplotlib y otras bibliotecas.
PabTorre

Al ejecutar en Spark, ¿tuvo que restringir esto para que se ejecute en el nodo principal o consiguió que esto funcione cuando se ejecuta en nodos de trabajo?
Saca

Estoy usando esto en un proyecto de django y esta fue la única forma en que pude hacerlo funcionar.
HenryM

31

Solo repetiré lo que dijo @Ivo Bosticky que se puede pasar por alto. Pon estas líneas al comienzo MUY del archivo py.

import matplotlib
matplotlib.use('Agg') 

O uno obtendría un error

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: UserWarning: esta llamada a matplotlib.use () no tiene efecto
porque el backend ya ha sido elegido;
matplotlib.use () debe llamarse * antes * pylab, matplotlib.pyplot, *

Esto resolverá todos los problemas de visualización


15

Encontré que este fragmento funciona bien al cambiar entre entornos X y 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

En mi opinión, esta es una solución superior a la aceptada, aunque no responde directamente a la pregunta y responde a una pregunta no formulada.
Daisuke Aramaki

14

Al iniciar sesión en el servidor para ejecutar el código, use esto en su lugar:

ssh -X username@servername

la -Xva a deshacerse de la pantalla sin nombre y sin $ DISPLAY error variable de entorno

:)


1
Necesito usar '-X' para poder guardar la imagen .png. Muchas gracias.
nos

Esto fallará durante un proceso largo si ssh agota el tiempo de espera o si necesita desconectarse por cualquier motivo. Tenga en cuenta que incluso puede ocurrir un tiempo de espera si el cliente que se conecta se queda dormido.
posdef

Puede evitar tiempos de espera añadiendo lo -o ServerAliveCountMax=120 -o ServerAliveInterval=30que hará que el cliente ssh envíe un paquete vacío cada 30 segundos durante un máximo de 1 hora.
Alex

5

¿En qué sistema estás? Parece que tiene un sistema con X11, pero la variable de entorno DISPLAY no se configuró correctamente. Intente ejecutar el siguiente comando y luego vuelva a ejecutar su programa:

export DISPLAY=localhost:0

pero ¿por qué es necesario establecer una variable de visualización, estoy conectado remotamente a este servidor, todo lo que debería hacer es generar un archivo PNG?
krisdigitx

1
@krisdigitx, si está conectado de forma remota, no configure una variable de visualización; en su lugar use la bandera "-XY" cuando se conecte. Para que se muestre, necesita saber a qué Xserver enviar la imagen; en este caso, sería la pantalla de su computadora, en lugar de la computadora remota. El uso del indicador "-XY" hace que SSH configure la variable DISPLAY automáticamente para que apunte a la pantalla de la computadora conectada.
Michael Aaron Safyan

@krisdigitx, estoy de acuerdo, es muy extraño que lo haga; Sin embargo, supongo que pinta la imagen con X11 y luego guarda el resultado con X11.
Michael Aaron Safyan

Usar esta configuración para $ DISPLAY no funciona en EC2 con Ubuntu 16 - no se pudo conectar para mostrar "localhost: 0"
PabTorre

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

Esto funciona para mi.


3

Otra cosa que debe verificar es si su usuario actual está autorizado para conectarse a la pantalla X. En mi caso, root no tenía permitido hacer eso y matplotlib se quejaba con el mismo error.

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

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


2

Para asegurarse de que su código sea portátil en Windows, Linux y OSX y para sistemas con y sin pantallas, sugeriría el siguiente fragmento:

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

Crédito: https://stackoverflow.com/a/45756291/207661


1

Para Google Cloud Machine Learning Engine:

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

Y luego imprimir a archivo:

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

y para crear el PDF:

multi_page(report_name)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.