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.

Benutzeroberfläche

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.

Benutzeroberfläche

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.

X

Fehler melden

X

Suche