s n h m r u
i

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
<pre><code>def stellen(self, h, m, s):
    self.stunden = h
    self.minuten = m
    self.sekunden = s

def tick(self):
    if self.sekunden &lt; 59:
        self.sekunden = self.sekunden + 1
    else:
        self.sekunden = 0
        if self.minuten &lt; 59:
            self.minuten = self.minuten + 1
        else:
            if self.stunden &lt; 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 *
<p>class GUIUhr(object):<br />
def <strong>init</strong>(self, uhr):</p>
<h1>Referenzattribute zum Datenmodell</h1>
<pre><code>    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
#-----------------------------------------------------------
<p>from uhr import *<br />
uhr = Uhr()</p>
<h1>-----------------------------------------------------------</h1>
<h1>GUI-Objekt</h1>
<h1>-----------------------------------------------------------</h1>
<p>from guiuhr import *<br />
guiuhr = GUIUhr(uhr)</p>
<h1>Ereignisschleife</h1>
<p>guiuhr.fenster.mainloop()<br />

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
<p>class Timer(threading.Thread):<br />
def <strong>init</strong>(self,interval,routine):<br />
threading.Thread.<strong>init</strong>(self)<br />
self.interval = interval<br />
self.routine = routine</p>
<pre><code>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 &lt; 59:
        self.sekunden = self.sekunden + 1
    else:
        self.sekunden = 0
        if self.minuten &lt; 59:
            self.minuten = self.minuten + 1
        else:
            if self.stunden &lt; 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 *
<p>class GUIUhr(object):<br />
def <strong>init</strong>(self, uhr):</p>
<h1>Referenzattribute zum Datenmodell</h1>
<pre><code>    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
#-----------------------------------------------------------
<p>from uhr import *<br />
uhr = Uhr()</p>
<h1>-----------------------------------------------------------</h1>
<h1>GUI-Objekt</h1>
<h1>-----------------------------------------------------------</h1>
<p>from guiuhr import *<br />
guiuhr = GUIUhr(uhr)</p>
<h1>Beobachter festlegen</h1>
<p>uhr.setBeobachter(guiuhr)</p>
<h1>Ereignisschleife starten</h1>
<p>guiuhr.fenster.mainloop()<br />

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.

Suche

v
7.2.3.7.6 Exkurs - Befragen / Beobachten
Kopieren durch Anklicken

Rückmeldung geben