s n h m r u
i

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

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
#-----------------------------------------------------------
<p>from tkinter import *</p>
<h1>Ereignisverarbeitung</h1>
<p>def buttonSpielerClick():<br />
pass</p>
<p>def buttonComputerClick():<br />
pass</p>
<p>def buttonNeuesSpielClick():<br />
pass</p>
<h1>Fenster</h1>
<p>tkFenster = Tk()<br />
tkFenster.title('Nim-Spiel')<br />
tkFenster.geometry('350x175')</p>
<h1>Rahmen Spieler</h1>
<p>frameSpieler = Frame(master=tkFenster, bg='#FFCFC9')<br />
frameSpieler.place(x=5, y=5, width=110, height=165)</p>
<h1>Label mit Überschrift für das Konto</h1>
<p>labelUeberschriftSpieler = Label(master=frameSpieler,<br />
text='Mensch',bg='white')<br />
labelUeberschriftSpieler.place(x=5, y=5, width=100, height=20)</p>
<h1>Eingabefeld für den Zug des Spielers</h1>
<p>entryZugSpieler = Entry(master=frameSpieler, bg='white')<br />
entryZugSpieler.place(x=45, y=75, width=20, height=20)</p>
<h1>Button des Spielers</h1>
<p>buttonSpieler = Button(master=frameSpieler, text='ziehen',<br />
command=buttonSpielerClick)<br />
buttonSpieler.place(x=5, y=140, width=100, height=20)</p>
<h1>Rahmen Haufen</h1>
<p>frameHaufen = Frame(master=tkFenster, bg='#D5E88F')<br />
frameHaufen.place(x=120, y=5, width=110, height=165)</p>
<h1>Label mit Überschrift für den Haufen</h1>
<p>labelUeberschriftHaufen = Label(master=frameHaufen,<br />
text='Haufen', bg='white')<br />
labelUeberschriftHaufen.place(x=5, y=5, width=100, height=20)</p>
<h1>Label für den Haufen</h1>
<p>labelHaufen = Label(master=frameHaufen, text='', bg='white')<br />
labelHaufen.place(x=45, y=75, width=20, height=20)</p>
<h1>Button für ein neues Spiel</h1>
<p>buttonNeuesSpiel = Button(master=frameHaufen, text='neues Spiel',<br />
command=buttonNeuesSpielClick)<br />
buttonNeuesSpiel.place(x=5, y=140, width=100, height=20)       </p>
<h1>Rahmen Computer</h1>
<p>frameComputer = Frame(master=tkFenster, bg='#FBD975')<br />
frameComputer.place(x=235, y=5, width=110, height=165)</p>
<h1>Label mit Überschrift für den Computer</h1>
<p>labelUeberschriftComputer = Label(master=frameComputer,<br />
text='Computer', bg='white')<br />
labelUeberschriftComputer.place(x=5, y=5, width=100, height=20)</p>
<h1>Anzeigefelder für den Zug des Computers</h1>
<p>labelZugComputer = Label(master=frameComputer, bg='white')<br />
labelZugComputer.place(x=45, y=75, width=20, height=20)<br />
labelZugComputer.config(text='')</p>
<h1>Button des Computers</h1>
<p>buttonComputer = Button(master=frameComputer, text='ziehen',<br />
command=buttonComputerClick)<br />
buttonComputer.place(x=5, y=140, width=100, height=20)</p>
<h1>Start der Ereignisschleife</h1>
<p>tkFenster.mainloop()<br />

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
#-----------------------------------------------------------
<p>from tkinter import *</p>
<p>class GUIManager(object):</p>
<pre><code>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 &Uuml;berschrift f&uuml;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&uuml;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 &Uuml;berschrift f&uuml;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&uuml;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&uuml;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 &Uuml;berschrift f&uuml;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&uuml;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
#-----------------------------------------------------------
<p>from tkinter import *</p>
<p>class GUIManager(object):</p>
<pre><code>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 &Uuml;berschrift f&uuml;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&uuml;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 &Uuml;berschrift f&uuml;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&uuml;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&uuml;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 &Uuml;berschrift f&uuml;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&uuml;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.

Suche

v
7.2.2.3.3.2 Eine grafische Benutzeroberfläche
Kopieren durch Anklicken

Rückmeldung geben