Seite 1 von 3
Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 10:34
von mathman
Guten Morgen
aus meiner Berechnung kann ich bis jetzt einen Plot mittels mathplotlib erzeugen und der wird bis jetzt in einem eigenen Fenster (ist scheinbar bei mathplotlib dabei) ausgegeben.
Jetzt möchte ich den Plot als PNG in meine html Seite die ich mittels Bottel erzeuge einbauen. Leider bekomme ich es nicht hin die Ausgabe dort einzubauen.
Code: Alles auswählen
import numpy
import matplotlib.pyplot as plt
...
#Ausgabe als Plot
#Plot x,y
plt.plot(ecz[:,0] , (ecz[:,2])*-1)
plt.ylabel('Moment [kN/m]')
plt.xlabel('Laenge [m]')
plt.title('Momentenverlauf')
plt.grid() # Gitternetz zeichnen
plt.show()
Jetzt habe ich mir das Buch "Matplotlib for Python Developers" gekauft um ein wenig mehr zu erfahren. Leider hilft mir das Kapitel über Webframework nicht weiter, weil da u.A. auf Django eingegangen wird. Gibt es irgendwo ein Tutorial oder kann mir jemand einen Ansatz geben wie man so einen Plot in die Webseite einbauen kann?
Gruß
Mathman
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 10:43
von BlackJack
@mathman: Du müsstest halt die Grafik nicht anzeigen -- da hat ein Benutzer ja nichts von, wenn das Diagramm auf dem Server angezeigt wird -- sondern über Bottle eine URL zu einem PNG mit dem Diagramm routen. Mit `pyplot.savefig()` kann man das Diagramm in verschiedenen Formaten speichern. Dabei muss man nicht zwingend in eine Datei speichern, sondern man kann auch ein dateiähnliches Objekt angeben. Also zum Beispiel `StringIO` aus der Standardbibliothek. Dessen Inhalt müsste man dann nur noch mit dem passenden MIME-Type an den Browser ausliefern.
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 14:16
von mathman
Vielen Dank für deine Antwort.
ich habe nun versucht mir was zusammen zu reimen aus deiner Aussage und meinem Buch.
Code: Alles auswählen
def schnittkraefte(traegertyp, I, Ecm, l1, l2, l3, g, q1, q2, q3):
import sys
import numpy
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
...
#Ausgabe als Plot
#Plot x,y
plt.plot(ecz[:,0] , (ecz[:,2])*-1)
plt.ylabel('Moment [kN/m]')
plt.xlabel('Laenge [m]')
plt.title('Momentenverlauf')
plt.grid() # Gitternetz zeichnen
#plt.show()
print "Content-Type: image/png\n"
plt.savefig(sys.stdout, format='png')
In der Datei (bzw. Funktion) generiere ich den Plot. In einer weiteren Datei lasse ich Bottel
laufen.
Code: Alles auswählen
from bottle import route, run, post, request
from schnitt_test import schnittkraefte
@route('/')
@route('/index.html')
def index():
@post('/berechnung')
def berechnung():
from schnitt_test import schnittkraefte
return schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
run(host='localhost', port=8080)
Wenn ich nun im Webbrowser auf die URL "
http://localhost:8080/berechnung" zugreife, passiert erst einmal nichts. Danach gucke ich in meine Konsole (siehe Bildschirmfoto) und dann wird da ein kryptischer Text ausgegeben was wahrscheinlich den Plot darstellt. Wie bekomme ich nun den Plot in den Browser bzw. ich möchte ja später den Plot und Text dort auf der Seite haben?
Gruß
***Nachtrag***
wahrscheinlich muss man das Bild noch in html verknüpfen mit
<img src="/img/test.png" /> aber wie ist der pfad dann zur Datei die eigentlich im Speicher ist ?
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 14:32
von frabron
Na, in deiner Bottle-Anwendung sind ja gleich mehrere Sachen drin, die für den Fehlschlag verantwortlich sein können. Deine IDE lasse ich jetzt mal aussen vor, bemerke nur am Rande, dass die auch zu Problemen mit der Ausführung von Python Skripten führen können.
Deine index-Route ist keine gültige Python-Funktion, da gehört afaik noch mindestens ein pass drunter.
Desweiteren benutzt du für deine Berechnungsroute den POST-Dekorator, d.h. die Route ist nur über Post verfügbar, und nicht über GET, so wie du es probiert hast. Wahrscheinlich musst du auch noch die passenden Header mitschicken, damit der Browser erkennt, dass da ein Bild kommt und kein Html/Text. Der doppelte Import von schnittkraefte ist auch nicht notwendig.
Gruß
Frank
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 14:46
von mathman
Vielen Dank
habe die Bottel Anwendung angepasst
Code: Alles auswählen
from bottle import route, run, post, request
@route('/')
@route('/index.html')
def index():
from schnitt_test import schnittkraefte
return schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
run(host='localhost', port=8080)
Danach habe ich dies dann im Terminal gestartet und das gleich Problem ist aufgetreten, es wird der Plot in der Konsole "angezeigt" und nicht mit geschickt.
Wenn ich in der Funktion ein print "Content-Type: image/png\n" mit schicke, sollte der Browser doch eigentlich wissen das da nen Bild kommt ?
gruß
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 14:58
von Hyperion
mathman hat geschrieben:
habe die Bottel Anwendung angepasst
Es heißt bott
le
(SCNR, weil Du es wiederholt falsch geschrieben hast, sonst wäre ich von einem Flüchtgkeitsfehler ausgegangen!)
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 16:00
von frabron
Deine schnittkraefte-Funktion darf schon mal nix mit print ausgeben, ist ja kein Wunder, dass du eine Ausgabe in deiner Konsole siehst. Desweiteren empfehle ich
http://bottle.paws.de/docs/dev/tutorial ... ng-content zum Einlesen.
Der Header der in der schnittkraefte-Funktion steht, gehört in den View, und (ohne Kenntnis von Matplotlib zu haben) musst du wahrscheinlich plt.savefig(sys.stdout, format='png') als Returnwert der Funktion behandeln.
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 17:10
von mathman
frabron hat geschrieben:Deine schnittkraefte-Funktion darf schon mal nix mit print ausgeben, ist ja kein Wunder, dass du eine Ausgabe in deiner Konsole siehst. Desweiteren empfehle ich
http://bottle.paws.de/docs/dev/tutorial ... ng-content zum Einlesen.
Der Header der in der schnittkraefte-Funktion steht, gehört in den View, und (ohne Kenntnis von Matplotlib zu haben) musst du wahrscheinlich plt.savefig(sys.stdout, format='png') als Returnwert der Funktion behandeln.
Was ist denn der View?
Code: Alles auswählen
plt.plot(ecz[:,0] , (ecz[:,2])*-1)
plt.ylabel('Moment [kN/m]')
plt.xlabel('Laenge [m]')
plt.title('Momentenverlauf')
plt.grid() # Gitternetz zeichnen
return "Content-Type: image/png\n"
return plt.savefig(sys.stdout, format='png')
Jedenfalls wird nun der Plot in der Konsole nicht mehr ausgegeben, aber leider auch kein Plot auf der Seite. Das einzige was im Quellcode der Seite nun steht ist "Content-Type: image/png "
*nachtrag*
Habe mir eben den Abschnitt "Generating content" durchgelesen und davon trifft dann der "File objects" Teil zu. Wenn ich das richtig verstanden habe, wird der Dateityp nicht mitgesendet (was wahrscheinlich den View Teil darstellt). Im weiteren Teil wird dann auf die statischen Dateien hingewiesen. Da sich der Plot aber dynamisch erstellt hab ich leider keine Statische Datei womit ich das Problem lösen könnte oder ich habs nicht richtig verstanden

Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 18:07
von /me
mathman hat geschrieben:
Code: Alles auswählen
return "Content-Type: image/png\n"
return plt.savefig(sys.stdout, format='png')
Ich weiß jetzt nicht genau wie bottle funktioniert, aber wenn du erwartest, dass nach einem return-Statement noch weiter folgender Code ausgeführt wird, dann hast du ein grundsätzliches Verständnisproblem.
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 18:29
von mathman
/me hat geschrieben:mathman hat geschrieben:
Code: Alles auswählen
return "Content-Type: image/png\n"
return plt.savefig(sys.stdout, format='png')
Ich weiß jetzt nicht genau wie bottle funktioniert, aber wenn du erwartest, dass nach einem return-Statement noch weiter folgender Code ausgeführt wird, dann hast du ein grundsätzliches Verständnisproblem.
Gut das return nur einmal vorkommen darf, wusste ich nicht
Wenn ich nun den Content Type auskommentiere bekomme ich wieder den Plot in der Konsole :K
**Nachtrag**
ich habe die letzte Zeile wie folgt geschrieben
return plt.savefig('test.png', format='png')
dabei wird mir ein wunderschöner Plot auf die Festplatte geschrieben. Also bis dahin funktioniert es. Es muss lediglich das Bild nur im flüchtigen Speicher bleiben, in Bottle ankommen und beim Nutzer auf der Webseite angezeigt werden :K
Re: Matplotlib und Bottel
Verfasst: Mittwoch 26. Januar 2011, 19:58
von Leonidas
mathman hat geschrieben:/me hat geschrieben:mathman hat geschrieben:
Code: Alles auswählen
return "Content-Type: image/png\n"
return plt.savefig(sys.stdout, format='png')
Ich weiß jetzt nicht genau wie bottle funktioniert, aber wenn du erwartest, dass nach einem return-Statement noch weiter folgender Code ausgeführt wird, dann hast du ein grundsätzliches Verständnisproblem.
Gut das return nur einmal vorkommen darf, wusste ich nicht
Das ist ja auch eine falsche Folgerung. Nach einem ``return`` kehrt die Funktion zurück, das heißt aber nicht dass ``return`` nur einmal Vorkommen darf:
Code: Alles auswählen
if farbe == "grün":
return "Python"
# hier bringt weiterer Code nichts
else:
return "Nicht Python"
# hier wird weiterer Code ebenfalls ignoriert
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 10:10
von mathman
Ok, die return Anweisung gibt als zum Schluß den Rückgabewert zurück.
In meinem Buch womit ich arbeite (Python 3, Das umfassende Handbuch) ist sys.stdout nur angerissen, aber ich hoffe ich habe es halbwegs verstanden. In meiner Returnanweisung habe ich als Speicherort des Bildes sys.stdout an gegeben, somit müsste doch eigentlich das Bild im Speicher sein ? Wenn ich dafür einen Dateinamen rein schreibe wird es wunderbar auf die Festplatte geschrieben.
Code: Alles auswählen
def schnittkraefte(traegertyp, I, Ecm, l1, l2, l3, g, q1, q2, q3):
import sys
import numpy
import matplotlib
import matplotlib.pyplot as plt
...
#Ausgabe als Plot
#Plot x,y
plt.plot(ecz[:,0] , (ecz[:,2])*-1)
plt.ylabel('Moment [kN/m]')
plt.xlabel('Laenge [m]')
plt.title('Momentenverlauf')
plt.grid() # Gitternetz zeichnen
return plt.savefig(sys.stdout, format='png')
Ich rufe die Funktion mittels meinem Testprogramm auf
Code: Alles auswählen
from bottle import route, run, post
@route('/')
@route('/index.html')
def index():
from schnitt_test import schnittkraefte
return schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
run(host='localhost', port=8080)
Demnach sollte doch der Rückgabewert der Funktion direkt in der Webseite erscheinen, jedoch wird die Ausgabe in die Konsole geschrieben :K
Wie kann man denn das Bild direkt an die Webseite schicken ?
gruß
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 10:51
von Rebecca
Die Ausgabe wird in die Konsole geschrieben, weil print und sys.stdout nunmal in die Konsole schreiben. BlackJack hat ja schon auf StringIO hingewiesen: Du musst dir solch ein Objekt anlegen, das an savefig uebergeben und es dann returnen.
Im Aufrufer der Funktion kannst du dann daraus den Inhalt als String lesen.
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 11:01
von Zap
Hallo mathman. Ich habe bisher weder mit matplotlib noch mit bottle gearbeitet.
Aber was du da machst, macht meines Erachtens keinen Sinn.
Auf die Konsole schreiben heist nicht in den Speicher schreiben.
Versuche mal folgendes.
Code: Alles auswählen
from StringIO import StringIO
def schnittkraefte(...):
#...
io = StringIO()
plt.savefig(io, format='png')
return io.getvalue()
Nicht vergessen in der Doku von Bottle nachzugucken wie man den MIME-Type für den Response setzt.
Der client muss ja schließlich wissen das dort kein normaler Text kommt sondern ein Bild.
PS: Rebecca war schneller, ich wollte meinen Text aber einfach wieder wegschmeissen

Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 13:15
von mathman
Zap hat geschrieben:Hallo mathman. Ich habe bisher weder mit matplotlib noch mit bottle gearbeitet.
Aber was du da machst, macht meines Erachtens keinen Sinn.
Auf die Konsole schreiben heist nicht in den Speicher schreiben.
Versuche mal folgendes.
Code: Alles auswählen
from StringIO import StringIO
def schnittkraefte(...):
#...
io = StringIO()
plt.savefig(io, format='png')
return io.getvalue()
Nicht vergessen in der Doku von Bottle nachzugucken wie man den MIME-Type für den Response setzt.
Der client muss ja schließlich wissen das dort kein normaler Text kommt sondern ein Bild.
PS: Rebecca war schneller, ich wollte meinen Text aber einfach wieder wegschmeissen

Vielen Dank
ich hab mir in der Zwischenzeit ein weiteres Buch zu Gemüt geführt und wollte was zu StringIO erfahren. Stand zwar was drinne, aber war auch nicht die Welt ...
Jedenfalls hab ich mich in der Zwischenzeit mit StringIO versucht.
War auch fast so wie dein Lösungsvorschlag, außer das das ich den Rückgabewert nicht mit getvalue() gemacht hatte und dies von Bottel ausführen lassen wollte. Aufjedenfall kommt jetzt schon mal was außerhalb der Funktion an
Code: Alles auswählen
def schnittkraefte(traegertyp, I, Ecm, l1, l2, l3, g, q1, q2, q3):
import sys
import numpy
import matplotlib
#soll Antigrain Enginge zum Rendern nutzen
matplotlib.use('Agg')
import matplotlib.pyplot as plt
#Lib fuer Ramdatei
from StringIO import StringIO
...
#Ausgabe als Plot
#Plot x,y
s = StringIO()
plt.plot(ecz[:,0] , (ecz[:,2])*-1)
plt.ylabel('Moment [kN/m]')
plt.xlabel('Laenge [m]')
plt.title('Momentenverlauf')
plt.grid() # Gitternetz zeichnen
#return plt.savefig(sys.stdout, format='png')
plt.savefig(s, format='png')
return s.getvalue()
Das sollte schon funktioniere, jetzt ist das Problem auf Bottle zu verlagern.
Code: Alles auswählen
from bottle import route, run, static_file, debug
@route('/')
@route('/index.html')
def index():
from schnitt_test import schnittkraefte
s = schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
return static_file(s, mimetype='image/png')
debug(True)
run(host='localhost', port=8080)
Da kommt es zu einem Fehler 500
Error 500: Internal Server Error
Sorry, the requested URL
http://localhost:8080/ caused an error:
Unhandled exception
Exception:
TypeError('static_file() takes at least 2 non-keyword arguments (1 given)',)
Traceback:
Traceback (most recent call last):
File "/home/mkoenig/workspace/sb2brain/src/bottle.py", line 499, in handle
return handler(**args)
File "/home/mkoenig/workspace/sb2brain/src/web_test_plot.py", line 10, in index
return static_file(s, mimetype='image/png')
TypeError: static_file() takes at least 2 non-keyword arguments (1 given)
Im Tutorial von Bottle wird auf statische Dateien eingegangen die man mittels
der Funktion "static_file(filename, root='/path/to/image/files', mimetype='image/png')"
an den Browser verschicken kann. Das habe ich probiert, außer das ich den root Pfad weg gelassen habe, da es keinen gibt da das Bild ja in der Ramdisk ist.
Jedenfalls bin ich überfragt wie man das Bild jetzt in den Browser bekommt.
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 14:04
von Rebecca
Warum kommst du jetzt auf static_file, wo du doch nun extra matplotlib dazu gebracht hast, KEINE Datei zu speichern?
sowas wie (ungetestet)
Code: Alles auswählen
def index():
from schnitt_test import schnittkraefte
s = schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
return "Content-Type: image/png\n" + s
sollte es doch tun.
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 14:30
von mathman
Rebecca hat geschrieben:Warum kommst du jetzt auf static_file, wo du doch nun extra matplotlib dazu gebracht hast, KEINE Datei zu speichern?
sowas wie (ungetestet)
Code: Alles auswählen
def index():
from schnitt_test import schnittkraefte
s = schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
return "Content-Type: image/png\n" + s
sollte es doch tun.
Ich hab gedacht, dass man so den Type übergeben kann.
Wenn ich das so probiere wie du das gesagt hast, bekomme ich das Bild kryptisch ausgegeben (so wies sonst in der Konsole war).
Ich hab dann doch noch eine weitere Frage zu der return Anweisung.
Wenn man eine komplette Seite mit Bildern etc. ausgeben will, muss man sich den String mit Bildern vorher zusammen bauen und dann diesen zurück geben ? Wie würde ich dann in diesem Fall das dynamisch generierte Bild dort unterbringen?
gruß
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 14:59
von Rebecca
Naja, das ist ja schonmal einen Schritt weiter. OK, den Content-Type an den String zu klatschen war offensichtlich Bloedsinn, aber schau dir mal das
Response-Objekt an...
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 15:30
von mathman
Rebecca hat geschrieben:Naja, das ist ja schonmal einen Schritt weiter. OK, den Content-Type an den String zu klatschen war offensichtlich Bloedsinn, aber schau dir mal das
Response-Objekt an...
Vielen Dank für den Hinweis, jetzt funktioniert es endlich
Code: Alles auswählen
from bottle import route, run, static_file, debug, response
@route('/')
@route('/index.html')
def index():
from schnitt_test import schnittkraefte
s = schnittkraefte(3, 2.25, 26700000., 5, 10, 30, 10, 20, 20, 20)
response.headers['Content-Type'] = 'image/png'
return s
debug(True)
run(host='localhost', port=8080)
Jetzt hab ich aber noch weitere Fragen
Wenn das Bild ausgegeben wurde, bleibt das dann im Arbeitsspeicher oder wird das dann automatisch gelöscht? Weil wenn das Programm auf einem Server ständig läuft, wäre ja irgendwann der Arbeitsspeicher voll
Ein weiteres Anliegen von mir, dass ich nicht nur Bilder ausgeben möchte, sondern halt eine gesamte Webseite. Jetzt habe ich den Header verändert, so dass der Browser annimmt dass da nur ein Bild kommt. Wie kann ich denn Bilder und Text verschicken?
gruß
Re: Matplotlib und Bottel
Verfasst: Donnerstag 27. Januar 2011, 18:02
von Leonidas
mathman hat geschrieben:Wenn das Bild ausgegeben wurde, bleibt das dann im Arbeitsspeicher oder wird das dann automatisch gelöscht? Weil wenn das Programm auf einem Server ständig läuft, wäre ja irgendwann der Arbeitsspeicher voll
Das Objekt bleibt so lange im Speicher wie lange es noch Referenzen drauf gibt. Wie jedes Objekt in Python eben
