Tkinter Eingabe als Variable übergeben

Fragen zu Tkinter.
Antworten
René
User
Beiträge: 6
Registriert: Mittwoch 18. Mai 2011, 22:44

Hallo Zusammen,

ich bin gerade dabei ein wenig in Python rein zu schnuppern und da kam dann auch Tkinter mit ins Spiel. Bin das Handbuch ü+ber python 3 am lesen und dort wurde ein Zahlenspiel erwähnt was ich ein wenig aufrüsten wollte, bzw. da bin ich gerade dran. Wie schon gesagt bin noch mitten im Lernprozess, deshalb ist dies bestimmt nicht die beste Lösung, aber die verstehe ich bis jetzt :)

Das Spiel ist eigentlich ganz Simpel, der Benutzer muss eine Zahl zwischen 1 und 100 erraten und es werden, sofern die Zahl nicht richtig ist, immer Fehlermeldungen ausgespuckt. Im Terminal hat das ganze auch schon funktioniert, doch bei tkinter bleibe ich hängen.

Ich habe erst einmal das Grundgerüst gebaut und dann eine Funktion die dann agieren soll wenn ich den Raten Button betätige. Jedoch benötige ich dafür die Eingabe als Variable, damit ich anschließend in der Funktion überprüfen kann ob der eingetragene Wert der vorgegeben Zahl entspricht. In nächsten Schritt möchte ich dann das die Ausgabe der Fehlermeldung immer eingeblendet wird, das ist aber was anderes...

So sieht bis jetzt mein kleines Spiel aus:

Code: Alles auswählen

# -*- coding: iso-8859-15 -*-
from tkinter import *

#Variablen        
geheimZahl = 88
geheim = geheimZahl
spieler = 0
antwort = "Test 123"
antwort1 = "Test 123"
i = 0

#Fehlermeldungen
fehler1 = "Ohje, suchen wir nicht eine Zahl zwischen 1 und 100?"
fehler2 = "Eine negative Zahl suchen wir heute leider nicht!"
fehler3 = "Upps, die Zahl, die du eingegeben hast ist schon etwas zu kein!"
fehler4 = "Mhm, die Zahl darf ruhig etwas kleiner sein!"
fehler5 = "Wie schon gesagt, die Zahl, die wir suchen liegt zwischen 1 und 100!"


def senden():
    while spieler != geheim:
        if spieler < geheim:  
            if spieler < 100:
                if spieler == 0:
                    antwort = fehler1
                if spieler <= -1:
                    antwort = fehler2
                if spieler >= 1:
                    antwort = fehler3

        if spieler > geheim:
            if spieler < 100:
                antwort = fehler4
            
            if spieler > 100:
                antwort = fehler5

        else:
            antwort = fehler5


#GUI
root = Tk()
root.title("Zahlenspiel")
root.geometry("200x200")

#Überschrift
root.labelStart = Label(root, text="Zahlenspiel", bg="grey", fg="blue", width=200, font=("Helvetica Neue",30))
root.labelStart.pack()

#Antwort
root.labelAntwort = Label(root, text=antwort, width=200, bg="blue", fg="white")
root.labelAntwort.pack()

#Eingabe
eingabe = Entry(root, width="200", text=antwort1)
eingabe.pack()

root.button = Button(root, text = "...Raten...", command=senden)
root.button.pack()

root.mainloop()
Wie schon gesagt, man kann das ganze bestimmt besser umsetzen, doch so habe ich es hinbekommen. ich lerne immer durch Projekte, nur ein buch zu Lesen bringt mich nur selten weiter. Ich brauche Probleme, die ich lösen kann und dies ist so ein Problem :)

Habt Ihr vielleicht eine Idee wie man es lösen kann?
BlackJack

@René: Bei GUI-Programmen funktioniert der Programmablauf anders als bei linear ablaufenden Programmen. Du kannst da nicht mehr länger laufende Schleifen verwenden, sondern musst Code schreiben der immer nur kurz auf Aktionen vom Benutzer reagiert und dann die Kontrolle wieder an die Hauptschleife der GUI übergibt. IMHO sollte man mit GUI-Programmierung nicht anfangen bevor man objektorientierte Programmierung drauf hat. Von Programmen in denen Variablen auf Modulebene existieren, sollte man davor auch schon weggekommen sein.

Konkret zum Problem: Immer wenn der Benutzer auf den Button drückt, müsstest Du die Zeichenkette aus dem Eingabe-Widget auslesen, in eine Zahl wandeln, vergleichen, und dann eine entsprechende Ausgabe machen. Mehr nicht.
problembär

Im Prinzip stimme ich BlackJack hier zu, auch wenn C-Programmierer GUI-Programme schreiben, ohne daß ihre Sprache OOP überhaupt unterstützt.
Auch Perl wird mit Tk oft ohne OOP verwendet.
Es ist allerdings sehr unbequem, weil dann die Widgetobjekte den Funktionen als Argumente mitgegeben werden müssen.
Ich hab' mal Deinen Code so abgeändert, daß er zumindest einigermaßen funktioniert:

Code: Alles auswählen

#!/usr/bin/env python
# coding: iso-8859-1

import Tkinter as tk

#Variablen        
geheimZahl = 88
geheim = geheimZahl
spieler = 0
antwort = "Test 123"
antwort1 = "Test 123"
i = 0

#Fehlermeldungen
fehler1 = "Ohje, suchen wir nicht eine Zahl zwischen 1 und 100?"
fehler2 = "Eine negative Zahl suchen wir heute leider nicht!"
fehler3 = "Upps, die Zahl, die du eingegeben hast ist schon etwas zu kein!"
fehler4 = "Mhm, die Zahl darf ruhig etwas kleiner sein!"
fehler5 = "Wie schon gesagt, die Zahl, die wir suchen liegt zwischen 1 und 100!"


def senden(eingabewidget, labelwidget):
    spieler = int(eingabewidget.get())
    if spieler < geheim:  
        if spieler < 100:
            if spieler == 0:
                antwort = fehler1
            if spieler <= -1:
                antwort = fehler2
            if spieler >= 1:
                antwort = fehler3

    if spieler > geheim:
        if spieler < 100:
            antwort = fehler4
       
        if spieler > 100:
            antwort = fehler5
    if spieler == geheim:
        antwort = "Jo, das war die gesuchte Zahl!"

    labelwidget.configure(text = antwort)

#GUI
root = tk.Tk()
root.title("Zahlenspiel")
root.geometry("800x600")

#Überschrift
labelStart = tk.Label(root, text="Zahlenspiel", bg="grey", fg="blue", width=200, font=("Helvetica Neue",30))
labelStart.pack()

#Antwort
labelAntwort = tk.Label(root, text=antwort, width=200, bg="blue", fg="white")
labelAntwort.pack()

#Eingabe
eingabe = tk.Entry(root, width="200", text=antwort1)
eingabe.pack()

button = tk.Button(root,
                        text = "...Raten...",
                        command = lambda: senden(eingabe, labelAntwort))
button.pack()

root.mainloop()
So schreibt man das zwar eigentlich immer noch nicht, aber den Code so zu verstehen wäre für Dich erstmal jetzt der nächste Schritt. Weitere sollten folgen!

Gruß
BlackJack

@problembär: C ist halt kein Python. Dort ist es dann entweder nicht besonders sauber → globale Variablen, oder es wird auch ohne spezielle Sprachunterstützung OOP praktiziert. Gtk ist zum Beispiel objektorientiert. OOP ist im Kern ja auch eine Idee und keine Spracheigenschaft. Bestimmte Spracheigenschaften machen die Umsetzung von OOP halt nur einfacher — oder eben schwieriger wenn sie fehlen.

Alternativ könnte man es auch funktional mit Closures umsetzen, aber das unterstützt Python — trotz Closures — nicht so gut, wegen den Sichtbarkeitsregeln von nicht-lokalen Variablen. Zumindest bis zur Einführung der ``nonlocal``-Deklaration in Python 3.
Sonny
User
Beiträge: 1
Registriert: Sonntag 21. Juni 2020, 12:47

Ich habe die Farben und Schriften noch mal verbessert:




#!/usr/bin/env python
# coding: iso-8859-1

import tkinter as tk

#Variablen
geheimZahl = 88
geheim = geheimZahl
spieler = 0
antwort = "Zahlenspiel - Las uns anfangen!"
antwort1 = "Zahlenspiel - Las uns anfangen!"
i = 0

#Fehlermeldungen
fehler1 = "Ohje, suchen wir nicht eine Zahl zwischen 1 und 100?"
fehler2 = "Eine negative Zahl suchen wir heute leider nicht!"
fehler3 = "Upps, die Zahl, die du eingegeben hast ist schon etwas zu kein!"
fehler4 = "Mhm, die Zahl darf ruhig etwas kleiner sein!"
fehler5 = "Wie schon gesagt, die Zahl, die wir suchen liegt zwischen 1 und 100!"


def senden(eingabewidget, labelwidget):
spieler = int(eingabewidget.get())
if spieler < geheim:
if spieler < 100:
if spieler == 0:
antwort = fehler1
if spieler <= -1:
antwort = fehler2
if spieler >= 1:
antwort = fehler3

if spieler > geheim:
if spieler < 100:
antwort = fehler4

if spieler > 100:
antwort = fehler5
if spieler == geheim:
antwort = "Jo, das war die gesuchte Zahl!"

labelwidget.configure(text = antwort)

#GUI
root = tk.Tk()
root.title("Zahlenspiel")
root.geometry("800x600")
root.configure(background='#E0EEE0')
#Überschrift
labelStart = tk.Label(root, text="Zahlenspiel", bg="turquoise", fg="red", width=200, font=("Helvetica Neue",30))
labelStart.pack()

#Antwort
labelAntwort = tk.Label(root, text=antwort, width=200, bg="blue", fg="white", font="Arial, 15")
labelAntwort.pack()

#Eingabe
eingabe = tk.Entry(root, width="400", text=antwort1)
eingabe.pack()

button = tk.Button(root,text = "Versuch\nabgeben!",font="Arial, 30", width=10,heigh=2,bg="orange", command = lambda: senden(eingabe, labelAntwort))
button.pack()

root.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 14087
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sonny: Anmerkungen zum Quelltext:

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

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

Das `geheimZahl` nur verwendet wird um gleich in der nächsten Zeile den Namen `geheim` zu definieren, der dann tatsächlich im Programm verwendet wird, macht keinen Sinn. Ebensowenig das `antwort` und `antwort1` an die gleiche Zeichenkette gebunden werden, und jeweils nur einmal verwendet werden.

`spieler` und `i` werden an 0en gebunden die nie irgendwo verwendet werden.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist.

Die ganzen Höhen und Breitenangaben gehören da nicht rein. Ausser beim `Entry` macht es keinen Sinn und ist potentiell sogar gefährlich wenn denn etwas tatsächlich nicht in den vorgegebenen Platz passen sollte.

Und beim `Entry` sind 400 Zeichen für die Eingabe einer Zahl zwischen 0 und 100 wohl ein ganz kleines bisschen übertrieben.

`senden()` ist kein wirklich guter Name denn diese Funktion sendet nichts.

`spieler` ist auch kein guter Name denn der Wert repräsentiert gar keinen Spieler.

Man nummeriert keine Namen und ich sehe auch nicht, warum die Fehlermeldungen unbedingt an Namen gebunden werden sollten, statt sie einfach dort hin zu schreiben, wo sie gebraucht werden.

Bei der Auwertung gibt es so einige sich Gegenseitig ausschliessende Bedingungen die nacheinander geprüft werden, aber es wird ausschliesslich ``if`` verwendet. Es gibt auch ``elif``.

Die beiden Antworten bei Antworden =0 und >100 sind Problematisch. Bei der ersten würde ich sagen, „Nein, das hatten wir nicht gesagt, nett dass man das jetzt erst erfährt.” Und bei der zweiten ist das "Wie gesagt …" nur treffend, falls das denn vorher tatsächlich schon mal angesagt worden wäre. Was nicht der Fall sein muss, falls der Benutzer gleich als erstes eine zu grosse Zahl eingibt. Man sollte den Benutzer also beim Spielstart vielleicht mal über die Regeln informieren, bevor man solche Sätze raus lässt. 🙂

Die Bedingungen sind für meinen Geschmack zu verschachtelt. Das würde ich alles auf einer Ebene erledigen und erst den Wertebereich prüfen, und dann wie sich die Eingabe zur gesuchten Zahl verhält.

Man sollte auch den Fall berücksichtigen das der Benutzer gar keine Zahl eingibt.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial

GEHEIMZAHL = 88
assert 1 <= GEHEIMZAHL <= 100


def eingabe_auswerten(eingabefeld, ausgabe_label):
    try:
        eingabe = int(eingabefeld.get())
    except ValueError:
        antwort = "Wir suchen eine Zahl!"
    else:
        if eingabe == 0:
            antwort = "Ohje, suchen wir nicht eine Zahl zwischen 1 und 100?"
        elif eingabe < 0:
            antwort = "Eine negative Zahl suchen wir heute leider nicht!"
        elif eingabe > 100:
            antwort = (
                "Wie schon gesagt, die Zahl,"
                " die wir suchen liegt zwischen 1 und 100!"
            )

        elif eingabe == GEHEIMZAHL:
            antwort = "Jo, das war die gesuchte Zahl!"
        elif eingabe < GEHEIMZAHL:
            antwort = (
                "Upps, die Zahl, die du eingegeben"
                " hast ist schon etwas zu kein!"
            )
        elif eingabe > GEHEIMZAHL:
            antwort = "Mhm, die Zahl darf ruhig etwas kleiner sein!"
        else:
            assert False

    ausgabe_label["text"] = antwort


def main():
    title = "Zahlenspiel"

    root = tk.Tk()
    root.title(title)
    root["background"] = "#E0EEE0"

    tk.Label(
        root,
        text=title,
        bg="turquoise",
        fg="red",
        font=("Helvetica Neue", 30),
    ).pack()

    ausgabe_label = tk.Label(
        root,
        text="Lass uns anfangen!",
        bg="blue",
        fg="white",
        font=("Arial", 15),
    )
    ausgabe_label.pack()
    #
    # TODO Den Text initial auswählen und den Fokus auf dieses Element setzen,
    #   damit der Benutzer den Text nicht erst löschen muss, sondern einfach
    #   gleich mit der Eingabe von Ziffern anfangen kann.
    #
    text = "Zahl von 1 bis 100"
    eingabefeld = tk.Entry(root, width=len(text), text=text)
    eingabefeld.pack()

    tk.Button(
        root,
        text="Versuch\nabgeben!",
        font=("Arial", 30),
        bg="orange",
        command=partial(eingabe_auswerten, eingabefeld, ausgabe_label),
    ).pack()

    root.mainloop()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten