Generieren eines PNG mit matplotlib, wenn DISPLAY nicht definiert ist

319

Ich versuche, networkx mit Python zu verwenden. Wenn ich dieses Programm starte, wird dieser Fehler angezeigt. Fehlt etwas?

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

Ich erhalte jetzt einen anderen Fehler:

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

Ich erhalte jetzt einen anderen Fehler:

#!/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
quelle
3
Mögliches Duplikat der Generierung von Matplotlib-Diagrammen ohne laufenden X-Server
Jouni K. Seppänen
9
Verschieben Sie den Aufruf von matplotlib.use ('Agg') über Ihre anderen Importe. Dies sollte insbesondere vor dem Import von matplotlib.pyplot
Ivo Bosticky am
@IvoBosticky Kommentar hat es auch für mich gelöst: Das einzige, was irreführend ist, ist "über Ihren anderen Importen". Es sollte offensichtlich sein, dass Sie matplotlib vorher importieren müssen ... Dies ist die gesamte Einstellung, die für mich funktioniert hat: import matplotlib // matplotlib.use ('Agg') // importiere matplotlib.pyplot als plt
mrk

Antworten:

518

Das Hauptproblem ist, dass matplotlib (auf Ihrem System) standardmäßig ein x-using-Backend auswählt. Ich hatte gerade das gleiche Problem auf einem meiner Server. Die Lösung für mich bestand darin, den folgenden Code an einer Stelle hinzuzufügen, die vor jedem anderen Import von Pylab / Matplotlib / Pyplot gelesen wird :

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

Die Alternative besteht darin, es in Ihrem .matplotlibrc einzustellen

Reinout van Rees
quelle
182
Wichtiger Hinweis: .use muss aufgerufen werden, bevor pyplot importiert wird. Wenn Sie beispielsweise nur versuchen, Pyplot zu importieren, müssen Sie zuerst matplotlib importieren, use aufrufen und dann pyplot importieren.
Seaotternerd
8
Der obige Kommentar wird durch diese Antwort näher erläutert .
Ioannis Filippidis
2
Wie stellen Sie es in Ihrem .matplotlibrc ein?
Tommy.carstensen
18
backend: aggin ~/.config/matplotlib'/matplotlibrc(als Beispiel siehe unten: //matplotlib.org/faq/troubleshooting_faq.html#locating-matplotlib-config-dir). Siehe auch matplotlib.org/users/customizing.html , das unten auf der Seite eine Beispielkonfigurationsdatei enthält. Suchen Sie auf dieser Seite nach "agg" und Sie sehen die Konfigurationsoption, die Sie benötigen.
Reinout van Rees
4
Als Referenz finden Sie hier den Link zur Matplotlib-Dokumentation, die dies erklärt. (+1, tolle Antwort, hat mir perfekt geholfen!)
Tim S.
72

Nur als Ergänzung zu Reinouts Antwort.

Die dauerhafte Möglichkeit, diese Art von Problem zu lösen, besteht darin, die .matplotlibrc-Datei zu bearbeiten. Finden Sie es über

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

Ändern Sie dann das Backend in dieser Datei in backend : Agg. Das ist es.

Chris.Q
quelle
5
Pro-Tipp: Stellen Sie $MATPLOTLIBRCdas Verzeichnis ein, in das Sie Ihr eigenes Matplotlibrc werfen möchten.
Kenneth Hoste
Ein bisschen übertrieben für ein Problem wie dieses, aber ich denke, wenn der Server immer kopflos arbeitet, ist es sinnvoll, eine Konfigurationsdatei zu ändern. Hätte dies irgendwelche Nebenwirkungen auf die Funktionsweise von Matplotlib?
BruceJohnJennerLawso
Ich führe matplotlib auf einem Webserver aus, daher war dies die Antwort für mich. Ich habe keine Nebenwirkungen bemerkt.
Spitz
42

Die saubere Antwort ist, sich ein wenig Zeit zu nehmen, um Ihre Ausführungsumgebung richtig vorzubereiten.

Die erste Technik müssen Sie Ihre Ausführungsumgebung vorzubereiten , ist eine verwenden matplotlibrcDatei, wie klug von Chris F. empfohlen , Einstellung

backend : Agg

in dieser Datei. Sie können sogar ohne Codeänderungen steuern, wie und wo matplotlib die matplotlibrcDatei sucht und findet .

Die zweite Technik, die Sie zur Vorbereitung Ihrer Ausführungsumgebung benötigen, besteht darin, die MPLBACKENDUmgebungsvariable zu verwenden (und Ihre Benutzer zu informieren, diese zu verwenden):

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

Dies ist praktisch, da Sie nicht einmal eine andere Datei auf der Festplatte bereitstellen müssen, damit dies funktioniert. Ich habe diesen Ansatz beispielsweise beim Testen der kontinuierlichen Integration und beim Ausführen auf Remotecomputern ohne Anzeige angewendet.

Das Hardcodieren Ihres Matplotlib-Backends auf "Agg" in Ihrem Python-Code ist wie das Schlagen eines quadratischen Stifts in ein rundes Loch mit einem großen Hammer, wenn Sie stattdessen einfach Matplotlib hätten sagen können, dass es ein quadratisches Loch sein muss.

gotgenes
quelle
Die zweite Technik scheint in dieser Situation die eleganteste zu sein.
Dmitry Kabanov
Die Verwendung von MPLBACKEND hat es für mich gelöst. Auf jeden Fall der eleganteste Weg!
SaturnFromTitan
41

Ich habe den Fehler bei der Verwendung von matplotlib über Spark erhalten. matplotlib.use('Agg')funktioniert bei mir nicht Am Ende funktioniert der folgende Code für mich. Mehr hier

import matplotlib.pyplot as plt.
plt.switch_backend('agg')
user3282611
quelle
Dies funktioniert hervorragend, ohne die Einschränkungen für die Reihenfolge, in der Matplotlib und andere Bibliotheken importiert werden.
PabTorre
Mussten Sie beim Ausführen auf Spark die Ausführung auf dem Hauptknoten einschränken oder haben Sie dies beim Ausführen auf Worker-Knoten zum Laufen gebracht?
Saca
Ich benutze dies in einem Django-Projekt und nur so konnte ich es zum Laufen bringen.
HenryM
31

Ich werde nur wiederholen, was @Ivo Bosticky gesagt hat, was übersehen werden kann. Setzen Sie diese Zeilen an den SEHREN Anfang der py-Datei.

import matplotlib
matplotlib.use('Agg') 

Oder man würde Fehler bekommen

* / usr / lib / pymodules / python2.7 / matplotlib / __ init__.py:923: UserWarning: Dieser Aufruf von matplotlib.use () hat keine Auswirkung
weil das Backend bereits ausgewählt wurde;
matplotlib.use () muss * vor * pylab, matplotlib.pyplot, * aufgerufen werden

Dadurch werden alle Anzeigeprobleme behoben

Somum
quelle
15

Ich fand, dass dieses Snippet gut funktioniert, wenn zwischen X- und No-X-Umgebungen gewechselt wird.

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
quelle
Meiner Meinung nach ist dies eine überlegene Lösung als die akzeptierte, obwohl sie die Frage nicht direkt beantwortet und eine nicht gestellte Frage beantwortet.
Daisuke Aramaki
14

Wenn Sie sich beim Server anmelden, um den Code auszuführen, verwenden Sie stattdessen Folgendes:

ssh -X username@servername

das -Xwird die keine Anzeige loswerden Namen erhalten und keine $ DISPLAY Umgebungsvariable Fehler

:) :)

Rajol Kochlashvili
quelle
1
Ich muss '-X' verwenden, um das PNG-Bild zu speichern. Danke vielmals.
Nr.
Dies schlägt für einen langen Prozess fehl, wenn ssh eine Zeitüberschreitung aufweist oder wenn Sie aus irgendeinem Grund die Verbindung trennen müssen. Beachten Sie, dass ein Timeout sogar auftreten kann, wenn der verbindende Client in den Ruhezustand wechselt.
Posdef
Sie können Zeitüberschreitungen verhindern, indem Sie hinzufügen -o ServerAliveCountMax=120 -o ServerAliveInterval=30, dass der SSH-Client maximal 1 Stunde lang alle 30 Sekunden ein leeres Paket sendet.
Alex
5

Auf welchem ​​System bist du? Es sieht so aus, als hätten Sie ein System mit X11, aber die Umgebungsvariable DISPLAY wurde nicht richtig eingestellt. Führen Sie den folgenden Befehl aus und führen Sie das Programm erneut aus:

export DISPLAY=localhost:0
Michael Aaron Safyan
quelle
Aber warum muss eine Anzeigevariable festgelegt werden? Ich bin remote an diesem Server angemeldet. Alles, was es tun sollte, ist eine PNG-Datei zu generieren.
krisdigitx
1
@krisdigitx, wenn Sie remote verbunden sind, legen Sie keine Anzeigevariable fest. Verwenden Sie stattdessen das Flag "-XY", wenn Sie eine Verbindung herstellen. Zum Anzeigen muss bekannt sein, an welchen Xserver das Bild gesendet werden soll. In diesem Fall ist dies die Anzeige Ihres Computers anstelle des Remotecomputers. Die Verwendung des Flags "-XY" bewirkt, dass SSH die Variable DISPLAY automatisch so einstellt, dass sie auf die Anzeige des Verbindungscomputers zeigt.
Michael Aaron Safyan
@krisdigitx, ich stimme zu, es ist sehr seltsam, dass es das tut; Ich vermute jedoch, dass das Bild mit X11 gemalt und das Ergebnis dann mit X11 gespeichert wird.
Michael Aaron Safyan
Die Verwendung dieser Einstellung für $ DISPLAY funktioniert nicht unter EC2 unter Ubuntu 16 - es konnte keine Verbindung hergestellt werden, um "localhost: 0"
anzuzeigen
5
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

Für mich geht das.

Qing En
quelle
3

Sie müssen auch prüfen, ob Ihr aktueller Benutzer berechtigt ist, eine Verbindung zum X-Display herzustellen. In meinem Fall durfte root das nicht und matplotlib beschwerte sich mit demselben Fehler.

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

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

Alex
quelle
2

Um sicherzustellen, dass Ihr Code unter Windows, Linux und OSX sowie für Systeme mit und ohne Anzeige portierbar ist, würde ich folgendes Snippet vorschlagen:

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

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

Shital Shah
quelle
1

Für die Google Cloud Machine Learning Engine:

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

Und dann zum Drucken in eine Datei:

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

und um das PDF zu erstellen:

multi_page(report_name)
Kim Miller
quelle