Seite 1 von 1
Eingabe übergeben
Verfasst: Mittwoch 28. September 2016, 19:11
von HeroTurtle
Hallo,
ich muss für ein Uni Projekt ein Billiard Spiel programmieren. Dazu haben ich nun eine kleine Oberfläche geschaffen(ist noch in ihren Anfängen) die es ermöglicht Spielernamen einzugeben und anschließend das Spiel mit den Namen zu starten. Zusätzlich gibt es noch ein Error-Fenster.
Nun zu meinem Problem, die Namen die ich eingebe werden nicht übermittelt bzw. die "error-abfrage" funktioniert nicht richtig.
Ich bin noch ein Python-Leihe, deshalb ist der Code wsh. etwas unübersichtlich.
Vielen Dank vorab für eure Hilfe !
Code: Alles auswählen
import tkinter as tk
root = tk.Tk()
root.wm_title("Billard") # WindowTitle
#Input Player Name
player1_name = tk.StringVar()
player2_name = tk.StringVar()
label1 = tk.Label(root, text = 'Name: Player 1')
label1.pack()
player1 = tk.Entry(root, textvariable = player1_name)
player1.pack()
label2 = tk.Label(root, text = 'Name: Player 2')
label2.pack()
player2 = tk.Entry(root, textvariable = player2_name)
player2.pack()
#starts the game and opens the game window
def click():
#close window for name input
def close_root():
root.destroy()
if player1 == None or player2 == None:
errorwindow()
else:
gamewindow()
close_root()
#gamewindow
def gamewindow():
import tkinter as tk
window = tk.Tk()
window.wm_title("Billard") # WindowTitle
name1 = tk.Label(window, player1)
name1.pack()
name2 = tk.Label(window, player2)
name2.pack()
#hier kommt der Befehl zum Hauptprogramm
window.mainloop
#errorwindow open if name isn't given
def errorwindow():
import tkinter as tk
e_window = tk.Tk()
e_window.wm_title("Error") # WindowTitle
label = tk.Label(root, text = 'Error: Please give 2 Names')
label.pack()
#close errorwindow
def close():
e_window.destroy()
#ok button in errorwindow
button = tk.Button(root, text='OK', command = close())
button.pack()
e_window.mainloop
button = tk.Button(root, text='Start the Game', command = click)
button.pack()
root.mainloop()
Re: Eingabe übergeben
Verfasst: Mittwoch 28. September 2016, 19:29
von Sirius3
@HeroTurtle: ich verstehe nicht was der Code machen soll. Auch Deine Erklärung helfen nicht wirklich. Was willst Du erreichen?
Zum Code: es darf nur ein Tk-Objekt geben, weitere Fenster werden von TopLevel abgeleitet. Imports sollten immer ganz am Anfang der Datei stehen, damit man gleich die Abhängigkeiten erkennt. Mehrmals tktinter zu importieren hat auch keinen Effekt. Die Bedingung in Zeile 17 wird nie erfüllt sein, weil player1 und player2 ja tk.Entry-Objekte sind. In Zeilen 41/44 was sollen Labels mit Entry-Objekten anfangen? Meinst Du player1_name? Zeile 49: Funktionen müssen aufgerufen werden.
Funktionen innerhalb von Funktionen zu definieren, macht nur in seltenen Fällen Sinn. Für GUI-Programmierung braucht man fast immer Klassen um Zustände zu speichern, vor allem wenn man mehrere Fenster hat.
Re: Eingabe übergeben
Verfasst: Mittwoch 28. September 2016, 19:42
von HeroTurtle
Also ich möchte zu Beginn ein Fenster haben, in dem man die beiden Spielernamen eingeben kann. Das Design etc folgt später noch. Zudem soll das Fenster einen Start Button beinhalten, der das aktuelle Fenster schließt und dann das Hauptprogramm lädt. Sprich das Billard Spiel. In dem Fenster soll dann über dem Brett die beiden Namen angezeigt werden die zuvor eingegeben wurden plus die Kugeln welche versenkt wurden(das billard programm habe ich soweit fertig).
Wenn nur ein Name eingeben wird, soll ein Fehler Fenster geöffnet werden, welches den Fehler anzeigt und wenn man dort auf "OK" klickt dieses geschlossen wird.
Re: Eingabe übergeben
Verfasst: Donnerstag 29. September 2016, 14:18
von HeroTurtle
Habe es jetzt soweit geschafft, dass die Namen übernommen werden. Und ich hoffe der Code ist jetzt etwas strukturierter.
Allerdings will er nicht das Error-Fenster öffnen, da e_window nicht definiert ist, "line 26, in close_ewindow e_window.destroy() NameError: name 'e_window' is not defined".
Woran liegts ?
Code: Alles auswählen
import tkinter as tk
root = tk.Tk()
root.wm_title("Billard") # WindowTitle
player1_name = tk.StringVar()
label1 = tk.Label(root, text = 'Name: Player 1')
label1.pack()
player1 = tk.Entry(root, textvariable = player1_name)
player1.pack()
player1_name.set('')
player2_name = tk.StringVar()
label2 = tk.Label(root, text = 'Name: Player 2')
label2.pack()
player2 = tk.Entry(root, textvariable = player2_name)
player2.pack()
player2_name.set('')
#close window for name input
def close_root():
root.destroy()
#close errorwindow
def close_ewindow():
e_window.destroy()
def click():
player1_name.set(player1_name.get())
player2_name.set(player2_name.get())
if player1_name.get() == '' or player2_name.get() == '':
errorwindow()
else:
gamewindow()
close_root()
#gamewindow
def gamewindow():
window = tk.Tk()
window.wm_title("Billard") # WindowTitle
label1 = tk.Label(window, text = player1_name.get())
label1.pack()
label2 = tk.Label(window, text = player2_name.get())
label2.pack()
window.mainloop
#errorwindow open if name isn't given
def errorwindow():
e_window = tk.Tk()
e_window.wm_title("Error") # WindowTitle
label = tk.Label(e_window, text = 'Error: Please give 2 Names')
label.pack()
#ok button in errorwindow
button = tk.Button(e_window, text='OK', command = close_ewindow)
button.pack()
e_window.mainloop
button = tk.Button(root, text='Start the Game', command = click)
button.pack()
root.mainloop()
Re: Eingabe übergeben
Verfasst: Donnerstag 29. September 2016, 14:25
von Sirius3
@HeroTurtle: der NameError ist nicht das einzige Problem. Wie schon geschrieben solltest Du unbedingt objektorientiert mit Klassen programmieren, und keine globalen Variablen verwenden, bzw. an deren Nicht-Vorhandensein scheitern. Alle Anweisungen, die nicht (Funktions- oder Klassen-) Definitionen sind, sollten in einer Funktion stehen, alles, was Du jetzt noch auf oberster Ebene stehen hast, gehört also auch in eine Funktion, die üblicherweise main genannt wird. Dann kommt nämlich noch dazu, dass click z.B. player1_name und player2_name nicht mehr kennt; die Lösung ist Objektorientierung. Im Übrigen gilt das zuletzt gesagte: es darf nur eine Tk-Instanz und nur einen mainloop-Aufruf geben. Zeilen 29/30 sind ziemlich sinnfrei.
Re: Eingabe übergeben
Verfasst: Donnerstag 29. September 2016, 14:27
von HeroTurtle
könntest du mir eventuell helfen das ganze objektorientiert zu programmieren ? Habe damit nicht all zu viel erfahrung

Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 06:50
von Sirius3
@HeroTurtle: eine erste Version könnte so aussehen
Code: Alles auswählen
import tkinter as tk
class ErrorWindow(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.wm_title("Error") # WindowTitle
tk.Label(self, text='Error: Please give 2 Names').pack()
tk.Button(self, text='OK', command=self.destroy).pack()
class NamesWindow(tk.Toplevel):
def __init__(self, parent, player1_name, player2_name):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.player1_name = player1_name
self.player2_name = player2_name
self.wm_title("Billard") # WindowTitle
tk.Label(self, text = 'Name: Player 1').pack()
tk.Entry(self, textvariable=player1_name).pack()
tk.Label(self, text = 'Name: Player 2').pack()
tk.Entry(self, textvariable=player2_name).pack()
tk.Button(self, text='Start the Game', command=self.click).pack()
self.protocol("WM_DELETE_WINDOW", parent.destroy)
def click(self):
if self.player1_name.get() == '' or self.player2_name.get() == '':
ErrorWindow(self)
else:
self.parent.deiconify()
self.destroy()
class GameWindow(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.wm_title("Billard") # WindowTitle
self.player1_name = tk.StringVar()
self.player2_name = tk.StringVar()
tk.Label(self, textvariable=self.player1_name).pack()
tk.Label(self, textvariable=self.player2_name).pack()
NamesWindow(self, self.player1_name, self.player2_name)
def main():
root = GameWindow()
root.withdraw()
root.mainloop()
if __name__ == '__main__':
main()
Da gibt es natürlich noch viel zu tun. Die Dialoge sollten modal sein, Eingaben möchte man gerne mit Enter abschließen oder per Escape abbrechen. Und eigentlich möchte man Dialoge gar nicht selbst programmieren, sondern welche mit Hilfe von tkinter.simpledialog erzeugen.
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 11:33
von HeroTurtle
@Sirius3 : Vielen Dank schon mal für deine Mühe

!!
Ich bin wie gesagt noch ein Leihe was das Programmieren mit Python angeht. Was genau meinst du mit "modale Dialoge" ?
Mein Plan ist es jetzt noch dem Nameswindow einen Hintergrund zu verpassen und etwas schöner zu gestalten. Zudem soll natürlich in dem GamesWindow das Spiel eingebaut werden und die Namen oberhalb des Spiels nebeneinander liegen mit den versenkten Kugeln darunter. Ich bin es gewohnt von anderen Sprachen, das Fenster in Bereiche zu unterteilen und diesen dann die Inhalte zuzuweisen. Ist dies in Python ähnlich ?
Noch ein paar Fragen zum Code, was machen folgende Befehle?:
-Zeile 22: self.protocol("WM_DELETE_WINDOW", parent.destroy)
-Zeile 43: root.withdraw()
-generell was ist dieses "tk.Toplevel"
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 12:24
von BlackJack
@HeroTurtle:
Modale und nicht-modale Dialoge.
Hintergründe scheitern oft daran das Widgets nicht durchsichtig sind. `Label` beispielsweise hat immer seine eigene Hintergrundfarbe. Solche Spielereien würde ich mir bei Tk verkneifen. Das ist mehr Aufwand als es Wert ist.
Das Layout von GUIs hat nichts mit Python zu tun, sondern wie die GUI-Bibliothek das handhabt. Tk bietet `pack()` und `grid()` als Layouts an, bei denen sich Tk um alles wichtige kümmert. Man darf die beiden Varianten innerhalb eines Container-Widgets nicht mischen, das kann zu eingefrorenen Programmen führen, und das visuelle Ergebnis lässt sich nicht wirklich vorhersagen. Container-Widgets sind die Fenster, also `Tk` und `Toplevel`, und `Frame` und `LabeledFrame`. Die letzten beiden kann man verschachteln. Damit kann man also die Aufteilung der GUI erreichen.
Die
protocol()- und
withdraw()-Methode sind bei Effbot dokumentiert.
`Toplevel` hat Sirius3 schon gesagt: Fenster. `Tk` ist *das* Hauptfenster, davon darf es nur ein Exemplar im Programm geben weil da der komplette Tk/Tcl-Interpreter dran hängt. Also muss man andere, zusätzliche Fenster mit `Toplevel` erstellen.
Die GUI-Anbindungen in Python sind fast alle nur sehr dünne Schichten über die jeweiligen Bibliotheken. Es ist also hilfreich wenn man auch mal einen Blick in die Originaldokumentation der jeweiligen Bibliothek schaut, falls irgend etwas nicht klar ist.
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 12:43
von HeroTurtle
@BlackJack: Vielen Dank ! Solche Erklärungen helfen mir sehr !
Habe mittlerweile mal versucht ein Hintergrund einzubauen, aber dein erster Satz erklärt wsh. warum es nicht klappt :/ ! Wenn ich den Code alleine in ein Skript schreibe funktioniert es. Allerdings in meinem Skript nicht. Hier mal der Auszug (Zeile 9-14 sind hinzugefügt bzw geändert worden):
Code: Alles auswählen
class NamesWindow(tk.Toplevel):
def __init__(self, parent, player1_name, player2_name):
tk.Toplevel.__init__(self, parent)
self.parent = parent
self.player1_name = player1_name
self.player2_name = player2_name
self.wm_title("Billard") # WindowTitle
image = tk.PhotoImage(file = "billard.gif")
w = image.width()
h = image.height()
self.geometry("%dx%d" % (w,h))
hintergrund = tk.Label(self, image = image)
hintergrund.pack(fill='both', expand='yes')
tk.Label(hintergrund, text = 'Name: Player 1').pack()
tk.Entry(hintergrund, textvariable=player1_name).pack()
tk.Label(hintergrund, text = 'Name: Player 2').pack()
tk.Entry(hintergrund, textvariable=player2_name).pack()
tk.Button(hintergrund, text='Start the Game', command=self.click).pack()
self.protocol("WM_DELETE_WINDOW", parent.destroy)
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 13:00
von BlackJack
@HeroTurtle: Das mit dem Hintergrund funktioniert so nicht, weil das `pack()`-Layout die Elemente natürlich nicht übereinander legt, sondern ”stapelt”. Also ohne Angabe einer Seite die Elemente untereinander anordnet. Im Falle eines Hintergrundbildes muss man dann tatsächlich mal Layout-Manager in einem Container mischen, und zwar `place()` und zum Beispiel `pack()`.
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 13:15
von HeroTurtle
@BlackJack
Entschuldigung aber das mit dem Layout-Manager und dem place() versteh ich nicht so ganz. Was genau muss ich denn jetzt machen, damit mein Bild angezeigt wird ?
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 15:01
von BlackJack
@HeroTurtle: Ich sehe gerade das Du da noch einen Fehler gemacht hast: `Label` sind keine Container-Widgets, die kann man nicht als Elternwidget für andere verwenden. Du musst in das gleiche Container-Widget das Bild und die anderen Widgets stecken und das Bild mit `place()` absolut positionieren und die anderen mit `pack()` oder `grid()` anordnen.
Code: Alles auswählen
import tkinter as tk
def main():
root = tk.Tk()
background_image = tk.PhotoImage(file='test.gif')
tk.Label(root, image=background_image).place(x=0, y=0)
root.geometry(
'{0}x{1}'.format(background_image.width(), background_image.height())
)
tk.Label(root, text='Test').pack()
tk.Entry(root).pack()
root.mainloop()
if __name__ == '__main__':
main()
Re: Eingabe übergeben
Verfasst: Freitag 30. September 2016, 15:31
von HeroTurtle
@BlackJack:
Hast ne Pn !
Re: Eingabe übergeben
Verfasst: Samstag 1. Oktober 2016, 13:33
von HeroTurtle
Genügt es, wenn ich deinen Code für "main" in meinem Programm ersetze oder muss ich noch mehr ändern ?
Re: Eingabe übergeben
Verfasst: Samstag 1. Oktober 2016, 16:19
von BlackJack
@HeroTurtle: Nein, das genügt natürlich nicht, was leicht auszuprobieren gewesen wäre. Ich habe ja nur gezeigt wie man ein Hintergrundbild in einem Container-Widget (schlecht und grafisch unschön) realisieren kann. Du musst das Wissen dann schon noch auf das Container-Widget übertragen wo Du diese Hintergrundbild haben möchtest. Ich habe Dein Programm ja nicht nachgebaut, sondern einfach nur ein Hauptfenster erstellt und die Elemente dort drin entsprechend angeordnet.
Oh, und auch noch wichtig: Man muss eine Referenz auf das Bild-Objekt auf Python-Seite behalten. Das passiert bei mir ”automatisch” weil es an einen lokalen Namen in der Funktion gebunden ist, die während des Anzeigens aktiv ist. Wenn die Funktion oder Klasse aber verlassen würde und der `mainloop()`-Aufruf ausserhalb der Funktion stehen würde, dann verschwindet der lokale Name, und damit auch das Objekt auf Python-Seite und damit auch das Bild auf Tk-Seite und es kann nicht mehr angezeigt werden.