Fenster und Knöpfe

Zunächst machen wir uns klar, dass man im Sinne einer übersichtlichen und pflegeleichten Software verlangen muß, das sogenannte "Fachkonzept" von der Benutzerschnittstelle zu trennen. Die Benutzerschnittstelle (hier ein Objekt der Klasse "Eingabe_Fenster") muß natürlich das Modell (den Zähler) kennen, um ihn zu steuern. Der Programmcode für die Steuerung aber muß in einer getrennten Klasse vorliegen.

Wir betrachten den folgenden Quelltext (Datei "eingabe0.py"):

import Tkinter									#1
from Tkconstants import *							#2

class Eingabe_Fenster:								#4
   def __init__(self,Counter):							#5
      self.Counter=Counter							#6
      window=Tkinter.Tk()      							#7
      fensterbreite=str(220)							#8
      fensterhoehe=str(100)							#9
      geometrie=fensterbreite+"x"+fensterhoehe+"+100+100"			#10
      window.geometry(geometrie)						#11
      window.title("Dr. Kokavecz")						#12
      Knopf1=Tkinter.Button(window, text=" aufwärts ", command=self.plus) 	#13
      Knopf1.pack()								#14
      Knopf2=Tkinter.Button(window, text="  abwärts ", command=self.minus) 	#15
      Knopf2.pack()								#16
      Knopf3=Tkinter.Button(window, background="red", \				#17
                                        text="   ENDE   ", command="exit") 
      Knopf3.pack()								#18


   def plus(self):								#21
      self.Counter.hochzaehlen()						#22
      print self.Counter.hat_Zaehlerstand()    # Testausgabe			#23

   def minus(self):								#25
      self.Counter.herunterzaehlen()						#26
      print self.Counter.hat_Zaehlerstand()    # Testausgabe			#27

Erläuterungen zum Quelltext:

In Zeile 1 wird das Grafik-Toolkit (ein packet) von Ousterhout importiert. Bei dieser Art des Imports muss immer der Paketname (Tkinter) mit angegeben werden, wenn auf die Klasse (Tk) zugegriffen wird (z.B.: Tkinter.Tk).
Tkconstants stellt einen Satz von Parameternamen zur Verfügung, der zur "Konfektion" von Knöpfen und Fenstern erforderlich ist (z.B. background).

In Zeile 4 kann man erkennen, dass keine Vererbung stattfindet.
In Zeile 5 wird ersichtlich, dass bei der Erzeugung eines Objekts durch die Klasse "Eingabe_Fenster" ein Parameter (Counter) angegeben werden muß. Counter ist die Referenz auf ein Objekt der Klasse Zähler o.ä.. Eingabe_Fenster muss den Zähler kennen, um ihn ansteuern zu können. Die Zuweisung in Zeile 6 ist erforderlich, damit alle Methoden der Klasse "Eingabe_Fenster" Zugriff auf den Zähler haben.
window wurde das Objekt genannt, das mit Tk erzeugt wurde. Es ist das auf dem Bildschirm sichtbare Fenster für die Eingabe, das in der Zeile 11 mit der Methode "geometry" in Größe und Lage konfiguriert wird. windows.Title ist eine in Tk erklärte Methode und legt die Fensterüberschrift fest (Zeile 12).

Mit Hilfe der Klasse Button werden drei Knopf-Objekte erzeugt. Die Methode "pack" plaziert sie auf dem Bildschirm. Sie liegen alle in "window", was am ersten Parameter in den Zeilen 13 , 15, 17 zu erkennen ist. Wie die Anordnung der Knöpfe erfolgt, ließe sich durch zusätzliche Parameter bei "pack" steuern, hier wird einfacherweise mit den Defaultwerten gearbeitet.
Die Zeilen 23 und 27 können später entfallen.

Als Testumgebung hilft uns main3.py:

#! /usr/bin/python

from zaehler import *
from eingabe0 import *

mein_Zaehler=Moderner_Zaehler()
mein_Zaehler.setzen(100)
meine_Eingabe=Eingabe_Fenster(mein_Zaehler)
Tkinter.mainloop()

Wir erhalten auf dem Bildschirm:

Zaehler

Interessant ist noch die letzte Zeile der Testumgebung:
Der Name Tkinter.mainloop() soll dem naiven Benutzer die Vorstellung vermitteln, in dieser "Warteschleife" verharre das Programm, bis es, durch ein Ereignis aufgeweckt, wieder weiterarbeitet. Der Sachverhalt ist jedoch komplexer, und soll hier (dennoch stark vereinfacht) dargestellt werden:
Die Methode mainloop aus Tkinter legt das Programm (incl. Python-Runtimesystem bzw. -Interpreter) schlafen. Wie der weitere Verlauf des Programms aussieht, hängt von äußeren Aktionen ab und ist deshalb innerhalb des Pythonprogramms algorithmisch nicht geschlossen darstellbar, es sei denn, man bezieht die Aktionen des Betriebssystems und die des Benutzers mit ein.
Wird beispielsweise die linke Maustaste gedrückt, so wird zunächst ein Hardware-Interrupt ausgelöst. Eine Hardware-Prioritätenkette bestimmt, wann das Betriebssystem darauf reagiert (Festplattenzugriffe sind oft wichtiger als Mausklicks). Reagiert der Interrupthandler des Betriebssystem auf das Mausklicken, so wird an Hand der Koordinaten u.U. entschieden, daß das Pythonprogramm (zunächst das Runtimesystem) wieder aus dem Schlaf geweckt werden soll. Auch dieses Programm startet mit einer Art Interrupthandler, denn es muß nun an Hand der übermittelten Mauskoordinaten entscheiden, welche Methode für welches Objekt jetzt aufgerufen werden muss. Um diese Frage zu entscheiden, hat das Runtimesystem von Python eine Tabelle der per Maus ansprechbaren Objekte angelegt und zu jedem Objekt eine Referenz auf die gewünschte Methode gespeichert.

Die entsprechende Referenz wird zum Beispiel in Zeile 13 hinter "command" dem System mitgeteilt. Dort steht tatsächlich kein Methodenaufruf, sondern eine Methodenreferenz (Adresse), da hinter self.plus keine Klammern stehen!


zurück

Dr. Bernd Kokavecz
15.12.99