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://inf-schule.de/content/7_oop/2_python/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.