Eine grafische Benutzeroberfläche
Zielsetzung
Das Nim-Spiel soll jetzt über eine grafische Benutzeroberfläche gespielt werden. Um die Analyse des Programms einfach zu machen benutzen wir hier vorerst auch eine möglichst einfach gestrickte Benutzerberfläche.
Der Benutzer kann hier Spielinformation auf Schriftfeldern (Label) ablesen, Eingaben in ein Eingabefeld machen und Schaltflächen (Button) anklicken.
GUI-Objekte
Die Grafische Benutzeroberfläche selbst wird mit Objekten von vorgegebenen GUI-Klassen
aus dem Modul tkinter
erzeugt.
#----------------------------------------------------------- # Grafische Benutzeroberfläche #----------------------------------------------------------- from tkinter import * # Ereignisverarbeitung def buttonSpielerClick(): pass def buttonComputerClick(): pass def buttonNeuesSpielClick(): pass # Fenster tkFenster = Tk() tkFenster.title('Nim-Spiel') tkFenster.geometry('350x175') # Rahmen Spieler frameSpieler = Frame(master=tkFenster, bg='#FFCFC9') frameSpieler.place(x=5, y=5, width=110, height=165) # Label mit Überschrift für das Konto labelUeberschriftSpieler = Label(master=frameSpieler, text='Mensch',bg='white') labelUeberschriftSpieler.place(x=5, y=5, width=100, height=20) # Eingabefeld für den Zug des Spielers entryZugSpieler = Entry(master=frameSpieler, bg='white') entryZugSpieler.place(x=45, y=75, width=20, height=20) # Button des Spielers buttonSpieler = Button(master=frameSpieler, text='ziehen', command=buttonSpielerClick) buttonSpieler.place(x=5, y=140, width=100, height=20) # Rahmen Haufen frameHaufen = Frame(master=tkFenster, bg='#D5E88F') frameHaufen.place(x=120, y=5, width=110, height=165) # Label mit Überschrift für den Haufen labelUeberschriftHaufen = Label(master=frameHaufen, text='Haufen', bg='white') labelUeberschriftHaufen.place(x=5, y=5, width=100, height=20) # Label für den Haufen labelHaufen = Label(master=frameHaufen, text='', bg='white') labelHaufen.place(x=45, y=75, width=20, height=20) # Button für ein neues Spiel buttonNeuesSpiel = Button(master=frameHaufen, text='neues Spiel', command=buttonNeuesSpielClick) buttonNeuesSpiel.place(x=5, y=140, width=100, height=20) # Rahmen Computer frameComputer = Frame(master=tkFenster, bg='#FBD975') frameComputer.place(x=235, y=5, width=110, height=165) # Label mit Überschrift für den Computer labelUeberschriftComputer = Label(master=frameComputer, text='Computer', bg='white') labelUeberschriftComputer.place(x=5, y=5, width=100, height=20) # Anzeigefelder für den Zug des Computers labelZugComputer = Label(master=frameComputer, bg='white') labelZugComputer.place(x=45, y=75, width=20, height=20) labelZugComputer.config(text='') # Button des Computers buttonComputer = Button(master=frameComputer, text='ziehen', command=buttonComputerClick) buttonComputer.place(x=5, y=140, width=100, height=20) # Start der Ereignisschleife tkFenster.mainloop()
Beachte, dass diese Benutzeroberfläche noch keinerlei Funktionalitäten bietet. Hier werden ausschließlich die Komponenten der Oberfläche erzeugt.
Aufgabe 1
Analysiere den Quelltext. Welche Objekte werden hier für welche GUI-Komponenten erzeugt? Nähere Informationen zum Aufbau grafischer Benutzeroberflächen findest du im Kapitel Grafische Benutzeroberflächen.
Ein GUI-Manager-Objekt
Für die Strukturierung eines Programms mit grafischer Benutzeroberfläche ist es günstig, ein GUI-Manager-Objekt einzuführen, das für die Erzeugung der GUI-Objekte (und auch für die Durchführung der Anwendung) zuständig ist. Hier der Quelltext zur Erzeugung eines solchen GUI-Manager-Objekts. Wir lassen vorerst jegliche Funktionalität noch weg.
#----------------------------------------------------------- # grafische Benutzeroberfläche #----------------------------------------------------------- from tkinter import * class GUIManager(object): def __init__(self): # Fenster self.tkFenster = Tk() self.tkFenster.title('Nim-Spiel') self.tkFenster.geometry('350x175') # Rahmen Spieler self.frameSpieler = Frame(master=self.tkFenster, bg='#FFCFC9') self.frameSpieler.place(x=5, y=5, width=110, height=165) # Label mit Überschrift für das Konto self.labelUeberschriftSpieler = Label(master=self.frameSpieler, text='Mensch',bg='white') self.labelUeberschriftSpieler.place(x=5, y=5, width=100, height=20) # Eingabefeld für den Zug des Spielers self.entryZugSpieler = Entry(master=self.frameSpieler, bg='white') self.entryZugSpieler.place(x=45, y=75, width=20, height=20) # Button des Spielers self.buttonSpieler = Button(master=self.frameSpieler, text='ziehen', command=self.buttonSpielerClick) self.buttonSpieler.place(x=5, y=140, width=100, height=20) # Rahmen Haufen self.frameHaufen = Frame(master=self.tkFenster, bg='#D5E88F') self.frameHaufen.place(x=120, y=5, width=110, height=165) # Label mit Überschrift für den Haufen self.labelUeberschriftHaufen = Label(master=self.frameHaufen, text='Haufen', bg='white') self.labelUeberschriftHaufen.place(x=5, y=5, width=100, height=20) # Label für den Haufen self.labelHaufen = Label(master=self.frameHaufen, text='', bg='white') self.labelHaufen.place(x=45, y=75, width=20, height=20) # Button für ein neues Spiel self.buttonNeuesSpiel = Button(master=self.frameHaufen, text='neues Spiel', command=self.buttonNeuesSpielClick) self.buttonNeuesSpiel.place(x=5, y=140, width=100, height=20) # Rahmen Computer self.frameComputer = Frame(master=self.tkFenster, bg='#FBD975') self.frameComputer.place(x=235, y=5, width=110, height=165) # Label mit Überschrift für den Computer self.labelUeberschriftComputer = Label(master=self.frameComputer, text='Computer', bg='white') self.labelUeberschriftComputer.place(x=5, y=5, width=100, height=20) # Anzeigefelder für den Zug des Computers self.labelZugComputer = Label(master=self.frameComputer, bg='white') self.labelZugComputer.place(x=45, y=75, width=20, height=20) self.labelZugComputer.config(text='') # Button des Computers self.buttonComputer = Button(master=self.frameComputer, text='ziehen', command=self.buttonComputerClick) self.buttonComputer.place(x=5, y=140, width=100, height=20) # Ereignisverarbeitung def buttonSpielerClick(self): pass def buttonComputerClick(self): pass def buttonNeuesSpielClick(self): pass #----------------------------------------------------------- # Erzeugung des GUI-Objekts #----------------------------------------------------------- guimanager = GUIManager() guimanager.tkFenster.mainloop()
Aufgabe 2
Erläutere die Unterschiede zum Programm oben. Verdeutliche die Objektkonstellation exemplarisch mit einem Objektdiagramm.
Verbindung des GUI-Manager-Objekts mit dem Datenmodell
Das GUI-Manager-Objekt soll jetzt die vorgesehenen Funktionalitäten realisieren. Hierzu soll das GUI-Manager-Objekt die Dienste passender Datenmodellobjekte nutzen.
#----------------------------------------------------------- # Grafische Benutzeroberfläche #----------------------------------------------------------- from tkinter import * class GUIManager(object): def __init__(self, pSpielmanager, pHaufen): # Referenzattribute zum Datenmodell self.rHaufen = pHaufen self.rSpielmanager = pSpielmanager # Fenster self.tkFenster = Tk() self.tkFenster.title('Nim-Spiel') self.tkFenster.geometry('350x175') # Rahmen Spieler self.frameSpieler = Frame(master=self.tkFenster, bg='#FFCFC9') self.frameSpieler.place(x=5, y=5, width=110, height=165) # Label mit Überschrift für das Konto self.labelUeberschriftSpieler = Label(master=self.frameSpieler, text='Mensch',bg='white') self.labelUeberschriftSpieler.place(x=5, y=5, width=100, height=20) # Eingabefeld für den Zug des Spielers self.entryZugSpieler = Entry(master=self.frameSpieler, bg='white') self.entryZugSpieler.place(x=45, y=75, width=20, height=20) # Button des Spielers self.buttonSpieler = Button(master=self.frameSpieler, text='ziehen', command=self.buttonSpielerClick) self.buttonSpieler.place(x=5, y=140, width=100, height=20) self.buttonSpieler.config(state = 'disabled') # Rahmen Haufen self.frameHaufen = Frame(master=self.tkFenster, bg='#D5E88F') self.frameHaufen.place(x=120, y=5, width=110, height=165) # Label mit Überschrift für den Haufen self.labelUeberschriftHaufen = Label(master=self.frameHaufen, text='Haufen', bg='white') self.labelUeberschriftHaufen.place(x=5, y=5, width=100, height=20) # Label für den Haufen self.labelHaufen = Label(master=self.frameHaufen, text='', bg='white') self.labelHaufen.place(x=45, y=75, width=20, height=20) # Button für ein neues Spiel self.buttonNeuesSpiel = Button(master=self.frameHaufen, text='neues Spiel', command=self.buttonNeuesSpielClick) self.buttonNeuesSpiel.place(x=5, y=140, width=100, height=20) # Rahmen Computer self.frameComputer = Frame(master=self.tkFenster, bg='#FBD975') self.frameComputer.place(x=235, y=5, width=110, height=165) # Label mit Überschrift für den Computer self.labelUeberschriftComputer = Label(master=self.frameComputer, text='Computer', bg='white') self.labelUeberschriftComputer.place(x=5, y=5, width=100, height=20) # Anzeigefelder für den Zug des Computers self.labelZugComputer = Label(master=self.frameComputer, bg='white') self.labelZugComputer.place(x=45, y=75, width=20, height=20) self.labelZugComputer.config(text='') # Button des Computers self.buttonComputer = Button(master=self.frameComputer, text='ziehen', command=self.buttonComputerClick) self.buttonComputer.place(x=5, y=140, width=100, height=20) self.buttonComputer.config(state = 'disabled') # Ereignisverarbeitung def buttonSpielerClick(self): zug = int(self.entryZugSpieler.get()) self.rSpielmanager.zugDurchfuehrenMensch(zug) self.haufenAnzeigen() self.gewinnerAnzeigen() self.zugComputerInitialisieren() self.buttonAktivieren() def buttonComputerClick(self): self.rSpielmanager.zugDurchfuehrenComputer() self.zugComputerAnzeigen() self.haufenAnzeigen() self.gewinnerAnzeigen() self.eingabeSpielerInitialisieren() self.buttonAktivieren() def buttonNeuesSpielClick(self): self.rHaufen.initHaufen(16) self.haufenZuBeginnAnzeigen() self.eingabeSpielerInitialisieren() self.zugComputerInitialisieren() self.ueberschriftInitialisieren() self.rSpielmanager.ersterSpieler() self.buttonAktivieren() # Aktualisierung der Anzeige def haufenAnzeigen(self): p = self.rHaufen.getPosition() self.labelHaufen.config(text=str(p)) def haufenZuBeginnAnzeigen(self): p = self.rHaufen.getPosition() self.labelHaufen.config(text=str(p)) def eingabeSpielerInitialisieren(self): self.entryZugSpieler.delete(0, 'end') self.entryZugSpieler.insert(0, '0') def zugComputerAnzeigen(self): z0 = self.rSpielmanager.getZugComputer() self.labelZugComputer.config(text=str(z0)) def zugComputerInitialisieren(self): self.labelZugComputer.config(text='') def buttonAktivieren(self): if self.rSpielmanager.getAktuellerSpieler() == 'Mensch': self.buttonSpieler.config(state = 'normal') self.buttonComputer.config(state = 'disabled') elif self.rSpielmanager.getAktuellerSpieler() == 'Computer': self.buttonSpieler.config(state = 'disabled') self.buttonComputer.config(state = 'normal') def gewinnerAnzeigen(self): if self.rSpielmanager.spielBeendet(): if self.rSpielmanager.getGewinner() == 'Mensch': self.labelUeberschriftSpieler.config(fg='red') self.labelUeberschriftSpieler.config(text='Gewinner') else: self.labelUeberschriftComputer.config(fg='red') self.labelUeberschriftComputer.config(text='Gewinner') def ueberschriftInitialisieren(self): self.labelUeberschriftSpieler.config(fg='black') self.labelUeberschriftSpieler.config(text='Mensch') self.labelUeberschriftComputer.config(fg='black') self.labelUeberschriftComputer.config(text='Computer') #----------------------------------------------------------- # Interaktive Durchführung des Spiels #----------------------------------------------------------- from nim_einhaufen import * # Erzeugung der Datenmodell-Objekte haufen = Nimhaufen(16) mSpieler = SpielerMensch(haufen) cSpieler = SpielerComputer(haufen) spielmanager = Spielmanager(haufen, mSpieler, cSpieler) # Erzeugung und Aktivierung des UI-Objekts guimanager = GUIManager(spielmanager, haufen) guimanager.tkFenster.mainloop()
Beachte, dass hier eine implementierte Version des Datenmodells benutzt wird, die sich in der Datei nim_einhaufen.py befindet.
Aufgabe 3
(a) Teste das gegebene Programm.
(b) Erkläre, wie das GUIManager
-Objekt mit den Datenmodellobjekten
kooperiert und die Spielaktionen durchführt.
Wie wird die Verbindung zwischen dem GUIManager
-Objekt und den Datenmodellobjekten
hergestellt?
Aufgabe 4
Entwickle analog eine GUIManager
-Klasse zum Nim-Spiel mit mehreren Haufen.