Ereignisgesteuerte Animation
Event-driven Animation - Abstract
Algorithms can be visualized by means of event-driven animation. Within an event-driven animation, the class containing the algorithm is programmed as usual, the only difference being the definition of custom events at the critical points of the algorithm. For the animation, the algorithm will be run. During execution the events triggered by the algorithm will be received within an object of another class, the Animator class. The Animator object will then transform the events into the animation output to be seen by the user.
Mit Hilfe der ereignisgesteuerten Programmierung lassen sich in Excel-VBA gut Animationen erzeugen, welche den Ablauf von Algorithmen veranschaulichen. Gegenüber dem Einsatz eines spezialisierten Animationswerkzeugs hat diese Lösung folgende Vorteile
- Es muss kein zusätzliches Animationswerkzeug gekauft werden
- Die ursprüngliche Programmierung des Algorithmus, der veranschaulicht werden soll, kann beibehalten werden. Sie muss lediglich um einige Anweisungen ergänzt werden, welche die für die Animation wichtigen Ereignisse auslösen.
- Die Animationslösung kann eine Schichtenarchitektur besitzen, welche eine klare Trennung zwischen Programmkern und Benutzersteuerung (UI) einschließt.
Prinzip der ereignisgesteuerten Animation
Das Prinzip ist einfach und lässt sich in einem Satz beschreiben:
Bei der Durchführung des Algorithmus werden an den entscheidenden Stellen Ereignisse ausgelöst, welche von einem anderen Modul aufgefangen und in eine bildliche Darstellung umgesetzt werden.
Erläuterung an einem Beispiel
Zur Illustration der Technik soll ein ganz einfaches Beispiel verwendet werden. Der zu animierende Algorithmus produziert die Collatzfolgen für eine Liste von Startzahlen. Die Animation soll daraus bestehen, dass die Ausgabe des Ergebnisses nicht en bloc auf einem Arbeitsblatt erfolgt, wie dies normalerweise geschähe, sondern dass die Ausgabe so verzögert erfolgt, dass der Betrachter die Folge Zahl für Zahl entstehen sieht. Außerdem sollen gerade und ungerade Folgenglieder in unterschiedliche Farben ausgegeben werden, weil die Berechnung eines Folgenelements davon abhängt, ob der Vorgänger gerade ist oder nicht.
Für die Folgen mit den Startzahlen 2 bis 7 würde die Ausgabe nach Ende der Animation folgendermaßen aussehen:
2 |
1 |
|||||||||||||||
3 |
10 |
5 |
16 |
8 |
4 |
2 |
1 |
|||||||||
4 |
2 |
1 |
||||||||||||||
5 |
16 |
8 |
4 |
2 |
1 |
|||||||||||
6 |
3 |
10 |
5 |
16 |
8 |
4 |
2 |
1 |
||||||||
7 |
22 |
11 |
34 |
17 |
52 |
26 |
13 |
40 |
20 |
10 |
5 |
16 |
8 |
4 |
2 |
1 |
Wie man sieht, wurden gerade Zahlen blau gedruckt, ungerade rot.
Der Aufbau des Animationsprogramms
Wir können für diese und ähnliche Animationen eine dreischichtige Architektur verwenden. Im folgenden Bild ist auf der linken Seite der Aufbau in allgemein formulierten Modulbenennungen abgebildet, rechts sind die in unserem Beispiel verwendeten Module zu sehen. Bei den beiden unteren Schichten handelt es sich jeweils um Klassenmodule. Dies ist eine Voraussetzung für die Anwendung der ereignisgesteuerten Programmierung.

Mit Hilfe der obersten Schicht, dem UI, werden die Parameter der Animation gesetzt bzw. eingelesen. In unserem Beispiel gehören zu den Parametern die Liste der Startzahlen, das Arbeitsblatt, auf dem die Animation ausgegeben werden soll, und die Dauer der zeitlichen Verzögerung, die zwischen die einzelnen Durchführungsschritte geschoben werden soll.
Das UI gibt die Parameter mit einem Aufruf an die zweite Schicht weiter, ein Objekt der Animator-Klasse bzw., in unserem konkreten Fall, ein Objekt der Klasse CollatzAnimator. Dieses Objekt führt die Animation durch, indem es ein Objekt der untersten Schicht mit der Durchführung des Algorithmus beauftragt, die in diesem Objekt ausgelösten Ereignisse auffängt und in die bildliche Darstellung auf dem Arbeitsblatt einfügt.
Welche Ereignisse für die Animation relevant sind, hängt von dem zu animierenden Algorithmus ab und muss vom Programmierer entschieden werden. Gewöhnlich wird man die Stellen im Algorithmus auswählen, an denen „die Weichen gestellt“ werden. In unserem Beispiel sind dies zwei Ereignisse:
- der Beginn einer neuen Collatzfolge,
- die Errechnung eines weiteren Elements in der Folge, die gerade entwickelt wird.
Das Programm
Wir beginnen mit der Betrachtung des Codes auf der untersten Stufe der Architektur. Dies ist die Klasse CollatzSequence. Sie besteht aus zwei Funktionen. Die Funktion CollatzArray2 liefert die Folgen für die in z gelisteten Startzahlen als geschachteltes Array vom Typ Variant. In diesem Ergebnis-Array ist für jede Startzahl ein Array vom Typ Long enthalten, das von der Funktion CollatzFolge an Funktion CollatzArray2 geliefert wird.
Der Code der Klasse ist gegenüber einer Normalfassung, welche nur der Berechnung der Folgen dient, nahezu unverändert. Lediglich die Definition der Ereignisse und die RaiseEvent-Anweisungen zur Auslösung dieser Ereignisse sind hinzugekommen (grün hervorgehoben).
Beachten Sie, dass die Ereignisse mit Parametern definiert sind. Diese Parameter enthalten die Informationen, welche innerhalb der Animator-Klasse zur Darstellung der Ereignisse auf dem Arbeitsblatt erforderlich sind.
‘----------------------------------
‘Class CollatzSequence
‘----------------------------------
Option Explicit
Public Event newStart(ByVal sNum As Long)
Public Event nextNum(ByVal nNum As Long)
'produces Collatz sequence starting with z
'-------------------------------------------------------------
Public Function CollatzFolge(ByVal z As Long) As Long()
Dim c() As Long
Dim n As Integer
n = 1
ReDim c(1 To n)
c(n) = z
Do While z > 1
n = n + 1
ReDim Preserve c(1 To n)
z = IIf(z Mod 2 = 0, z \ 2, 3 * z + 1)
RaiseEvent nextNum(z)
c(n) = z
Loop
CollatzFolge = c
End Function
'provides a nested array containing Collatz sequences based on
'the starting numbers listed in z
'---------------------------------------------------------------------------------------------
Public Function CollatzArray2(ByRef z() As Long) As Variant
Dim f() As Variant
ReDim f(LBound(z) To UBound(z))
Dim i As Integer, j As Integer
For i = LBound(z) To UBound(z)
RaiseEvent newStart(z(i))
f(i) = CollatzFolge(z(i))
Next i
CollatzArray2 = f
End Function
Die mittlere Ebene besteht aus der Klasse CollatzAnimator. Die Klasse besitzt eine Instanzenvariable cs vom Typ CollotzSequence, welche mit der Durchführung des Algorithmus betraut wird. Wichtig ist, dass diese Variable mit dem Zusatz WithEvents deklariert wird. Die weiteren Instanzenvariablen sind: currRow (aktuelle Ausgabezeile), currCol (aktuelle Ausgabespalte), w (das Ausgabe-Arbeitsblatt) und dly (die gewünschte zeitliche Verzögerung zwischen den Einzelschritten).
Die Hauptprozedur ist doAnimation. Dies ist die Prozedur, die aus dem UI zur Ausführung der Animation aufgerufen wird. Prozedur doAnimation bereitet das Arbeitsblatt für die Ausgabe vor und beauftragt das Objekt cs mit der Durchführung des Algorithmus. Beachten Sie, dass die Rückgabe der Funktion CollatzArray2 nicht verwertet wird; die Funktion wird wie eine Prozedur aufgerufen. Auf die Lieferung von CollatzArray2 kann verzichtet werden, weil alle Ausgaben bereits während der Algorithmusdurchführung innerhalb der beiden Ereignisprozeduren cs_newStart und cs_nextNum durchgeführt werden.
Prozedur cs_newStart tritt in Aktion, sobald im Algorithmus die Bearbeitung einer neuen Collatzfolge begonnen wird. Die Prozedur macht rückt im Arbeitsblatt in die nächste Zeile vor und schreibt in deren erste Spalte die Startzahl der neu begonnenen Folge.
Die Prozedur cs_nextNum wird für jedes neu gefundene Element innerhalb einer Folge durchgeführt. Sie rückt innerhalb der aktuellen Ausgabezeile um eine Spalte nach rechts und schreibt dorthin das neue Element.
Beide Ereignisprozeduren rufen während ihrer Durchführung die Prozedur delay auf und veranlassen damit eine Verzögerung der Ausführung.
‘---------------------------------
‘Class CollatzAnimator
‘---------------------------------
Option Explicit
Private WithEvents cs As CollatzSequence
Private currRow As Long
Private currCol As Long
Private w As Worksheet
Private dly As Single
'controls the animation
'--------------------------------
Public Sub doAnimation(ByVal outW As Worksheet, _
ByRef sNums() As Long, _
ByVal delay As Single)
Set w = outW
Set cs = New CollatzSequence
dly = delay
currRow = 0
w.Cells.Clear
w.Cells.Font.Color = vbBlack
w.Cells.ColumnWidth = 5
cs.CollatzArray2 sNums
End Sub
'event procedure: a new sequence has been started
'-------------------------------------------------------------------------
Private Sub cs_newStart(ByVal sNum As Long)
currRow = currRow + 1
currCol = 1
If sNum Mod 2 = 0 Then
w.Cells(currRow, currCol).Font.Color = vbBlue
Else
w.Cells(currRow, currCol).Font.Color = vbRed
End If
w.Cells(currRow, currCol) = sNum
delay dly
End Sub
'event procedure: a new element has been added
'---------------------------------------------------------------------
Private Sub cs_nextNum(ByVal nNum As Long)
currCol = currCol + 1
If nNum Mod 2 = 0 Then
w.Cells(currRow, currCol).Font.Color = vbBlue
Else
w.Cells(currRow, currCol).Font.Color = vbRed
End If
w.Cells(currRow, currCol) = nNum
delay dly
End Sub
'causes a delay
'---------------------
Private Sub delay(ByVal duration As Single)
Dim t As Single
t = Timer
Do While Timer < t + duration
DoEvents
Loop
End Sub
Die oberste Schicht, bestehend aus dem Modul CollatzAnimStart, enthält nur eine simple Prozedur, welche den Animator in Gang setzt und mit Werten für die Parameter versorgt. In einer komfortableren Version könnte man dieses Modul durch ein Formular ersetzen, mit dessen Hilfe der Benutzer die Parameter eingeben kann.
‘------------------------------------
‘Module CollatzAnimStart
‘------------------------------------
Option Explicit
Public Sub startCollatzAnimation()
Dim ca As CollatzAnimator
Dim s(2 To 7) As Long
Dim i As Long
Dim w As Worksheet
Set w = Worksheets("Tabelle2")
Set ca = New CollatzAnimator
For i = 2 To 7
s(i) = i
Next i
w.Activate
ca.doAnimation w, s, 1
End Sub
Download