Exkurs - Befragen / Beobachten
Modellierung einer Uhr
Zur Modellierung einer Uhr muss die aktuelle Uhrzeit verwaltet werden. Zudem wird eine Operation benötigt, die die Uhrzeit um einen Sekundentick weiterstellt.
class Uhr(object):
def __init__(self):
self.stunden = 0
self.minuten = 0
self.sekunden = 0
def stellen(self, h, m, s):
self.stunden = h
self.minuten = m
self.sekunden = s
def tick(self):
if self.sekunden < 59:
self.sekunden = self.sekunden + 1
else:
self.sekunden = 0
if self.minuten < 59:
self.minuten = self.minuten + 1
else:
if self.stunden < 23:
self.stunden = self.stunden + 1
else:
self.stunden = 0
Eine grafische Benutzeroberfläche zur Anzeige der Uhr
Zur Erzeugung der Benutzeroberfläche wird die folgende Klasse GUIUhr
benutzt:
from tkinter import *
class GUIUhr(object):
def __init__(self, uhr):
# Referenzattribute zum Datenmodell
self.uhr = uhr
# Erzeugung des Fensters
self.fenster = Tk()
self.fenster.title("Uhr")
self.fenster.geometry('120x110')
# Anzeige der Uhr
self.labelUhr = Label(master=self.fenster,
text="",
background="#FBD975")
self.labelUhr.place(x=25, y=20, width=70, height=30)
# Button zum Ticken
self.buttonTick = Button(master=self.fenster,
text="tick",
command=self.Button_Tick_Click)
self.buttonTick.place(x=10, y=80, width=100, height=20)
def Button_Tick_Click(self):
# Verarbeitung der Daten
self.uhr.tick()
# Anzeige der Daten
uhrzeit = str(self.uhr.stunden) + ":" + \
str(self.uhr.minuten) + ":" + \
str(self.uhr.sekunden)
self.labelUhr.config(text=uhrzeit)
Mit dem folgenden Testprogramm lassen sich die Datenmodell- und GUI-Objekte erzeugen.
#-----------------------------------------------------------
# Datenmodell
#-----------------------------------------------------------
from uhr import *
uhr = Uhr()
#-----------------------------------------------------------
# GUI-Objekt
#-----------------------------------------------------------
from guiuhr import *
guiuhr = GUIUhr(uhr)
# Ereignisschleife
guiuhr.fenster.mainloop()
Beim jedem Anklicken der Schaltfläche wird die Uhr um einen Sekundentick weitergestellt.
Das Objekt guiuhr
nutzt eine Strategie, die man Befragen
oder Polling nennt.
Das Objekt guiuhr
weiß
genau, wann sich das Datenmodell ändert (nach jedem Anklicken
der Schaltfläche) und besorgt sich
die geänderten Daten zur Anzeige auf dem vorgesehenen Label.
Modellierung einer tickenden Uhr
Das Modell der Uhr wird erweitert. Ein Uhr
-Objekt soll einen Timer
haben, der selbständig
jede Sekunde die Uhr um einen Sekundentick weiterstellt.
Zur Implementierung werden hier sogenannte Threads benutzt. Du musst die Details nicht verstehen.
Entscheidend ist das Verhalten der Operation ticken
: Jede Sekunde werden die Attribute
zur Erfassung der Uhrzeit aktualisiert.
import threading, time
class Timer(threading.Thread):
def __init__(self,interval,routine):
threading.Thread.__init__(self)
self.interval = interval
self.routine = routine
def run(self):
time.sleep(self.interval)
self.routine()
class Uhr(object):
def __init__(self):
self.stunden = 0
self.minuten = 0
self.sekunden = 0
self.aktiv = False
self.beobachter = None
def setBeobachter(self, beobachter):
self.beobachter = beobachter
def stellen(self, h, m, s):
self.stunden = h
self.minuten = m
self.sekunden = s
self.beobachter.anzeigeAktualisieren()
def stoppen(self):
self.aktiv = False
def tick(self):
if self.sekunden < 59:
self.sekunden = self.sekunden + 1
else:
self.sekunden = 0
if self.minuten < 59:
self.minuten = self.minuten + 1
else:
if self.stunden < 23:
self.stunden = self.stunden + 1
else:
self.stunden = 0
self.beobachter.anzeigeAktualisieren()
if self.aktiv:
self.timer = Timer(1, self.tick)
self.timer.start()
def ticken(self):
self.aktiv = True
self.timer = Timer(1, self.tick)
self.timer.start()
Eine grafische Benutzeroberfläche zur Anzeige der tickenden Uhr
Beim Anklicken der Schaltfläche starten
soll die Uhr anfangen zu ticken,
beim Anklicken der Schaltfläche stoppen
soll sie angehalten werden.
Die Schwierigkeit bei der Anzeige einer tickenden Uhr besteht darin, dass die Befragen
-Strategie
nicht angewandt werden kann.
Die Ereignisse, die zum Ticken der Uhr führen, werden nicht von der Benutzeroberfläche ausgelöst,
sondern vom Timer der Uhr.
Die Benutzeroberfläche kann sich daher die zur Anzeige benötigten aktualisierten Daten nicht selbstständig
besorgen.
Um das Problem der Datenweitergabe nach jedem Sekundentick zu lösen, ist die Klasse Uhr
um ein Attribut beobachter
erweitert worden. Dieses Referenzattribut verwaltet zur Laufzeit
einen Zeiger auf ein GUIUhr
-Objekt. Wenn sich Daten im Uhr
-Objekt verändern,
so wird das GUIUhr
-Objekt angewiesen, die Anzeige zu aktualisieren.
from tkinter import *
class GUIUhr(object):
def __init__(self, uhr):
# Referenzattribute zum Datenmodell
self.uhr = uhr
# Erzeugung des Fensters
self.fenster = Tk()
self.fenster.title("Uhr")
self.fenster.geometry('120x110')
# Anzeige der Uhr
self.labelUhr = Label(master=self.fenster,
text="",
background="#FBD975")
self.labelUhr.place(x=25, y=10, width=70, height=30)
# Button zum Starten
self.buttonStart = Button(master=self.fenster,
text="starten",
command=self.Button_Start_Click)
self.buttonStart.place(x=10, y=50, width=100, height=20)
# Button zum Stoppen
self.buttonStopp = Button(master=self.fenster,
text="stoppen",
command=self.Button_Stopp_Click)
self.buttonStopp.place(x=10, y=80, width=100, height=20)
def Button_Start_Click(self):
# Verarbeitung der Daten
self.uhr.ticken()
def Button_Stopp_Click(self):
# Verarbeitung der Daten
self.uhr.stoppen()
def anzeigeAktualisieren(self):
# Anzeige der Daten
uhrzeit = str(self.uhr.stunden) + ":" + \
str(self.uhr.minuten) + ":" + \
str(self.uhr.sekunden)
self.labelUhr.config(text=uhrzeit)
Mit dem folgenden Testprogramm lassen sich die Datenmodell- und GUI-Objekte erzeugen.
#-----------------------------------------------------------
# Datenmodell
#-----------------------------------------------------------
from uhr import *
uhr = Uhr()
#-----------------------------------------------------------
# GUI-Objekt
#-----------------------------------------------------------
from guiuhr import *
guiuhr = GUIUhr(uhr)
# Beobachter festlegen
uhr.setBeobachter(guiuhr)
# Ereignisschleife starten
guiuhr.fenster.mainloop()
Das Objekt guiuhr
nutzt hier eine Strategie, die man Beobachten nennt.
Das Objekt guiuhr
wird als registriertes beobachtendes Objekt jeweils benachrichtigt,
wenn sich das Datenmodell verändert hat.
Beachte, dass die Klassen Uhr
und GUIUhr
so gestaltet sind, dass
die Trennung zwischen Datenmodell und GUI weiterhin Bestand hat.
Erst zur Laufzeit wird der Beobachter
des Datenmodellobjekts festgelegt.
Bei der Modellierung wird ein Beobachter mit Hilfe eines Referenzattributs vorgesehen, aber noch
nicht konkretisiert.