Wie bekommt man die Konsolenausgabe eines Python-Skripts in ein Tkinter-Fenster

Fragen zu Tkinter.
Antworten
Felbeeey
User
Beiträge: 2
Registriert: Mittwoch 1. September 2021, 13:09

Hallo zusammen!

Ich bin relativ neu in der Python-Welt.... Ich habe ein kleines Skript mit der Twitch-API gebastelt, das mir anzeigt, wer gerade live ist.
Die Konsolenausgabe gefällt mir schon ganz gut, aber ich würde das Ganze jetzt gerne in einem Tkinter-Fenster haben, um es optisch etwas schöner darzustellen.
Wie bekomme ich die Ausgabe in so ein Tkinter-Fenster? Das Skript ist etwa 65 Zeilen lang, deswegen würde ich es nur ungern ganz verwerfen wollen.

Die Konsolenausgabe sieht in etwa so aus;

Code: Alles auswählen

KANAL			SPIEL			ZUSCHAUER
------------------------------------------------------------------------------- -
cmdkrieger 		Among Us		220
------------------------------------------------------------------------------- -

Hier für mögliche Änderungsvorschläge fürs Tkinter-Fenster der gesamte Quelltext (ohne API-Key):

Code: Alles auswählen

import json
import requests
import sys
from tkinter import *

try:
    reload(sys)
    sys.setdefaultencoding('utf8')
except Exception:
    pass

# oauth.txt
try:
    with open('oauth.txt', 'r') as f:
        oauth = f.read()
# speicher OAuth in oauth.txt
except FileNotFoundError:
    print("Füge den OAuth ein (z.B.: 'yaxb50....')")
    with open('oauth.txt', 'w') as f:
        f.write(input())
    with open('oauth.txt', 'r') as f:
        oauth = f.read()

headers = {
    'Accept': 'application/vnd.twitchtv.v5+json',
    'Client-ID': 'abcdefghijklmnopqrstuvwxyz',
    'Authorization': 'OAuth ' + oauth,
}
try:
    response = requests.get('https://api.twitch.tv/kraken/streams/followed', headers=headers)
    data = response.json()
    numStreams = len(data['streams'])
except (KeyError, ValueError):
    print("Error - stelle sicher dass der Key in oauth.txt korrekt ist!")
    sys.exit(1)

print("\nKANAL " + ' ' * 15 + "SPIEL" + ' ' * 35 + "ZUSCHAUER" + ' ' * 8 + "\n" + '-' * 80)

for i in range(0, numStreams):
    channelName = data["streams"][i]["channel"]["name"]
    channelGame = data["streams"][i]["channel"]["game"]
    channelViewers = str(data["streams"][i]["viewers"])
    streamType = data["streams"][i]["stream_type"]

    # Check ob stream live/vodcast
    if streamType == "live":
        streamType = ""
    else:
        streamType = "(vodcast)"

    # Kanal Name/Spiele
    if len(channelName) > 18:
        channelName = channelName[:18] + ".."
    if len(channelGame) > 38:
        channelGame = channelGame[:38] + ".."

    # Formatieren
    print("{} {} {} {}".format(
        channelName.ljust(20),
        channelGame.ljust(40),
        channelViewers.ljust(8),
        streamType
    ))

    if i == numStreams - 1:
        print('-' * 80)

# Fenster für Konsolenausgabe ist aktuell in Arbeit !
# fenster = Tk()
# fenster.title('Stream0rz')
# fenster.geometry('600x450')
# fenster.mainloop()
Vielen lieben Dank im Voraus für mögliche Ideen oder Lösungen 😊
Beste Grüße, Felbeeey
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Felbeeey: Ich würde sagen neu schreiben, das ist einfacher. GUI-Programme funktionieren anders als Konsolenprogramme.

Anmerkungen zum Quelltext: Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 200 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Das `json`-Modul wird importiert, aber nirgends verwendet.

`reload()` gibt es in Python 3 nicht mehr, und auch in Python 2 hätte man das so nicht machen sollen. Der Code kann weg.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Der "oauth.txt"-Dateiname sollte nur einmal im Quelltext stehen, als Konstante.

Die Datei die man gerade geschrieben hat, sofort wieder zu lesen, macht nicht wirklich Sinn.

Bei der `response` sollte man den Fall behandeln, dass der HTTP-Status nicht Ok war.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Aus `data` wird nur der Wert zum Schlüssel "streams" verwendet, also könnte man sich `data` als Namen sparen und gleich die Liste mit den Streamdaten an einen Namen binden.

Die Ausrichtung der Spalten ist ungünstig kodiert, weil an mehreren Stellen und dann auch noch mit unterschiedlichen Zahlen. Es wäre sinnvoller *eine* Vorlage zu erstellen, die dann für Überschrift und Datenzeilen verwendet wird.

``for i in range(sequence):`` nur um dann mit `i` auf die Elemente von `sequence` zuzugreifen ist in Python ein „anti-pattern“. Man kann direkt über die Elemente von Sequenzen iterieren, ohne den Umweg über einen Laufindex.

`i` wird auch noch verwendet um zu testen ob man sich im Schleifendurchlauf mit dem letzten Element befindet, aber dazu braucht man das auch nicht wirklich. Man kann die Schleife auch einfach nur durchlaufen wenn es Elemente gibt, und die Trennlinie dann nach der Schleife im Code erzeugen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sys
import tkinter as tk
from pathlib import Path

import requests

OAUTH_FILE_PATH = Path("oauth.txt")
CLIENT_ID = "abcdefghijklmnopqrstuvwxyz"


def shorten_text(text, max_length):
    return (
        text[: max_length - 2] + ".." if len(text) > max_length - 2 else text
    )


def main():
    try:
        oauth = OAUTH_FILE_PATH.read_text(encoding="utf-8")
    except FileNotFoundError:
        oauth = input("Füge den OAuth ein (z.B.: 'yaxb50....')\n")
        OAUTH_FILE_PATH.write_text(oauth, encoding="utf-8")

    try:
        response = requests.get(
            "https://api.twitch.tv/kraken/streams/followed",
            headers={
                "Accept": "application/vnd.twitchtv.v5+json",
                "Client-ID": CLIENT_ID,
                "Authorization": "OAuth " + oauth,
            },
        )
        response.raise_for_status()
        streams = response.json()["streams"]
    except (KeyError, ValueError):
        print("Error - stelle sicher dass der Key in oauth.txt korrekt ist!")
        sys.exit(1)

    row_template = "{:<20} {:<40} {:<8} {}"
    print()
    print(row_template.format("KANAL", "SPIEL", "ZUSCHAUER", ""))
    print("-" * 80)

    if streams:
        for stream in streams:
            print(
                row_template.format(
                    shorten_text(stream["channel"]["name"], 20),
                    shorten_text(stream["channel"]["game"], 40),
                    stream["viewers"],
                    "" if stream["stream_type"] == "live" else "(vodcast)",
                )
            )

        print("-" * 80)

    # Fenster für Konsolenausgabe ist aktuell in Arbeit !
    # fenster = tk.Tk()
    # fenster.title("Stream0rz")
    # fenster.mainloop()
Wobei hier noch unschön ist, das die Spaltenbreiten einmal in der `format()`-Vorlage und dann noch mal in den Aufrufen zum kürzen des Texts. Da würde ich auf eine Bibliothek zur Formatierung von Tabellen zurückgreifen. `rich` beispielsweise.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Felbeeey
User
Beiträge: 2
Registriert: Mittwoch 1. September 2021, 13:09

Vielen Dank für die schnelle Antwort und die Erklärungen @__blackjack__!

Ich versuche mal anhand Deiner "Vorlage" vielleicht was draus zu basteln...
Erscheint mir aber auf jeden Fall schonmal logisch! Wie bereits erwähnt bin ich noch relativ neu mit Python, aber so lernt man zumindest Erfahrungswerte.
Das was Du mir oben erklärt hast wusste ich zum größten Teil noch gar nicht... (z.B. "Sternchen-Importe" usw.)

Vielen lieben Dank schonmal, ich melde mich die Tage nochmal wenn ich etwas daraus basteln konnte/oder halt wenn nicht :D

BG, Felbeeey
Antworten