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.
![GUI](https://schuljahr.inf-schule.de/2019-20/content/3-modellierung/2-ooppython/2-spiele/3-benutzeroberflaechen/3-ui/2-gui/gui_nim_einhaufen.png)
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.