ModuleNotFoundError: No module named '_tkinter'

Fragen zu Tkinter.
Antworten
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Donnerstag 16. August 2018, 23:35

Hallo ich versuche diesen code hier auf meinen Heroku webserver zuladen Dabei kommt immer diese Fehlermeldung im Log das ganze hat offensichtlich was mit "matplotlib.pyplot" zu tun da wenn ich diesen import auskommentiere wir der Fehler nicht mehr angezeigt. Weiß irgendwer wie ich das Problem lösen kann?

Code: Alles auswählen

from flask import Flask, render_template, url_for, request
import datetime                     #Uhrzeit
import pytz                         #Zeitzone
import csv                          #Csv Datei schreiben
import matplotlib.pyplot as plt     #Csv Daten plotten


app = Flask(__name__)


with open('data/data.csv') as lines:
    reader = csv.reader(lines, delimiter=',')
    tabelle = [
        row[:1] + [float(c) for c in row[1:]]
        for row in reader
    ]

@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html')

@app.route('/test')
def test():
    return render_template('test.html')

@app.route('/getdata', methods=['GET', 'POST'])
def getdata():
    if request.method == 'POST':
        file = open('test.txt', "a")
        data = request.data
        my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
        fmt = "%Y-%m-%d %H:%M:%S"
        for i in data:
                file.write(my_date.strftime(fmt) + "; ")
                file.write(str(data) + '\n')
                file.close() 
        return data
    return render_template('getdata.html')

@app.route('/data/luftfeuchtigkeit')
def luftfeuchtigkeit():

    time = [zeile[0] for zeile in tabelle]
    luftfeuchtedrin = [zeile[1] for zeile in tabelle]
    luftfeuchteausen = [zeile[3] for zeile in tabelle]
    
    fig, ax = plt.subplots()
    ax.plot(time, luftfeuchtedrin, label="Luftfeuchtigkeit drin")
    ax.plot(time, luftfeuchteausen, label="Luftfeuchtigkeit ausen")
    ax.grid(True)
    ax.legend()
    ax.set_title('Luftfeuchtigkeit')
    ax.set_ylabel('Luftfeuchtigkeit in %')
    ax.set_xlabel('Uhrzeit')

    plt.savefig('data/pictures/luftfeuchtigkeit.png', dpi=None, transparent=True)

@app.route('/data/temperatur')
def temperatur():
    time = [zeile[0] for zeile in tabelle]
    tempraturdrin = [zeile[2] for zeile in tabelle]
    temperaturausen = [zeile[4] for zeile in tabelle]

    fig, ax = plt.subplots()
    ax.plot(time, tempraturdrin, label="Temperatur drin")
    ax.plot(time, temperaturausen, label="Temperatur ausen")
    ax.grid(True)
    ax.legend()
    ax.set_title('Temperatur')
    ax.set_ylabel('Temperatur in Celsius')
    ax.set_xlabel('Uhrzeit')

    plt.savefig('data/pictures/temperatur.png', dpi=None, transparent=True)

@app.route('/data/erdfeuchtigkeit')
def erdfeuchtigkeit():
    time = [zeile[0] for zeile in tabelle]
    erdfeuchte = [zeile[5] for zeile in tabelle]

    fig, ax = plt.subplots()
    ax.plot(time, erdfeuchte, label="Erdfeuchtigkeit")
    ax.grid(True)
    ax.legend()
    ax.set_title('Erdfeuchtigkeit')
    ax.set_ylabel('Erdfeuchtigkeit')
    ax.set_xlabel('Uhrzeit')

    plt.savefig('data/pictures/erdfeuchtigkeit.png', dpi=None, transparent=True)

@app.route('/data/lux')    
def lux():
    time = [zeile[0] for zeile in tabelle]
    lux = [zeile[6] for zeile in tabelle]

    fig, ax = plt.subplots()
    ax.plot(time, lux, label="Lichtitensität(lux)")
    ax.grid(True)
    fig.legend()
    ax.set_title('Lichtitensität(lux)')
    ax.set_ylabel('Lux')
    ax.set_xlabel('Uhrzeit')

    plt.savefig('data/pictures/lux.png', dpi=None, transparent=True)
    return render_template('file.html')

if __name__ == '__main__': app.run(debug=True)
Fehler:

Code: Alles auswählen

2018-08-16T22:33:34.442512+00:00 app[web.1]: Traceback (most recent call last):

2018-08-16T22:33:34.442514+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker

2018-08-16T22:33:34.442515+00:00 app[web.1]:     worker.init_process()

2018-08-16T22:33:34.442517+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 129, in init_process

2018-08-16T22:33:34.442518+00:00 app[web.1]:     self.load_wsgi()

2018-08-16T22:33:34.442519+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/workers/base.py", line 138, in load_wsgi

2018-08-16T22:33:34.442524+00:00 app[web.1]:     self.wsgi = self.app.wsgi()

2018-08-16T22:33:34.442525+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi

2018-08-16T22:33:34.442527+00:00 app[web.1]:     self.callable = self.load()

2018-08-16T22:33:34.442528+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 52, in load

2018-08-16T22:33:34.442529+00:00 app[web.1]:     return self.load_wsgiapp()

2018-08-16T22:33:34.442530+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 41, in load_wsgiapp

2018-08-16T22:33:34.442532+00:00 app[web.1]:     return util.import_app(self.app_uri)

2018-08-16T22:33:34.442533+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/util.py", line 350, in import_app

2018-08-16T22:33:34.442534+00:00 app[web.1]:     __import__(module)

2018-08-16T22:33:34.442535+00:00 app[web.1]:   File "/app/app.py", line 5, in <module>

2018-08-16T22:33:34.442537+00:00 app[web.1]:     import matplotlib.pyplot as plt     #Csv Daten plotten

2018-08-16T22:33:34.442538+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/matplotlib/pyplot.py", line 115, in <module>

2018-08-16T22:33:34.442539+00:00 app[web.1]:     _backend_mod, new_figure_manager, draw_if_interactive, _show = pylab_setup()

2018-08-16T22:33:34.442541+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/matplotlib/backends/__init__.py", line 62, in pylab_setup

2018-08-16T22:33:34.442542+00:00 app[web.1]:     [backend_name], 0)

2018-08-16T22:33:34.442543+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/matplotlib/backends/backend_tkagg.py", line 4, in <module>

2018-08-16T22:33:34.442544+00:00 app[web.1]:     from . import tkagg  # Paint image to Tk photo blitter extension.

2018-08-16T22:33:34.442546+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/matplotlib/backends/tkagg.py", line 5, in <module>

2018-08-16T22:33:34.442547+00:00 app[web.1]:     from six.moves import tkinter as Tk

2018-08-16T22:33:34.442548+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/six.py", line 92, in __get__

2018-08-16T22:33:34.442550+00:00 app[web.1]:     result = self._resolve()

2018-08-16T22:33:34.442551+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/six.py", line 115, in _resolve

2018-08-16T22:33:34.442552+00:00 app[web.1]:     return _import_module(self.mod)

2018-08-16T22:33:34.442554+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/six.py", line 82, in _import_module

2018-08-16T22:33:34.442555+00:00 app[web.1]:     __import__(name)

2018-08-16T22:33:34.442556+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/tkinter/__init__.py", line 36, in <module>

2018-08-16T22:33:34.442557+00:00 app[web.1]:     import _tkinter # If this fails your Python may not be configured for Tk

2018-08-16T22:33:34.442558+00:00 app[web.1]: ModuleNotFoundError: No module named '_tkinter'
Benutzeravatar
__blackjack__
User
Beiträge: 1051
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 17. August 2018, 00:20

Man muss, bevor man `pyplot` importiert, ein anderes Backend wählen, damit nicht versucht wird `tkinter` zu importieren:

Code: Alles auswählen

import matplotlib
matplotlib.use('agg')
In dem Code sind noch ein paar komische Sachen. Die Datei sollte nicht auf Modulebene geladen werden. Da gehört in der Regel sowieso nur Code hin der Konstanten, Funktionen, und Klassen definiert. Der Code wird ja nur einmal beim importieren des Moduls ausgeführt.

`getdata()` wird bei POST nicht richtig funktionieren weil die Schleife über die Elemente von `data` keinen Sinn macht, und ausserdem die Datei in die geschrieben wird *in* der Schleife geschlossen wird, wobei es dann beim zweiten Schleifendurchlauf beim schreiben scheitern wird. Dateien mit ``with`` zu verwenden, vermeidet unter anderem auch so einen Fehler.

Ich sehe den Sinn der Zeitzone an der Stelle übrigens auch nicht, denn die wird im weiteren Verlauf ja ignoriert.

Die '/data/…'-Routen enthalten alle sehr viel fast identischen Code und alle bis auf eine liefern gar nichts als Antwort zurück. Der Ansatz da Bilder auf er Platte zu erzeugen funktioniert auch nur wenn man die Anwendung nur alleine und nur aus einem Browserfenster oder Reiter heraus benutzt. Sowie mehr als eine Anfrage nahezu gleichzeitig bearbeitet werden soll, werden die sich da in die Quere kommen. Üblicherweise würde man über so eine Route das Bild direkt als Antwort ausliefern statt es lokal zu speichern.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Freitag 17. August 2018, 09:29

Danke für die Antwort das man matplotlib.use zwischen matplotlib und pyplot schreiben muss wusste ich nicht deshalb hat das wahrscheinlich auch nicht funktioniert als ich es ausprobiert habe.
Danke für die Tipps beim Rest des Codes dann werde ich das ändern und verbessern.
Du meinst ich soll die Bilder gar nicht speichern sondern direkt versenden, wie mache ich das dann am besten, soll ich die in eine Variabel speichern und diese dann da im HTML Code einfügen wo momentan der Pfad drin steht wo die Bilder gespeichert werden. Und für die schlechte Struktur Entschuldigung das ist mein erstes größeres Projekt mit Python.
Sirius3
User
Beiträge: 8265
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 17. August 2018, 10:15

@Hypec: savefig funktioniert auch mit ByteIO-Objekten.

Die Routen `/data/temperatur.png`, etc. bindest Du einfach direkt in HTML ein.
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Freitag 17. August 2018, 14:45

@Sirius3 kannst du mir sagen wo ich mich gut über ByteIO-Objekte einlesen kann, weil des was ich im internet gefunden habe geht mal wieder nicht.
__deets__
User
Beiträge: 3295
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 17. August 2018, 14:54

Wie immer einfach in die Python Dokumentation schauen, da gibt es ein Such-Feld, da kannst du das eingeben.
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Montag 20. August 2018, 17:11

Ich habe jetzt den Code hier Geschrieben mit IO Bytes. Das Problem jetzt ist das auf der Webseite (https://pythonv1.herokuapp.com/data/luftfeuchtigkeit) das ganze jetzt in bytes angezeigt wird.
app.py:

Code: Alles auswählen

from flask import Flask, render_template, url_for, request
import matplotlib 
matplotlib.use('agg') 
import matplotlib.pyplot as plt     #Csv Daten plotten
import datetime                     #Uhrzeit
import pytz                         #Zeitzone
import csv                          #Csv Datei schreiben
import io 



app = Flask(__name__)


@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html')

@app.route('/data/luftfeuchtigkeit')
def luftfeuchtigkeit():

    with open('data/data.csv') as lines:
        reader = csv.reader(lines, delimiter=',')
        tabelle = [
            row[:1] + [float(c) for c in row[1:]]
            for row in reader
        ]

    time = [zeile[0] for zeile in tabelle]
    luftfeuchtedrin = [zeile[1] for zeile in tabelle]
    luftfeuchteausen = [zeile[3] for zeile in tabelle]
    
    fig, ax = plt.subplots()
    ax.plot(time, luftfeuchtedrin, label="Luftfeuchtigkeit drin")
    ax.plot(time, luftfeuchteausen, label="Luftfeuchtigkeit ausen")
    ax.grid(True)
    ax.legend()
    ax.set_title('Luftfeuchtigkeit')
    ax.set_ylabel('Luftfeuchtigkeit in %')
    ax.set_xlabel('Uhrzeit')

    buffer = io.BytesIO()
    plt.savefig(buffer, format = 'png')
    plot_data = buffer.getvalue()


    return render_template('file.html', plot = plot_data)
file.html:

Code: Alles auswählen

{% extends "base.html" %}
{% block content %}
    <h1>Gewächshaus</h1>
    <h2>Test </h2>
    <p>
        only a Test {{plot}}
    </p>
{% endblock %}
io:

Code: Alles auswählen

# New I/O library conforming to PEP 3116.

__author__ = ("Guido van Rossum <guido@python.org>, "
              "Mike Verdone <mike.verdone@gmail.com>, "
              "Mark Russell <mark.russell@zen.co.uk>, "
              "Antoine Pitrou <solipsis@pitrou.net>, "
              "Amaury Forgeot d'Arc <amauryfa@gmail.com>, "
              "Benjamin Peterson <benjamin@python.org>")

__all__ = ["BlockingIOError", "open", "IOBase", "RawIOBase", "FileIO",
           "BytesIO", "StringIO", "BufferedIOBase",
           "BufferedReader", "BufferedWriter", "BufferedRWPair",
           "BufferedRandom", "TextIOBase", "TextIOWrapper",
           "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END"]


import _io
import abc

from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation,
                 open, FileIO, BytesIO, StringIO, BufferedReader,
                 BufferedWriter, BufferedRWPair, BufferedRandom,
                 IncrementalNewlineDecoder, TextIOWrapper)

OpenWrapper = _io.open # for compatibility with _pyio

# Pretend this exception was created here.
UnsupportedOperation.__module__ = "io"

# for seek()
SEEK_SET = 0
SEEK_CUR = 1
SEEK_END = 2

# Declaring ABCs in C is tricky so we do it here.
# Method descriptions and default implementations are inherited from the C
# version however.
class IOBase(_io._IOBase, metaclass=abc.ABCMeta):
    __doc__ = _io._IOBase.__doc__

class RawIOBase(_io._RawIOBase, IOBase):
    __doc__ = _io._RawIOBase.__doc__

class BufferedIOBase(_io._BufferedIOBase, IOBase):
    __doc__ = _io._BufferedIOBase.__doc__

class TextIOBase(_io._TextIOBase, IOBase):
    __doc__ = _io._TextIOBase.__doc__

RawIOBase.register(FileIO)

for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom,
              BufferedRWPair):
    BufferedIOBase.register(klass)

for klass in (StringIO, TextIOWrapper):
    TextIOBase.register(klass)
del klass

try:
    from _io import _WindowsConsoleIO
except ImportError:
    pass
else:
RawIOBase.register(_WindowsConsoleIO)
Benutzeravatar
__blackjack__
User
Beiträge: 1051
Registriert: Samstag 2. Juni 2018, 10:21

Montag 20. August 2018, 22:38

@Hypec: Den Anfang des `io`-Moduls aus der Standardbibliothek in den Beitrag zu kopieren macht irgendwie keinen Sinn. Den haben wir ja alle selbst auf der Festplatte. ;-)

Du kannst nicht HTML und Bild gleichzeitig ausliefern. (Also es geht schon als Data-URL, aber das würde niemand in diesem Fall machen.)

Du musst ein HTML ausliefern mit <img> das seine Daten dann aus der Route bezieht, die das Bild generiert und dann auch als Bild ausliefert, das heisst Du musst AFAIK mit `flask.make_response()` selbst eine Antwort aus den Binärdaten erzeugen und auf dem Objekt auch den passenden `content_type` für die Daten setzen.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Montag 20. August 2018, 23:45

Also ich habe jetzt das hier geschrieben und bekomme allerdings kein Bild auf der Webseite (https://pythonv1.herokuapp.com/data)

Code: Alles auswählen

@app.route('/data')
def daten():

    with open('data/data.csv') as lines:
        reader = csv.reader(lines, delimiter=',')
        tabelle = [
            row[:1] + [float(c) for c in row[1:]]
            for row in reader
        ]

    time = [zeile[0] for zeile in tabelle]
    luftfeuchtedrin = [zeile[1] for zeile in tabelle]
    tempraturdrin = [zeile[2] for zeile in tabelle]
    luftfeuchteausen = [zeile[3] for zeile in tabelle]
    temperaturausen = [zeile[4] for zeile in tabelle]
    erdfeuchte = [zeile[5] for zeile in tabelle]
    lux = [zeile[6] for zeile in tabelle]
    
    fig, ax = plt.subplots()
    ax.plot(time, luftfeuchtedrin, label="luftfeuchtedrin")
    ax.plot(time, luftfeuchteausen, label="luftfeuchteausen")
    ax.grid(True)
    ax.legend()
    ax.set_title('Luftfeuchtigkeit')
    ax.set_ylabel('Luftfeuchtigkeit in %')
    ax.set_xlabel('Uhrzeit')

    buffer = io.BytesIO()
    plt.savefig(buffer, format = 'png')
    plot_data = buffer.getvalue()

    with open('data/data.csv') as f:
        response = make_response(plot_data)
        response.headers['Content-type'] = 'image/png'

    return render_template('file.html', plot = response)
Benutzeravatar
__blackjack__
User
Beiträge: 1051
Registriert: Samstag 2. Juni 2018, 10:21

Montag 20. August 2018, 23:54

ich verstehe nicht so ganz was das erneute öffnen der CSV-Datei an der Stelle bringen soll‽

Du musst dort `response` zurückgeben und nicht versuchen das in ein Template einzusetzen welches dann zurückgegeben wird. Das geht so wie schon gesagt nicht. Du kannst nur HTML *oder* Bilddaten ausliefern. Auf *eine* Anfrage bekommt der Browser *eine* Antwort.

Das Template wird an der Stelle wo Du '{{plot}}' verwendest eine Zeichenkettendarstellung von dem Objekt einsetzen. Das macht keinen Sinn.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
Hypec
User
Beiträge: 69
Registriert: Mittwoch 1. August 2018, 16:11

Dienstag 21. August 2018, 00:12

Also ich kann das Bild nicht in das Template einbinden. Ja das am anfang macht keinen sinn das muss ich ändern.
Antworten