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
krisdigitx
fonte
3
possível duplicata de gráficos matplotlib Geradoras sem um servidor X rodando
Jouni K. Seppänen
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

Reinout van Rees
fonte
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.

Chris.Q
fonte
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.

gotgenes
fonte
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')
user3282611
fonte
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

Somum
fonte
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
Matthias123
fonte
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

:)

rajol kochlashvili
fonte
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
Michael Aaron Safyan
fonte
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.

Qing En
fonte
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

Alex
fonte
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

Shital Shah
fonte
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)
Kim Miller
fonte