LernApp - Fragen und Antworten als dictionary aus CSV einlesen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Hello :)

vor kurzem habe ich angefangen Python zu lernen. Da ich mittlerweile das Gefühl habe die Grundlagen zu verstehen, dachte ich mir, dass ich mich mal an ein eigenes Projekt wage. Und zwar möchte ich ähnlich einer Quiz App ein Programm zum Lernen von Python schreiben, was auch dazu dienen soll, dass ich immer mal wieder die verschiedenen Inhalte von Python wiederhole.

Auf dieser Seite (https://dev.to/mindninjax/how-to-build- ... ython-10ik) habe ich eine, wie ich finde ganz gute Vorlage gefunden, wie ich so ein "Python-Lernquiz" aufbauen könnte. Nach und nach möchte die Teile des Programmes auf meine Bedürfnisse umschreiben und mir die Sprache so auch ein bisschen erarbeiten.

Zuerst möchte ich einmal eine Grundstruktur schaffen, die einige Fragen abfragt. Danach würde ich gerne einige Kategorien/Kapitel/Level erschaffen, die man irgendwann einmal über eine Score freispielen muss. So viel zur Grundidee.

Jetzt habe ich natürlich das Problem, dass ich ganz am Anfang bei der Organisation der Daten festhänge. In der Vorlage werden die Fragen und Antworten in einem Dictionary gespeichert.

Code: Alles auswählen

quiz = {
    1 : {
        "question" : "What is the first name of Iron Man?",
        "answer" : "Tony"
    },
    2 : {
        "question" : "Who is called the god of lightning in Avengers?",
        "answer" : "Thor"
    },
    3 : {
        "question" : "Who carries a shield of American flag theme in Avengers?",
        "answer" : "Captain America"
    },
    4 : {
        "question" : "Which avenger is green in color?",
        "answer" : "Hulk"
    },  
    5 : {
        "question" : "Which avenger can change it's size?",
        "answer" : "AntMan"
    },
    6 : {
        "question" : "Which Avenger is red in color and has mind stone?",
        "answer" : "Vision"
    }
}
Ich würde die Fragen und Antworten gerne in einer externen Datei speichern, damit ich diese später leichter bearbeiten und erweitern kann. CSV Dateien bieten die Möglichkeit die Dateien leicht mit Excel nachzubearbeiten, deshalb erscheint mir das irgendwie eine gute Wahl zu sein.

Code: Alles auswählen

level,ID,question,answer
level01,1,question1,answer1
level01,2,question2,answer2
level01,3,question3,answer3
level01,4,question4,answer4
level01,5,question5,answer5
level02,1,Frage1,Antwort1
level02,2,Frage2,Antwort2
level02,3,Frage3,Antwort3
level02,4,Frage4,Antwort4
level02,5,Frage5,Antwort5
Die Daten mit dem Dict.Reader einzulesen bekomme ich auch hin.

Code: Alles auswählen

from csv import DictReader

file_handle = open("questionstest.csv",'r')
csv_reader = DictReader(file_handle)

quiz = {}

for row in csv_reader:
    print(row)
Was ich aber nicht hinbekomme ist, die Daten so einzulesen und in einem Dictionary zu speichern, dass ich die Fragen und Antworten wie bei der Vorlage weiterverarbeiten kann. Die Zeilen werden mit Key und Value in einem Dictionary eingelesen, weiter komme ich leider nicht.

Code: Alles auswählen

{'level': 'level01', 'ID': '1', 'question': 'question1', 'answer': 'answer1'}
{'level': 'level01', 'ID': '2', 'question': 'question2', 'answer': 'answer2'}
{'level': 'level01', 'ID': '3', 'question': 'question3', 'answer': 'answer3'}
{'level': 'level01', 'ID': '4', 'question': 'question4', 'answer': 'answer4'}
{'level': 'level01', 'ID': '5', 'question': 'question5', 'answer': 'answer5'}
{'level': 'level02', 'ID': '1', 'question': 'Frage1', 'answer': 'Antwort1'}
{'level': 'level02', 'ID': '2', 'question': 'Frage2', 'answer': 'Antwort2'}
{'level': 'level02', 'ID': '3', 'question': 'Frage3', 'answer': 'Antwort3'}
{'level': 'level02', 'ID': '4', 'question': 'Frage4', 'answer': 'Antwort4'}
{'level': 'level02', 'ID': '5', 'question': 'Frage5', 'answer': 'Antwort5'}
Kann mir hier jmd einen Tipp geben, wie das geht? Falls es (für Anfänger) bessere Möglichkeiten gibt die Fragen und Antworten extern zu speichern, dann bin ich dafür natürlich auch offen. CSV erschien mir zum Erweitern des Katalogs einfach eine gute Wahl zu sein.

Vielen Dank schon mal im Voraus!
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@makcode,

ich habe mir den Sourcecode von dem Tutorial angesehen. Es mag zwar funktionieren, ist aber nicht sonderlich gut geschrieben.
Allein die Tatsache, dass er die Daten in einem .py als Dictionary speichert ist fragwürdig.
Die while -Schleifen sind auch merkwürdig... und vieles, vieles mehr.

Aber zu deinem Code:
Schau dir mal die Kommentare an.

Code: Alles auswählen

from csv import DictReader

file_handle = open("questionstest.csv",'r')
csv_reader = DictReader(file_handle)

quiz = {}

for row in csv_reader:
    # mach hier doch mal:
    print(csv_reader["question"])
    print(csv_reader["answer"])
    # dann fällt dir bestimmt auf, wie es weiter geht
Für die Struktur deiner Daten ist ein csv allerdings nicht ideal. Z.B. kommt level01 in jeder Zeile vor. Ich würde eine JSON Datei vorziehen. Ob du das jetzt in Excel oder in einer JSON Datei eintippst, sollte kaum einen Unterschied machen.

Natürlich musst du dann das Programm entsprechend anpassen.
Nufnus
User
Beiträge: 18
Registriert: Sonntag 29. November 2020, 21:40

Als jemand, der wie ich auch noch an den Grundlagen arbeitet finde ich das Projekt interessant.
Erinnert mich an einen Vokabeltrainer, den ich vielleicht demnächst mal in Python schreiben werde, das ist ja ein sehr ähnliches Prinzip.

Die DictReader Klasse habe ich noch nicht benutzt, mir ist nur aufgefallen dass du von einem Dictionary sprichst. Meiner Meinung ist das aber ein dictionary in einem dictionary.
kommst also ohne diese DictReader Klasse prinzipiell erstmal so "händisch" an die Elemente

Code: Alles auswählen

print(quiz[1]["question"])
.

Nur als kleine Anmerkung, vermutlich eher unwichtige Anmerkung.

Wie gesagt interessantes Projekt auf jeden Fall.

Gruß
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Vielen Dank für deine Antwort!
rogerb hat geschrieben: Sonntag 27. Juni 2021, 22:17

Code: Alles auswählen

for row in csv_reader:
    # mach hier doch mal:
    print(csv_reader["question"])
    print(csv_reader["answer"])
    # dann fällt dir bestimmt auf, wie es weiter geht
Das gibt mir einen Fehler aus.

Code: Alles auswählen

    print(csv_reader["question"])
TypeError: 'DictReader' object is not subscriptable
Ist aber auch egal - wenn es sinnvoller ist die Daten in einer .json Datei zu speichern, dann nehme ich eine .json. Damit hatte ich vorher auch schon ein bisschen rumprobiert. Die bekomme ich auch eingelesen. Das habe ich gerade nochmal probiert.

Dann werde ich mich im Laufe der Woche bzw. am Wochenende mal dran machen, dass das Programm Fragen stellt und mit den Antworten abgleicht. :)
rogerb hat geschrieben: Sonntag 27. Juni 2021, 22:17Die while -Schleifen sind auch merkwürdig... und vieles, vieles mehr.
Wenn du dich auf die

Code: Alles auswählen

while attempts > 0:
Schleifen beziehst, dann verstehe ich was meinst. Ich habe hier noch ein Buch "Einstieg in Python", da wurde das glaube ich mit for-Schleifen gelöst.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Nufnus hat geschrieben: Montag 28. Juni 2021, 17:13 Erinnert mich an einen Vokabeltrainer, den ich vielleicht demnächst mal in Python schreiben werde, das ist ja ein sehr ähnliches Prinzip.
Ja. ich glaube bei Fragen und Antworten Apps ist das Grundprinzip immer das gleiche, wobei das oft auch ganz unterschiedlich gelöst wird. :)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Ein Wörterbuch, das als Schlüssel nur aufsteigende Zahlen hat, ist unsinnig, weil dafür sind Listen erfunden worden.
Bei

Code: Alles auswählen

for row in csv_reader:
    # mach hier doch mal:
    print(csv_reader["question"])
    print(csv_reader["answer"])
    # dann fällt dir bestimmt auf, wie es weiter geht
wird mit `row` ja nichts gemacht, den Fehler solltest Du einfach korrigieren können.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

So, ich habe mich gestern und heute mal hingesetzt und mal eine Grundstruktur erarbeitet. Es ist jetzt noch nicht so elaboriert, aber ich bin ganz zufrieden, da es doch ganz gut lief.
Nächster Schritt ist wahrscheinlich Spieler(namen) und deren Score in einer externen Datei zu speichern und den Code insgesamt noch ein bisschen übersichtlicher zu gestalten. :)

Code: Alles auswählen

import json
import sys

# Import question file
with open("questions.json") as jsonFile:
    jsonObject = json.load(jsonFile)
    jsonFile.close()

quiz = jsonObject["quiz"]

categories = list(quiz.keys())

# initialling score
score = 0

print("Choose your category:", categories)

# for-loop 5 attempts
for c in range(1,6):
    chosen_category = input()
    if chosen_category in categories:
        print("Have fun playing the", chosen_category, "category")
        break
    elif c == 5:
        print("You haven't chosen an existing category")
        quit()
    else:
        print("This category doesn't exist. Try again!")
   
# set question stack
questions = quiz[chosen_category]

# play the question stack
for x in range(len(questions)):
    print(questions[x]["question"])

    # for-loop 3 attempts
    for attempts in range(1,4):
        user_input = input()
        if questions[x]["answer"] == user_input:
            score += 10
            print("That's right!")
            break
        else:
            print("That's not correct. Try again!")
            score -= 2
    
# print the final score     
print("You have earned ", score, "points")
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@makcode: sys wird importiert, aber gar nicht benutzt.
Variablennamen schreibt man komplett klein. with ist ja gerade dazu da, dass die Datei sicher wieder geschlossen wird, das `close` ist also unnötig.
In Python ist alles ein Objekt, etwas `jsonObject` ist nicht sehr aussagekräftig.
Warum ist in der JSON-Datei ein Wörterbuch mit einem einzigen Schlüssel `quiz`? Diese Ebene könnte man doch gleich weg lassen.
Variablen initialisiert man erst, wenn man sie braucht, `score` wird 18 Zeilen zu früh initalisiert.
`quit` verwendet man nicht in Skripten. Warum hat man nur 6 Versuche, eine richtige Kategorie einzugeben?
Über einen Index iteriert man nicht, weil man direkt über die Element einer Liste iterieren kann, `x` ist auch kein guter Name für einen Index.

Code: Alles auswählen

import json

# Import question file
with open("questions.json") as file:
    quiz = json.load(file)["quiz"]

categories = list(quiz.keys())

print("Choose your category:", categories)
while True:
    chosen_category = input()
    if chosen_category in categories:
        print("Have fun playing the", chosen_category, "category")
        break
    else:
        print("This category doesn't exist. Try again!")
   

# initialling score
score = 0
# play the question stack
for question in quiz[chosen_category]
    print(question["question"])

    # for-loop 3 attempts
    for attempts in range(1,4):
        user_input = input()
        if question["answer"] == user_input:
            score += 10
            print("That's right!")
            break
        else:
            print("That's not correct. Try again!")
            score -= 2
    
# print the final score     
print("You have earned ", score, "points")
Nächster Schritt wäre, diesen Spaghetti -Code in Funktionen aufzuteilen.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Sirius3 hat geschrieben: Dienstag 29. Juni 2021, 06:35
[.....]
Das habe ich in dem Moment natürlich nicht gecheckt.

Wenn ich das an meinen jetztigen Code anpasse, dann funktioniert das genauso.

Code: Alles auswählen

for row in csv_reader:
    print(row["question"])
    user_input = input()

    if row["answer"] == user_input:
            print("That's right!")
            break
    else:
        print("That's not correct. Try again!")
Danke!
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

uff, das ging aber fix. :o
Sirius3 hat geschrieben: Sonntag 4. Juli 2021, 17:55 @makcode: sys wird importiert, aber gar nicht benutzt.
[...]
`quit` verwendet man nicht in Skripten. Warum hat man nur 6 Versuche, eine richtige Kategorie einzugeben?
Es ging mir hierbei weniger darum dem Spieler Versuche einzuräumen, sondern ich wollte hier vermeiden, dass man in einer Endlosschleife hängen bleibt, falls der Spieler aus irgendwelchen Gründen keine vorhandene Kategorie eingeben kann. Daher habe ich nach einer Möglichkeit gesucht das Programm gezielt abzubrechen. Daher stammt auch noch das import sys. exit() hat irgendwie nicht funktioniert.
Sirius3 hat geschrieben: Sonntag 4. Juli 2021, 17:55 Variablennamen schreibt man komplett klein. with ist ja gerade dazu da, dass die Datei sicher wieder geschlossen wird, das `close` ist also unnötig.
In Python ist alles ein Objekt, etwas `jsonObject` ist nicht sehr aussagekräftig.
Danke für den Hinweis.
Sirius3 hat geschrieben: Sonntag 4. Juli 2021, 17:55 Warum ist in der JSON-Datei ein Wörterbuch mit einem einzigen Schlüssel `quiz`? Diese Ebene könnte man doch gleich weg lassen.
Ich habe recht lange gebraucht bis ich eine funktionierende JSON-Datei einbinden konnte. Mir hat es irgendwie geholfen, dass ich auf diese Weise prüfen konnte, ob in der JSON-Datei ein Fehler war oder ob ich in der Python Datei einen Fehler hatte.

Ich habe zwar ein Buch, welches ich ganz gut finde, dort gibt es zwar auch Abschnitte über Listen und Dictionary, die sind aber auch nicht immer so ausführlich. Daher muss ich mir immer wieder auch im Netz was raussuchen. Ähnlich wie oben bei quit(). Mir ist auch klar, dass das nicht immer korrekt ist, was im Netz steht.
Sirius3 hat geschrieben: Sonntag 4. Juli 2021, 17:55 Nächster Schritt wäre, diesen Spaghetti -Code in Funktionen aufzuteilen.
Das meinte ich auch, als ich geschrieben habe, dass ich den Code ein bisschen übersichtlicher gestalten möchte, auch wenn ich das so nicht geschrieben habe. Ich denke das ist auch ein guter nächster Schritt zum Üben. :)
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Sirius3 hat geschrieben: Sonntag 4. Juli 2021, 17:55 @makcode:

Über einen Index iteriert man nicht, weil man direkt über die Element einer Liste iterieren kann, `x` ist auch kein guter Name für einen Index.

Code: Alles auswählen

for question in quiz[chosen_category]
    print(question["question"])

[...]

        if question["answer"] == user_input:
boah, nice! Danke! Das ist mir gerade erst aufgefallen, als ich die Änderungen übertragen habe.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

]So, nach einigen Höhen und Tiefen bzw. schlechten Entscheidungen habe ich in dieser Woche (da Urlaub) mal wieder ein bisschen mehr Zeit um mich intensiver mit dem Programmieren zu befassen.

Ich habe noch eine grundlegende Frage. Die Eingangsfrage ("Fragen und Antworten als dictionary aus CSV einlesen") ist schon eine Weile geklärt. Ich würde trotzdem hin und wieder gerne Code posten um ein Feedback zu erhalten - da ich Python ohne Lehrer bzw. Tutor lerne. Ist es möglich den Threat umzubenennen und in einen anderen Forenbereich zu verschieben oder soll ich einfach einen neuen eröffnen?

So, jetzt mal ans Eingemachte:
Ich hatte vor einiger Zeit den Code schon in Funktionen umgeschrieben. Ich hatte aber immer Probleme mit dem lokalen und globalen Variablen. Genauer gesagt wie bekomme ich einen Wert aus einem lokalen Bereich in eine andere Funktion. Ich hatte es zwar hinbekommen, dass das Programm funktioniert hat - jedoch mehr schlecht als recht, weil ich es nicht so richtig verstanden hatte. Mittlerweile bin ich - das hoffe ich zumindest - ein ganzes Stück weiter.

Das ist das Ergebnis:

Code: Alles auswählen

import json

def intro():
    playername = input("Hello friend! Whats your name?: ")
    print("Hello", playername)
    return playername


# Load json files
def load_json(filename):
    with open(filename) as file:
        file_content = json.load(file)
    return file_content


# Choose the category you want to play 
def choose_category(category_list):
   
    while True:
        print(category_list)
        chosen_category = input("Tell me! Which category do you want to play?: ")
        if chosen_category in category_list:
            print("Have fun playing the", chosen_category, "category")
            break
        else:
            print("This category doesn't exist. Try again!")
    return chosen_category


# play the question stack
def play_questions(items, question_stack):
    for question in items[question_stack]:
        print(question["question"])
        
        # for-loop 3 attempts
        
        for attempts in range(1,4):
            user_input = input()
            if question["answer"] == user_input:
                print("That's right!")
                break
            else:
                print("That's not correct. Try again!")


def main():
    intro()
    questions = load_json("questions.json")
    categories = list(questions.keys())
    print("What are you up to? Press 0 to quit. Press 1 to have a look at the highscore table. Or press 2 to play.")
    
    while True:
        
        try:
            menu = int(input())
        except:
            print("You haven't chosen a valid number")

        if menu == 0:
            break

        elif menu == 1:
            print("This option is not available yet")               # Add highscore function later
            print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")

        elif menu == 2:
            selected_category = choose_category(categories)  
            play_questions(questions, selected_category)
            print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")
        
        else:
            print("You haven't chosen a valid number")
            print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")
            

main()

Als nächsten Schritt hatte ich mir überlegt eine erste GUI für das Programm zu schreiben. Zuerst dachte ich an Qt/Qml, da ich irgendwann später (2025 :lol:) einmal das Programm auf dem Smartphone (mit SailfishOS) laufen lassen möchte. Das war einer der schlechten Entscheidungen, weil ich viel Zeit investiert habe ohne wirklich weiter zu kommen. Daher denke ich, dass es besser ist mit Tkinter anzufangen. Hier habe ich auch einen Online Tutorial von Linkedin gefunden, dass die ersten Schritte erklärt. Danach würde ich gerne eine Highscore entwickeln und danach verschiedene Spielmodi.

Haltet ihr diese nächsten Schritte für sinnvoll oder eher nicht?

Anmerkung: Ich weiß, dass ich mich irgendwann nochmal mit dem Thema Klassen beschäftigen muss. Bisher erschließt sich mir das noch nicht so ganz. Derzeit möchte ich die Grundprinzipien besser verstehen und würde das Thema noch ein bisschen aufschieben.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@makcode: Irgendwann mit Klassen beschäftigen bedeutet *jetzt* mit Klassen beschäftigen, denn wenn Du da jetzt eine GUI drauf setzen willst, brauchst Du Klassen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

__blackjack__ hat geschrieben: Freitag 7. Januar 2022, 00:35 @makcode: Irgendwann mit Klassen beschäftigen bedeutet *jetzt* mit Klassen beschäftigen, denn wenn Du da jetzt eine GUI drauf setzen willst, brauchst Du Klassen.
So, daran habe ich mich jetzt mal versucht und das Programm in Klassen umgeschrieben und aufgeteilt. Danach habe ich mir einen Kurs über Tkinter angesehen und versuche das gerade umzusetzen.

Hier hapert aber schon wieder. Die meisten Tutorials erklären, wie man Labels, Buttons, Listboxes etc... erstellt, aber ich verstehe nicht wie ich das zusammenführe.
Mein Ziel ist es die Programmlogik, die ich bisher habe, in der GUI anzeigen zu lassen.

Der Grundgedanke ist erst einmal alles linear ablaufen zu lassen.

1. Frame: Willkommen + Eingabe Spielername
2. Frame: Begrüßung + Menü (quit, highscore, spielen)
3. Frame: Fragen + Antworten
4. Frame: Fragen + Antworten

* mit Frame meine ich hier keine Unterteilung des Fensters, sondern dass ein komplett neuer Frame erst scheint - ich hoffe das ist verständlich. :roll:

Meine Dateien sehen derzeit wie folgt aus:

quiz.py (Klassen)

Code: Alles auswählen

import json

class Player:
    def __init__(self, name):
        # self.name = input("Hello friend! Whats your name?:")
        # print("Hello", self.name)
        self.name = name
        print("Hello", self.name)


class Files:
    def __init__(self, filename):
        self.filename = filename

    # Load json files
    def load_json(self):
        with open(self.filename) as file:
            file_content = json.load(file)
        return file_content



class Quiz:
    def __init__(self, q_list, category_list):
        self.score = 0
        self.question_list = q_list
        self.category_list = category_list

    # Select the category you want to play 
    def select_category(self):
        
        while True:
            print(self.category_list)
            selected_category = input("Tell me! Which category do you want to play?: ")
            if selected_category in self.category_list:
                print("Have fun playing the", selected_category, "category")
                break
            else:
                print("This category doesn't exist. Try again!")
        return selected_category


    # play the question stack
    def play_questions(self, questions, selected_category):
        for question in questions[selected_category]:
            print(question["question"])
        
            # for-loop 3 attempts
        
            for attempts in range(1,4):
                user_input = input()
                if question["answer"] == user_input:
                    print("That's right!")
                    self.score +=10
                    break
                else:
                    print("That's not correct. Try again!")
                    self.score -=2
main.py

Code: Alles auswählen

import quiz

player = quiz.Player("Markus")
files = quiz.Files("questions.json")
    
questions = files.load_json()
categories = list(questions.keys()) 
quiz = quiz.Quiz(questions, categories)
    
while True:
    print("What are you up to? Press 0 to quit. Press 1 to have a look at the highscore table. Or press 2 to play.")
            
    try:
        menu = int(input())
    except:
        print("You haven't chosen a valid number")

    if menu == 0:
        break

    elif menu == 1:
        print("This option is not available yet")               # Add highscore function later
        print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")

    elif menu == 2:
        selected_category = quiz.select_category()  
        quiz.play_questions(questions, selected_category)
        print("Congrats, you've scored", quiz.score, "Points. Awesome!")
        print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")
        
    else:
        print("You haven't chosen a valid number")
        print("Do you want to play another round? Then press 2. Do you want to quit? Press 0. Do you want to see the highscore? Press 1")

view.py (erstes Tkinter-Fenster)

Code: Alles auswählen

from tkinter import *
import main

class Application(Frame):
    def __init__(self, master=None, labeltext=""):
        Frame.__init__(self, master)
        self.lb = Label(master, text=labeltext)
        self.lb.pack()

root = Tk()
app = Application(master=root, labeltext = main.player)
app.mainloop()

Jetzt ist es so, dass ich hier schon mal (min.) zwei (Denk-)Fehler habe.
Zum einen läuft erst die Programmlogik durch und dann öffnet sich das Fenster und zum anderen wird im Label das Objekt und nicht der String ausgegeben.

Bild

Der zweite Punkt wirft bei mir noch eine zusätzliche Frage auf. In welcher Datei werden den die Strings (wie "Wie lautet dein Name" oder du "Hast 100 Punkte erreicht.") gespeichert? In der Tkinter Datei oder als Variable in der Programmlogik?

Vll kann mir hier jmd ein bisschen weiterhelfen.
Vielen Dank schon mal. :)
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst dein bestehendes Programm nicht benutzen. Zum einen, weil es so geschrieben ist, dass es direkt beim import laeuft. Wie du ja auch schon merkst. Zum anderen, weil es nicht dem GUI-Paradigma von Ereignissen, auf die reagiert wird, entspricht.

Wenn du deinen Ablauf so wie beschrieben machen willst, dann bietet sich dafuer eine Reihe von tk.Frame-Objekten uebereinander an, von denen man das aktive mit tkraise() in den Fordergrund hebt. Ausserdem eine Zustandsvariable, die festlegt, wo im Ablauf man sich gerade befindet, und die dann durch entsprechende Ereignisse wie zB einem Knopf in der Namenseingabe veraendert wird. Aber nur, wenn da auch ein Name vergeben wurde!

Was die Strings angeht - wieso sollten die anders sein, als schon bei dir im jetzigen Programm?
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Vielen Dank schon mal für die schnelle Antwort.
__deets__ hat geschrieben: Sonntag 23. Januar 2022, 15:57 Du kannst dein bestehendes Programm nicht benutzen. Zum einen, weil es so geschrieben ist, dass es direkt beim import laeuft.
Diesen Teil verstehe ich glaube ich. Die Konsequenz ist dann wahrscheinlich, dass die main.py überflüssig ist und ich die Logik, die darin steckt, auf die view.py und quiz.py Dateien aufteilen muss, sodass der view "losläuft" und sich die Werte aus der quiz.py holt, wenn sie benötigt werden.
__deets__ hat geschrieben: Sonntag 23. Januar 2022, 15:57 Zum anderen, weil es nicht dem GUI-Paradigma von Ereignissen, auf die reagiert wird, entspricht.
Zuerst konnte ich hiermit gar nichts anfangen. Mittlerweile dämmert es. Das ist der Ort an dem das Objekt gespeichert wird. Wenn du sagst, das entspricht nicht dem GUI-Paradigma, heißt das ich kann mit einem Label(*, text=variable) überhaupt nicht auf den String zugreifen oder greife ich einfach "falsch" darauf zu?
__deets__ hat geschrieben: Sonntag 23. Januar 2022, 15:57 Wenn du deinen Ablauf so wie beschrieben machen willst, dann bietet sich dafuer eine Reihe von tk.Frame-Objekten uebereinander an, von denen man das aktive mit tkraise() in den Fordergrund hebt. Ausserdem eine Zustandsvariable, die festlegt, wo im Ablauf man sich gerade befindet, und die dann durch entsprechende Ereignisse wie zB einem Knopf in der Namenseingabe veraendert wird. Aber nur, wenn da auch ein Name vergeben wurde!
Hier zu habe ich bereits ein Vorlage gefunden.

Code: Alles auswählen

import tkinter as tk
class Page(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)

    def show(self):
        self.lift()
         

class Page1(Page):
    def __init__(self, *args, **kwargs,):
        Page.__init__(self, *args, **kwargs)
        label = tk.Label(self, text="This is page 1")
        label.pack(side="top", fill="both", expand=True)

class Page2(Page):
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        label = tk.Label(self, text="This is page 2")
        label.pack(side="top", fill="both", expand=True)

class Page3(Page):
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        label = tk.Label(self, text="This is page 3")
        label.pack(side="top", fill="both", expand=True)

class Page4(Page):
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        label = tk.Label(self, text="This is page 4")
        label.pack(side="top", fill="both", expand=True)


class MainView(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        p1 = Page1(self)
        p2 = Page2(self)
        p3 = Page3(self)
        p4 = Page4(self)


        buttonframe = tk.Frame(self)
        container = tk.Frame(self)
        buttonframe.pack(side="top", fill="x", expand=False)
        container.pack(side="top", fill="both", expand=True)

        p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        p2.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        p3.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        p4.place(in_=container, x=0, y=0, relwidth=1, relheight=1)

        b1 = tk.Button(buttonframe, text="Page 1", command=p1.show)
        b2 = tk.Button(buttonframe, text="Page 2", command=p2.show)
        b3 = tk.Button(buttonframe, text="Page 3", command=p3.show)
        b4 = tk.Button(buttonframe, text="Page 4", command=p4.show)

        b1.pack(side="left")
        b2.pack(side="left")
        b3.pack(side="left")
        b4.pack(side="left")

        p1.show()


def main():
    root = tk.Tk()
    root.title("Title")
    main = MainView(root)
    main.pack(side="top", fill="both", expand=True)
    root.wm_geometry("400x400")
    root.mainloop()

if __name__ == '__main__':
    main()
Damit wollte ich mich beschäftigen, wenn ich den ersten Teil hinbekommen habe. Ich habe oft beim Lernen von Python den Hang zu schnell zu viel zu wollen, daher versuche ich mittlerweile eher kleinere Schritte zu gehen. Für deinen Input diesbezüglich bin ich dir sehr dankbar, weil mir nicht bewusst war, dass das Frames sind, die statt nebeneinander, übereinander liegen. Jetzt verstehe ich was da passiert.
__deets__ hat geschrieben: Sonntag 23. Januar 2022, 15:57 Was die Strings angeht - wieso sollten die anders sein, als schon bei dir im jetzigen Programm?
Vll habe ich hier wieder einen Denkfehler, aber bisher sind diese Strings nicht gespeichert, sondern werden anhand der print-Funktion Zeile für Zeile ausgegeben. Wenn ich die über ein Label ausgeben möchte, dann muss ich die doch zuerst in einer Variablen speichern, oder nicht? Auf diese Weise könnte ich doch auch ein Label anlegen, welches später auf mehrere Variablen bzw. Strings zugreift. Die print-Funktion würde dann in der Programmlogik entfallen.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

__deets__ hat geschrieben: Sonntag 23. Januar 2022, 15:57 Zum anderen, weil es nicht dem GUI-Paradigma von Ereignissen, auf die reagiert wird, entspricht.
Okay, das Problem habe ich schon mal gelöst. :)

Code: Alles auswählen

class Player:
    def __init__(self, name):
        # self.name = input("Hello friend! Whats your name?:")
        # print("Hello", self.name)
        self.name = name
        print("Hello", self.name)
    def __str__(self):
        return self.name

Code: Alles auswählen

from tkinter import *
import main

class Application(Frame):
    def __init__(self, master=None, labeltext=""):
        Frame.__init__(self, master)
        self.lb = Label(master, text=labeltext)
        self.lb.pack()

root = Tk()
root.geometry("400x400")
app = Application(master=root, labeltext=main.player)
app.mainloop()
Mit der Methode

Code: Alles auswählen

def __str__(self):
kann man einen String zurückgeben, der im Label angezeigt wird.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist zwar richtig, das du da was gefunden hast. Aber das hat nichts mit dem was ich gesagt habe zu tun. Und ist auch eine eher nebensaechliche Erkenntnis in Bezug auf dein Problem - du wirst dadurch *nicht* ein update deines Textes erreichen.

Das Paradigma, von dem ich spreche, ist etwas anderes: GUIs basieren auf Ereignissen. Benutzereingaben, Timer, andere IO-Ereignisse. Man hat als Programmierer keinen Kontrolle mehr ueber den Programmfluss, sondern gibt den an eine Hauptschleife - mainloop - ab. Und dann wird man nur noch zurueckgerufen, wenn etwas passiert. Also zB dein Benutzer den "Weiter"-Knopf nach der Namenseingabe gedrueckt hat. Da entscheidet man dann, was passiert. Und sobald das passiert ist, laeuft wieder die Hauptschleife, und updated die GUI wie gewuenscht.
makcode
User
Beiträge: 13
Registriert: Sonntag 27. Juni 2021, 16:37

Vom Grundsatz her verstehe ich das. Meist hängt es an der Fähigkeit dies umzusetzen. Es fängt damit an, dass ich gar nicht wirklich weiß bzw. wusste wie ich das Programm aufteile und welche Dateien mit welchen interagieren. Ich dachte ein erster Schritt wäre einfach mal zu probieren, wie man einen oder mehrere Werte in der GUI angezeigt bekommt.

Diese Woche habe ich ein gutes Tutorial für eine Quiz-App mit GUI gefunden - https://www.freecodecamp.org/news/how-t ... trivia-db/. Das Tutorial habe ich heute gemacht und sogar das meiste verstanden. Nächstes Wochenende werde ich mal versuchen den Code nach meinen Vorstellung umzuschreiben. Ich hoffe, dass mir das weiterhilft. So habe ich wenigstens eine Vorlage mit einer funktionierenden GUI und ein Beispiel, wie man die Dateien und Klassen organisiert.

Es ist schon traurig, wie schwer ich mich damit tue und wie langsam ich vorankomme, trotzdem bin ich noch nicht an dem Punkt angekommen, dass ich aufgebe.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn du dich wirklich erst seit einigen Wochen damit beschaeftigst, dann ist das schon ok, dass es noch nicht sitzt. Fuer so etwas braucht es schon ein bisschen Auseinandersetzung.
Antworten