Gerando um PNG com matplotlib quando DISPLAY está indefinido


319

Estou tentando usar o networkx com Python. Quando executo este programa, esse erro ocorre. Falta alguma coisa?

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

Agora, recebo um erro diferente:

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

Agora, recebo um erro diferente:

#!/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
Mova a chamada para matplotlib.use ('Agg') acima de suas outras importações, em particular antes da importação do matplotlib.pyplot
Ivo Bosticky

O comentário do @IvoBosticky também resolveu o problema: a única coisa enganosa é "acima das outras importações". Deve ser óbvio que você precisa importar o matplotlib antes ... Essa é toda a configuração que funcionou para mim: importar matplotlib // matplotlib.use ('Agg') // importar matplotlib.pyplot como plt
mrk

Respostas:


518

O principal problema é que (no seu sistema) o matplotlib escolhe um back-end usando x por padrão. Eu apenas tive o mesmo problema em um dos meus servidores. A solução para mim foi adicionar o seguinte código em um local que seja lido antes de qualquer outra importação do pylab / matplotlib / pyplot :

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

A alternativa é configurá-lo no seu .matplotlibrc


182
Nota importante: .use precisa ser chamado antes que o pyplot seja importado. Portanto, se você está, por exemplo, apenas tentando importar o pyplot, precisa importar primeiro o matplotlib, chamar o uso e importar o pyplot.
seaotternerd

8
O comentário acima é explicado mais por esta resposta .
Ioannis Filippidis

2
Como você o "define no seu .matplotlibrc"?
21415 Tommy.carstensen

18
backend: aggem ~/.config/matplotlib'/matplotlibrc(como exemplo, consulte http://matplotlib.org/faq/trou Troubleshooting_faq.html#locating-matplotlib-config-dir). Veja também matplotlib.org/users/customizing.html , que tem um exemplo de arquivo de configuração na parte inferior da página. Encontre "agg" nessa página e você verá a opção de configuração necessária.
Reinout van Rees

4
Para referência, aqui está o link para a documentação do matplotlib que explica isso. (+1, grande resposta, me ajudou perfeitamente!)
Tim S.

72

Apenas como complemento da resposta de Reinout.

A maneira permanente de resolver esse tipo de problema é editar o arquivo .matplotlibrc. Encontre-o via

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

Em seguida, modifique o back-end nesse arquivo para backend : Agg. É isso.


5
Ponta Pro: definir $MATPLOTLIBRCo diretório onde você quer jogar seu próprio matplotlibrc no.
Kenneth Hoste

Meio que exagero em um problema como esse, mas acho que se o servidor estiver sempre funcionando sem cabeça, faz sentido modificar um arquivo de configuração. Isso teria algum efeito colateral no funcionamento do matplotlib?
precisa saber é o seguinte

Estou executando o matplotlib em um servidor web, então essa foi a resposta para mim. Eu não notei nenhum efeito colateral.
Spitz #

42

A resposta limpa é levar um pouco de tempo para preparar corretamente seu ambiente de execução.

A primeira técnica que você precisa para preparar seu ambiente de execução é usar um matplotlibrcarquivo, como sabiamente recomendado por Chris Q. , definindo

backend : Agg

nesse arquivo. Você pode controlar - sem alterações de código - como e onde o matplotlib procura e localiza o matplotlibrcarquivo .

A segunda técnica que você precisa para preparar seu ambiente de execução é usar a MPLBACKENDvariável de ambiente (e informar seus usuários a utilizá-la):

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

Isso é útil porque você nem precisa fornecer outro arquivo no disco para fazer isso funcionar. Empreguei essa abordagem com, por exemplo, testes em integração contínua e execução em máquinas remotas que não possuem monitores.

Codificar seu back-end do matplotlib para "Agg" no código Python é como bater um pino quadrado em um buraco redondo com um grande martelo, quando, em vez disso, você poderia ter dito ao matplotlib que ele precisava ser um buraco quadrado.


A segunda técnica parece a mais elegante nessa situação.
Dmitry Kabanov

Usando MPLBACKEND resolveu isso para mim. Definitivamente a maneira mais elegante!
SaturnFromTitan

41

Eu recebi o erro ao usar o matplotlib através do Spark. matplotlib.use('Agg')não funciona para mim. No final, o código a seguir funciona para mim. Mais aqui

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

Isso funciona muito bem, sem as restrições na ordem usada para importar o matplotlib e outras bibliotecas.
PabTorre 5/09

Ao executar no Spark, você teve que restringir isso para executar no nó principal ou conseguiu que isso funcionasse ao executar nos nós do trabalhador?
Saca 24/09

Estou usando isso em um projeto django e essa foi a única maneira de fazê-lo funcionar.
HenryM

31

Vou repetir o que o @Ivo Bosticky disse que pode ser esquecido. Coloque essas linhas no MUITO início do arquivo de py.

import matplotlib
matplotlib.use('Agg') 

Ou um iria receber erro

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: Aviso do usuário: Esta chamada para matplotlib.use () não tem efeito
porque o back-end já foi escolhido;
matplotlib.use () deve ser chamado * antes de * pylab, matplotlib.pyplot, *

Isso resolverá todos os problemas de exibição


15

Eu achei esse trecho para funcionar bem ao alternar entre ambientes X e não-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

Na minha opinião, esta é uma solução superior à que foi aceita, embora não responda diretamente à pergunta e responda a uma pergunta não feita.
Daisuke Aramaki

14

Ao entrar no servidor para executar o código, use isto:

ssh -X username@servername

a -X irá se livrar do erro de variável de ambiente sem nome de exibição e sem $ DISPLAY

:)


1
Preciso usar '-X' para salvar a imagem .png. Muito Obrigado.
nºs

Isso falhará por um longo processo se o tempo limite do ssh for excedido ou se você precisar se desconectar por qualquer motivo. Observe que um tempo limite pode até ocorrer se o cliente de conexão entrar em suspensão.
posdef 13/12/16

Você pode evitar tempos limite adicionando o -o ServerAliveCountMax=120 -o ServerAliveInterval=30que fará com que o cliente ssh envie um pacote vazio a cada 30 segundos por no máximo 1 hora.
21419 Alex

5

Em que sistema você está? Parece que você possui um sistema com o X11, mas a variável de ambiente DISPLAY não foi definida corretamente. Tente executar o seguinte comando e, em seguida, execute novamente o seu programa:

export DISPLAY=localhost:0

mas por que ele definiu uma variável de exibição, estou conectado remotamente a este servidor, tudo o que deve fazer é gerar um arquivo PNG ???
Krisdigitx

1
@krisdigitx, se você estiver conectado remotamente, não defina uma variável de exibição; em vez disso, use o sinalizador "-XY" ao se conectar. Para exibir, ele precisa saber para qual Xserver enviar a imagem; nesse caso, seria a exibição do seu computador, em vez do computador remoto. O uso do sinalizador "-XY" faz com que o SSH defina a variável DISPLAY automaticamente para apontar para a tela do computador conectado.
Michael Aaron Safyan

@krisdigitx, eu concordo, é muito estranho que faça isso; meu palpite, no entanto, é que ele pinta a imagem usando o X11 e salva o resultado usando o X11.
Michael Aaron Safyan

Usando essa configuração para $ DISPLAY não funciona em EC2 rodando Ubuntu 16 - não poderia ligar para exibir "localhost: 0"
PabTorre

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

Funciona para mim.


3

Outra coisa a verificar é se o usuário atual está autorizado a se conectar ao monitor X. No meu caso, o root não tinha permissão para fazer isso e o matplotlib estava reclamando com o mesmo erro.

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

Para garantir que seu código seja portável no Windows, Linux e OSX e para sistemas com e sem telas, sugiro o seguinte trecho:

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 o Google Cloud Machine Learning Engine:

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

E, em seguida, para imprimir em arquivo:

#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 para criar o PDF:

multi_page(report_name)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.