Seite 1 von 3
GUI in Programm integrieren
Verfasst: Freitag 21. September 2007, 20:08
von klaus
Hi,
Ich hab ein Problem mit Tkinter. Ich weiß nicht wie ich es hinkriegen soll, diese Funktion
Code: Alles auswählen
def frage(data):
question, solve, stat = data
solution = solve.split(", ")
answer = raw_input(question)
if answer in solution:
print "Richtig!"
data[2] = data[2] + 2
return 1
else:
print "Falsch! Richtig ist: ", solve
data[2] = data[2] + 1
return 0
und diese Klasse:
Code: Alles auswählen
class Iface:
def __init__(self, master):
frame = Frame(master, width=300, height=200)
frame.pack()
self.label = Label(frame, text="Start", pady=10)
self.label.pack()
self.entry = Entry(frame)
self.entry.bind("<Return>", self.get_entry)
self.entry.pack()
self.solve=Button(frame, text="Start")
self.solve.pack()
def get_entry(self, event):
x = self.entry.get()
return x
miteinander zu kombinieren.
Dabei soll dann:
1. "question" dem Label als Text zugewiesen werden.
2. Der Benutzer gibt dann seine Antwort ind das Feld ein und drückt Enter, wodurch die Funktion entry_get() seine Eingabe zurückgibt. Diese soll dann answer zugewiesen werden.
3. Dann soll dem Button entweder "Richtig!" bzw. "Falsch ..." zugewiesen werden. Wenn der Benutzer auf den button klickt, bzw. nochmal Enter drückt soll die nächste Frage gestellt werden.
Es würde mich freuen, wenn mir jemand helfen könnte. Ich hab bis jetzt noch keine Erfahrungen mit Tkinter und weiß daher überhaupt nicht, wie ich das umsetzen soll.
Verfasst: Freitag 21. September 2007, 20:33
von BlackJack
Die beiden Quelltextfragmente wirst Du nicht kombinieren können. Es sind beides "GUI"s für unterschiedliche "Toolkits". Eine "GUI" ist eben eine Textoberfläche.
Verfasst: Freitag 21. September 2007, 20:46
von poker
Stimmt, er könnte aber die Funktionalität von ``frage()`` in ``Iface`` einbinden. Also das ``raw_input`` und die ganzen print's loswerden.
Kurz: Ein rewrite.
Verfasst: Samstag 22. September 2007, 19:45
von klaus
Auch damit hätte ich kein Problem, hauptsache ich schaff es, das ich durch irgendeinen Befehl (vorzugsweise Funktionsaufruf) den gewünschten Effekt erziele und das Programm das macht, was es soll, nämlich aus der Liste ["Frage", "Antwort", 0] die richtige Anzeige.
Ich kann ja mal versuchen, die Funktion irgedwie in die Klasse zu integrieren, allerdings hab ich dann immernoch das Problem, wie ich der Klasse sagen soll, dass sie ne Frage stellen soll.
Es wäre allerdings nett, wenn irgendjemand mal nen Lösungsansatz posten könnte, weil ich hab nämlich bis jetzt keine Ahnung, wie ich das umsetzen soll und ich wollte eigentlich bis zum Schuljahresende fertig sein (Ab da hab ich dann nämlich voraussichtlich kein Latein mehr

).
Verfasst: Samstag 22. September 2007, 20:07
von BlackJack
Du musst wohl etwas umdenken müssen. GUIs sind in der Regel ereignisbasiert, d.h. Du rufst keine Funktion auf die alles steuert, sondern du baust die GUI auf und reagierst auf entsprechende Ereignisse. Also zum Beispiel das ein Button gedrückt wurde.
Verfasst: Samstag 22. September 2007, 20:35
von klaus
Dann muss ich einen Weg finden (falls es einen geben sollte), wie ich es schaffe, in dem Programm ein Ereignis zu erzeugen, auf welches dei GUI dann reagiert.
Verfasst: Samstag 22. September 2007, 21:00
von poker
klaus hat geschrieben:Dann muss ich einen Weg finden (falls es einen geben sollte), wie ich es schaffe, in dem Programm ein Ereignis zu erzeugen, auf welches dei GUI dann reagiert.
Vorausgesetzt du hast die tuts durchgearbeitet, ist das doch relativ simpel.
Du baust dir eine GUI in dem du eine Eingabezeile. Bei jedem eintragen der Antwort erzeugt das ``Eingabezeile``-Widget ein Event. Danach schreibst du eine Methode (Event-Handler) die an das Event gebindet wird. Immer wenn dann das Event ausgelöst wurde, wird dann diese Methode gestartet in den dann dein Code ausgeführt wird.
Verfasst: Sonntag 23. September 2007, 15:51
von klaus
Ich bin jetzt schon mal so weit gekommen:
Code: Alles auswählen
from Tkinter import *
data = ["abc", "a, b, c", 0]
question, solve, stat = data
solution = solve.split(", ")
class Iface:
def __init__(self, master):
frame = Frame(master, width=300, height=200)
frame.pack()
self.label = Label(frame, text=question, pady=10)
self.label.pack()
self.entry = Entry(frame)
self.entry.bind("<Return>", self.correct)
self.entry.pack()
self.solve=Button(frame, text="-------")
self.solve.bind("<1>", self.next)
self.solve.pack()
def correct(self, event):
x = self.entry.get()
print x
if x in solution:
respond = "Richtig!"
else:
respond = "Falsch! Richtig ist "+ solve
self.solve.config(text=respond)
def next(self, event):
pass
root=Tk()
iface=Iface(root)
root.mainloop()
Ich muss es jetzt nur noch hinkriegen, mit next() die nächste Frage aufzurufen, und des ganze überhaupt erstmal zu starten.
Verfasst: Sonntag 23. September 2007, 17:30
von BlackJack
Aktionen für Buttons gibt man in der Regel als `command`-Argument beim Erzeugen des Buttons an. Wenn Du die Maustaste an den Button bindest, verhält sich das ganze nicht so, wie man das von Buttons sonst gewohnt ist.
Verfasst: Montag 24. September 2007, 14:43
von klaus
Wenn ich das mache, ist in der Funktion next die Variable self nicht bekannt. Ich habs nicht geschafft des zu umgehen.
Ich hab jetzt mal wieder ein paar Sachen in Angriff genommen, z.B. dass das Programm nicht mehr reagiert, wenn man 2 Mal hintereinander auf Enter bzw. den Button klickt. Dafür hab ich allerdings ein paar globale Variablen in Kauf nehmen müssen, was ich nicht so toll finde. Ich bin für jeden Verbesserungsvorschlag offen. Hier ist mal Code:
Code: Alles auswählen
from Tkinter import *
index = [["1", "2", 0], ["2", "3", 0], ["3", "5", 0], ["4", "7", 0]]
counter, switch = 0, 0
class Iface:
def __init__(self, master):
global solve
question, solve, stat = index[0]
frame = Frame(master, width=300, height=200)
frame.pack()
self.label = Label(frame, text=question, pady=10)
self.label.pack()
self.entry = Entry(frame)
self.entry.bind("<Return>", self.correct)
self.entry.pack()
self.button=Button(frame, text="-------")
self.button.bind("<1>", self.next)
self.button.pack()
def correct(self, event):
global switch, counter
if switch:
pass
else:
counter = counter + 1
x = self.entry.get()
if x in solve.split(", "):
respond = "Richtig!"
else:
respond = "Falsch! Richtig ist "+ solve
self.button.config(text=respond)
switch = 1 - switch
def next(self, event):
global switch, question, solve, stat
if not switch:
pass
else:
print "Button pressed"
question, solve, stat = index[counter]
self.label.config(text=question)
self.entry.config(text="")
self.button.config(text="-------")
switch = 1 - switch
root=Tk()
iface=Iface(root)
root.mainloop()
Ich würde allerdings noch gerne wissen, ob man ein Entry-Feld reseten kann, damit es wieder leer wird. Ich habe das schon mit
self.entry.config(text="")
probiert, aber es hat nicht geklappt.
Außerdem, wieso funktionieren width und height im Frame (Z 11) nicht?
Verfasst: Montag 24. September 2007, 15:04
von BlackJack
Das kann nicht sein das `self` nicht bekannt ist, das ist das erste Argument von `next()`.
Verfasst: Montag 24. September 2007, 18:54
von klaus
Ja, wenn ich next() über .bind aufruf. Wenn ich es über command aufruf (wie du es vorgeschlagen hast) wird ihm nur das Event als Argument übergeben, aber nicht self.
Es wird ja dann mit command = next aufgerufen (nicht mit self.next).
Die Parameter lauten dann next(event) und nicht next(self, event).
Deswegen muss ich auch mit "<1>" arbeiten. Allerdings weicht das Verhalten gegenüber einem "gewöhnlich" erstellten Button mit command nur sehr gefingfügig ab, was ich wahrscheinlich in Kauf nehmen muss.
Ich bin aber für jede bessere Lösung offen, und wäre froh, wenn mir jemand einen Tipp geben könnte, wie ich index (die Vokabelliste) jetzt noch durch den Benutzer einstellen lassen kann, bevor die Abfrage startet. Bis hierher hab ich mich nämlich schon mehr schlecht als recht durchgewurschtel, und da Tkinter für mich absolutes Neuland ist, gehen mir allmählich die Umsetzungsideen aus.
Verfasst: Montag 24. September 2007, 19:03
von poker
klaus hat geschrieben:
Die Parameter lauten dann next(event) und nicht next(self, event).
Hi versuch es doch mit einem lambda:
Code: Alles auswählen
self.button.bind("<1>", lambda event: self.next(self, event))
Sollte funktionieren.
Verfasst: Montag 24. September 2007, 19:07
von klaus
@ poker: bei .bind klappt es doch. Nur BlackJack hat gesagt, ich soll es mit command umsetzen und da hat die Funktion kein Parameter self. Außerdem weiß ich nicht mal im Ansatz, was lambda bewirkt.
Verfasst: Montag 24. September 2007, 19:24
von poker
klaus hat geschrieben:@ poker: bei .bind klappt es doch. Nur BlackJack hat gesagt, ich soll es mit command umsetzen und da hat die Funktion kein Parameter self. Außerdem weiß ich nicht mal im Ansatz, was lambda bewirkt.
Argh sorry, hab das ganze falsch verstanden.
Labmbda macht eine anonyme Funktion. ``f = lambda x: x`` macht das gleiche wie ``def f(x): return x`` mit dem unterschied das man lambda auch in ausdrücken lokal definieren kann, halt anonym. Damit kann man dann z.B. dirket in einem Funktionsaufruf, der als Parameter eine Funktion erwartet, eine Funktion definieren was mit ``def`` nicht geht.
Verfasst: Montag 24. September 2007, 19:36
von BlackJack
@klaus: Die Funktion hat immer ein Argument `self`, das steht da als erstes in der Argumentliste. Das innerhalb der Methode `self` nicht definiert ist, ist einfach nicht möglich. Und `self` wird auch übergeben wenn man die Funktion von der Instanz holt und dabei eine gebundene Methode bekommt. Da ist absolut kein Unterschied ob man das nun bei `bind` oder beim Button als `command` übergibt.
Verfasst: Montag 24. September 2007, 19:53
von klaus
Oh, ich hab meinen Fehler gemerkt. Ich dachte die Funktion erhält als Argument das Event, aber das wird gar nicht übergeben wenn man sie mit command aufruft.
Habs schon umgeschrieben und es funktioniert.
Übrigens, seh ich des richig, das ich Variablen in der Klasse zwar lesen kann, wenn sie außerhalb definiert sind, wenn ich sie aber ändern will, muss ich sie als global deklarieren?
Verfasst: Montag 24. September 2007, 19:58
von BlackJack
Ja, aber Du willst sie nicht ändern. Oder zumindest solltest Du das nicht wollen.
Verfasst: Montag 24. September 2007, 20:00
von klaus
Wie meinst du das? Ich ändere doch z.B. switch und counter.
edit: Ich glaub ich mach für heute Schluss. Ich programmier erst morgen weiter.
Verfasst: Montag 24. September 2007, 20:11
von BlackJack
Ich meine man will keine "globalen" Variablen ändern, das ist unsauber. Du könntest sie zum Beispiel an das Objekt binden.