2. Ein simples Makro erstellen

Werbung
Vision Analyzer
Makro-Kochbuch
Version 1.05
© Brain Products GmbH 1999 - 2004
Der Inhalt dieses Handbuchs ist geistiges Eigentum der Brain Products GmbH. Er kann ohne
besondere Ankündigung geändert werden. Die Brain Products GmbH übernimmt keine
Gewähr oder Haftung für die Richtigkeit einzelner Aussagen bzw. geht mit diesem Dokument
keine Verpflichtung ein.
Alle in diesem Dokument aufgeführten Warenzeichen sind geschützte Marken ihrer
jeweiligen Inhaber.
2
Inhalt
1.
Einleitung .......................................................................................................... 5
2.
Ein simples Makro erstellen ............................................................................ 6
3.
Kurzer Streifzug durch Basic .......................................................................... 7
3.1. Variablen ......................................................................................................... 7
3.2. Prozeduren und Funktionen ............................................................................ 8
3.3. Objekte ............................................................................................................ 8
3.4. Benutzer-Interaktion ...................................................................................... 10
3.5. Kommentare .................................................................................................. 10
3.6. Kontrollstrukturen........................................................................................... 10
3.7. Fehlerbehandlung .......................................................................................... 11
3.8. Optimierung der Ablaufgeschwindigkeit......................................................... 12
4.
Objektmodell des Analyzers .......................................................................... 14
5.
Datenmanipulation mit Makros – Grundlagen ............................................. 16
6.
Beispiele aus der Praxis ................................................................................ 18
6.1. Automatisierung ............................................................................................. 18
6.1.1. Komprimieren aller History-Dateien ......................................................... 18
6.1.2. Drucker-Batchlauf .................................................................................... 18
6.1.3. Umbenennen eines History-Knotens in allen History-Dateien ................. 18
6.1.4. Grafikexport mit Report nach Winword .................................................... 21
6.2. Datenmanipulation ......................................................................................... 23
6.2.1. Marker entfernen, setzen, umbenennen .................................................. 23
6.2.2. Erzeugen neuer Daten ............................................................................. 24
6.2.3. Einlesen von Stimulatordaten aus externen Dateien ............................... 26
6.2.4. Einlesen von Kanalpositionen aus externen Dateien ............................... 28
6.2.5. Exportieren von Frequenzdaten in eine ASCII-Datei ............................... 30
6.3. Dynamische Parametrisierung ....................................................................... 32
7.
Tipps für Fortgeschrittene ............................................................................. 35
7.1. Variablendeklaration ...................................................................................... 35
7.2. Eigene Dialogboxen....................................................................................... 36
Vision Analyzer Makro-Kochbuch
3
7.3. Funktionen / Prozeduren ............................................................................... 38
7.4. Unterdrückung von Dialogen in History-Vorlagen .......................................... 39
7.5. Debugging ("Entwanzen", Fehlersuche) ........................................................ 40
4
1. Einleitung
Dieses kleine Makro-Kochbuch soll Ihnen ohne größeren theoretischen Ballast einen
Schnelleinstieg in die Erstellung von Makros für den Vision Analyzer ermöglichen.
Makros im Analyzer können Sie für (fast) alles verwenden. Sie können Verarbeitungsschritte
automatisieren, eigene Verfahren und Algorithmen zur Neuberechnung von Daten
implementieren. Außerdem können Sie Marker zu Datensätzen hinzufügen oder entfernen,
Kanalpositionen aus den eigenwilligsten Dateien in vorhandene EEG-Datensätze importieren,
Daten und Marker in eigenen Formaten exportieren, Reports erstellen und vieles mehr.
Obwohl Makros in der Programmiersprache Basic geschrieben werden, müssen Sie kein
Programmierer sein, um sie zu erstellen. Sie können sich im Kapitel "Beispiele aus der
Praxis" weiter unten davon überzeugen, dass schon einfache Makros Ihnen einen großen
Zugewinn an Funktionalität ermöglichen.
Am einfachsten erstellen Sie Makros, indem Sie sich unter den Beispielen eines
heraussuchen, das Ihrer Aufgabenstellung am nächsten kommt und dieses dann modifizieren.
Die den Beispielen vorausgehenden Kapitel sollen Sie in der Lage versetzen, einfache
Manipulationen an den vorhandenen Makros vornehmen zu können.
Sie finden die Beispielmakros unterhalb des Vision-Verzeichnisses im Verzeichnis
"Examples". Wenn Sie ein Beispiel verwenden oder modifizieren wollen, sollten Sie es in das
Unterverzeichnis "Workfiles" des Vision-Verzeichnisses kopieren und dann auf die Kopie
zurückgreifen.
Vision Analyzer Makro-Kochbuch
5
2. Ein simples Makro erstellen
In diesem Kapitel werden wir ein simples Makro erstellen, das alle offenen History-Dateien
und die zugehörigen Fenster schließt, also aufräumt. Damit Sie diesem und allen weiteren
Beispielen folgen können, sollten Sie eine funktionsfähige Version des Analyzers installiert
haben. Außerdem sollten sich in Ihrem aktuellen Workspace einige History-Dateien befinden.
Gehen Sie nun wie folgt vor.
Starten Sie den Analyzer. Wählen Sie den Menüpunkt Macro > New. Es erscheint ein Fenster
mit dem Titel "Macro1 (macro)...". In dem Fenster finden Sie zwei Zeilen "Sub Main" und
"End Sub". Diese Zeilen schließen das eigentliche Makro ein, d.h. Sie positionieren Ihren
Text zwischen diesen beiden Zeilen. Tippen Sie das Makro wie folgt ein:
Sub Main
for each hf in historyfiles
hf.close
next
End Sub
Der Editor ändert teilweise die Groß-/Kleinschreibung automatisch. Allgemein gilt, dass
Groß- und Kleinschreibung ignoriert werden können. Ausnahmen gibt es nur dort, wo Texte
verglichen werden sollen.
Speichern Sie das Makro unter den Menüpunkt File > Save mit dem Namen "Close All"
(ohne Anführungsstriche) ab. Führen Sie es mit der Taste F5 aus. Sollten Sie keinen
Tippfehler gemacht haben, so sollte gar nichts passieren. Ansonsten wird das Programm Sie
zu der Zeile mit dem Tippfehler führen.
Öffnen Sie einige History-Dateien, indem Sie auf das (+)-Zeichen neben den Buchsymbolen
drücken. Führen Sie das Makro noch einmal aus. Alle History-Dateien werden wie von
Zauberhand wieder geschlossen.
Sie können nun das Makrofenster schließen. Um das Makro noch einmal auszuführen, wählen
Sie es unter Macro > Run aus.
Alternativ können Sie auch Makros als Menüpunkte in der Makromenüleiste erscheinen
lassen. Wählen Sie dazu den Menüpunkt Macro > Options. Hier können Sie bis zu zehn
verschiedene Makros auswählen. Wenn Sie die Auswahl abgeschlossen haben und nun wieder
das Menü Macro anwählen, finden Sie Ihr Makro als Menüpunkt vor. Die ausgewählten
Makros können Sie nun auch über Tastaturbefehle erreichen (Alt-M, 1, 2 , 3...). Bitte beachten
Sie, dass die betreffenden Makros sich im aktuellen Arbeitsverzeichnis befinden müssen. Das
aktuelle Arbeitsverzeichnis wird im Analyzer unter Configuration > Select Folder for
Workfiles festgelegt.
In den folgenden Kapiteln gehen wir auf die Bedeutung der Zeilen ein, die Sie soeben
eingetippt haben.
6
3. Kurzer Streifzug durch Basic
Dieses Kapitel gibt Ihnen einen kurzen und damit natürlich auch unvollständigen Überblick
über einige Aspekte der Makroerstellung mit der verwendeten Basic-Sprache. Für
weitergehende Informationen verwenden Sie bitte die Online-Hilfe, die Ihnen während der
Makro-Erstellung unter Help > Language Help zur Verfügung steht.
Wenn Sie in den Beispielen Begriffe finden, die Sie nicht verstehen, können Sie auch die
sogenannte kontextsensitive Hilfe beanspruchen. Setzen Sie dafür den Cursor auf einen
Begriff und betätigen Sie anschließend die F1-Taste. Die Online-Hilfe wird Ihnen dann eine
Erläuterung des Begriffes zeigen, sofern dieser in der Hilfedatei registriert ist. Das gilt
grundsätzlich nicht für die Analyzer-Objekte, die im folgenden Kapitel erläutert werden.
Der verwendete Basic-Dialekt ist kompatibel mit dem von Microsoft vertriebenen Visual
Basic, so dass Sie für weitere Fragen auch auf Visual-Basic-Literatur zurückgreifen können.
3.1. Variablen
Variablen sind so etwas wie kleine Behälter, in die man einen bestimmten Inhalt, z.B. Zahlen
oder Texte verstauen kann, um sie dann an einer beliebigen Stelle im Makro wieder
verwenden zu können.
Ein Beispiel:
i = 0
i = i + 1
In der ersten Zeile wurde der Variablen i der Wert 0 zugewiesen. In der zweiten Zeile wurde i
um eins erhöht.
Man kann Variablen in einem Makro explizit deklarieren wie im folgenden Beispiel.
Dim f as Single
Hier haben wir eine Variable mit dem Namen f als Behälter für Fließkommazahlen einfacher
Genauigkeit deklariert. Die Deklaration einer Variablen ist optional. Sie können Variablen
auch benutzen, ohne sie zu deklarieren.
Solange Makros kurz sind, spielt die Deklaration keine Rolle. Werden Ihre Makros aber
länger als 30 bis 50 Zeilen, so ist es ratsam, Variablen zu deklarieren, um den Überblick nicht
zu verlieren. Lesen Sie dazu mehr im Kapitel "Tipps für Fortgeschrittene" am Ende dieses
Buches.
Wollen Sie in einer Variablen nicht einen Wert, sondern mehrere unterbringen, so sprechen
wir von einem sogenannten Array (Feld). Die folgende Deklaration erzeugt ein Array mit 20
Fließkommazahlen einfacher Genauigkeit. In der zweiten Zeile wird der zweite Eintrag in
diesem Array gesetzt. In der dritten Zeile wird der zweite Eintrag des Arrays der Variablen x
zugewiesen.
Dim fArray(1 To 20) as Single
fArray(2) = 12
x = fArray(2)
Vision Analyzer Makro-Kochbuch
7
Wenn Sie ein Array benötigen, dessen Größe sich während des Makrolaufs ändern kann,
deklarieren Sie es wie folgt:
Dim fArray() as Single
Redim fArray(1 to 20)
Hier wurde das Array fArray deklariert und dann dimensioniert auf zwanzig Einträge.
Redim Preserve fArray(1 to 20)
dient ebenso der Redimensionierung, lässt aber einen eventuell schon vorhandenen Inhalt
unversehrt.
3.2. Prozeduren und Funktionen
Der Basic-Interpreter verfügt über eine größere Anzahl von eingebauten Funktionen und
Prozeduren, die Sie in Ihren Makros verwenden können.
Funktionen führen Operationen aus und liefern ein Resultat zurück. Ein Beispiel ist die
Funktion "sin". Sie liefert den Sinus einer Zahl im Bogenmaß zurück und wird wie folgt
eingesetzt:
x = sin(0.3)
Die Variable x hat in diesem Falle den Sinus von 0,3 erhalten.
Prozeduren führen ebenfalls eine oder mehrere Operationen aus, liefern aber keinen Wert
zurück. Beispiel:
beep
Die Prozedur "Beep" erzeugt einen kurzen Ton.
Eine vollständige Beschreibung aller eingebauten Funktionen und Prozeduren des BasicInterpreters finden Sie in der Online-Hilfe.
Sie können in Ihren Makros eigene Prozeduren und Funktionen verwenden. Dies kann bei
größeren Makros die Übersichtlichkeit erhöhen. Mehr dazu erfahren Sie im Kapitel "Tipps für
Fortgeschrittene" weiter unten.
3.3. Objekte
Aufgrund der inflationären Verwendung des Begriffs "Objekt" erklären wir hier das Objekt,
wie es in diesem Handbuch verwendet wird.
Ein Objekt ist eine Funktionseinheit, die Sie programmatisch manipulieren können. Es
verfügt über Methoden und Eigenschaften. Methoden entsprechen in ihrer Form Funktionen
oder Prozeduren. Eigenschaften können entweder einfache Variablen sein oder ihrerseits
wieder Objekte.
Das wichtigste Objekt im Analyzer heißt "Application". Es repräsentiert den Analyzer. Das
folgende Makro bedient sich des "Application"-Objekts, um das Programm zu beenden.
Sub Main
Application.Quit
End Sub
8
Hier wurde die Methode "Quit" des "Application"-Objekts aufgerufen, um die Ausführung
des Analyzers zu beenden.
Das "Application"-Objekt ist das sogenannte Standard-Objekt des Analyzers. Das bedeutet,
dass es im Text auch weggelassen werden kann. Das folgende Macro ist mit dem
vorhergehenden in der Funktion identisch.
Sub Main
Quit
End Sub
Das "Application"-Objekt verfügt seinerseits wieder über weitere Objekte, z.B. das Objekt
"HistoryExplorer", das den History-Explorer repräsentiert. "HistoryExplorer" verfügt über die
Eigenschaft "Visible", die den Wert 1 hat, wenn der Explorer sichtbar ist, und den Wert 0,
wenn er unsichtbar ist.
Die Zeile
Application.HistoryExplorer.Visible = 0
oder
HistoryExplorer.Visible = 0
schaltet den Explorer weg.
Mehr über das Analyzer-Objektmodell erfahren Sie im nächsten Kapitel.
Sie können Variablen verwenden, die auf Objekte verweisen. In diesem Falle verwenden Sie
für eine Zuweisung neben dem Zuweisungszeichen "=" das Schlüsselwort "Set". Beispiel:
set he = HistoryExplorer
he.Visible = 0
Objekte können sogenannte Standardelemente enthalten. Diese müssen dann im Makrotext
nicht mehr explizit erwähnt werden. Im folgenden Beispiel wird der Variablen "hf" ein
Verweis auf die erste History-Datei im Workspace zugewiesen:
set hf = HistoryFiles.Item(1)
Da "Item" das Standardelement von "Application.HistoryFiles" ist, kann der Ausdruck auch
wie folgt lauten:
set hf = HistoryFiles(1)
Ein besonderer Objekttyp ist die sogenannte Auflistung. Hierbei handelt es sich um Objekte,
die ihrerseits wieder mehrere Objekte eines Typs enthalten. Ein Beispiel ist das Objekt
"HistoryFiles", das mehrere Objekte des Typs "HistoryFile" enthält. Solche
Auflistungsobjekte erkennt man im Analyzer und auch in meisten anderen OLE-AutomationServern daran, dass sie in der (englischen) Mehrzahl geschrieben sind, "HistoryFiles"
beispielsweise enthält Objekte vom Typ "HistoryFile", "HistoryNodes" Objekte vom Type
"HistoryNode" usw.. Elemente der Auflistungen können wie Arrays indiziert werden:
Dim hf as HistoryFile
set hf = HistoryFiles(1)
Vision Analyzer Makro-Kochbuch
9
3.4. Benutzer-Interaktion
Sie können während des Ablaufs eines Makros auch Meldungen an den Anwender ausgeben,
oder ihn zur Eingabe von Parametern auffordern.
Zur Eingabe können Sie die Funktion "InputBox" und zur Ausgabe "MsgBox" verwenden.
Das folgende Makro nimmt eine Benutzereingabe an und gibt sie wieder als Meldung aus.
Sub Main
x = InputBox("Enter Text")
MsgBox x
End Sub
Zur Auswahl von Dateien steht Ihnen noch die Funktion "GetFilePath" zur Verfügung. Lesen
Sie dazu die Beschreibung in der Online-Hilfe.
Schließlich können Sie noch eigene Dialoge entwerfen und im Makro verwenden. Lesen Sie
dazu mehr im Kapitel "Tipps für Fortgeschrittene" sowie in der Online-Hilfe.
3.5. Kommentare
Um Makros übersichtlicher und verständlicher zu machen, können Sie Kommentare einfügen.
Kommentare beginnen mit dem Zeichen '. Der Text nach diesem Zeichen bis zum Ende der
Zeile wird dann vom Basic-Interpreter ignoriert.
Beispiel:
' This macro receives an user input and shows the result in a message box.
Sub Main
x = InputBox("Enter Text")
' Get user input and store it in x.
MsgBox x
' Show user input in a message box.
End Sub
Sie sollten nicht an Kommentaren sparen. Speziell bei größeren Makros passiert es sonst
schnell, dass Sie die Übersicht verlieren, welchem Zweck die einzelnen Anweisungen
eigentlich dienen.
3.6. Kontrollstrukturen
Zu jeder leistungsfähigen Makrosprache gehören auch Kontrollstrukturen, die eine bedingte
Ausführung von Code zulassen, oder basierend auf eine Bedingung eine oder mehrere
Operationen mehrfach wiederholen.
Zur bedingten Verzweigung verwendet der Basic-Interpreter die If ... then ... else ... end ifKonstruktion. Beispiel:
Sub Main
Dim x as long
x = InputBox("Enter a number:")
if x > 20 then
MsgBox "X is larger than 20."
else
MsgBox "X is not larger than 20."
end if
End Sub
Wird hier eine Zahl eingegeben, die größer als 20 ist, so wird anschließend die erste Meldung
ausgegeben, sonst die zweite.
10
Der else-Zweig kann auch weggelassen werden. In diesem Falle passiert einfach nichts, wenn
die eingegebene Zahl kleiner oder gleich 20 ist:
Sub Main
Dim x as long
x = InputBox("Enter a number:")
if x > 20 then
MsgBox "X is larger than 20."
end if
End Sub
Das Einrücken der Zeilen erfolgt, um Ebenen im Makro-Code aufzuzeigen und ist nicht
zwingend erforderlich. Bei größeren Makros erhöht es aber deutlich die Lesbarkeit.
Soll eine oder mehrere Operationen wiederholt werden (Schleife), so bietet sich die for ...
next-Konstruktion an. Beispiel:
Dim fArray(1 to 20) as single
for i = 1 to 20
fArray(i) = 2
next
Es werden alle Elemente des Arrays fArray auf den Wert 2 gesetzt.
Eine andere Wiederholungskonstruktion behandelt speziell Auflistungsobjekte - die for each
...in .. next-Konstruktion:
Dim hf as HistoryFile
for each hf in HistoryFiles
hf.Close
next
Hier werden nacheinander alle in der "HistoryFiles"-Auflistung vorhandenen "HistoryFile"Objekte von "hf" referenziert. Innerhalb der Schleife werden dann die History-Dateien
geschlossen.
Es ist nun an der Zeit, auf eine sehr schöne Schleifeneigenschaft hinzuweisen, die sogenannte
Endlosschleife. Programmieren Sie ein Makro wie folgt, so wird "i" intern hochgezählt und in
der Schleife wieder heruntergezählt. Die Schleife wird erst verlassen, wenn i den Wert 1000
überschreitet, was hier nie der Fall ist.
Sub Main
For i = 1 To 1000
i = i - 1
Next
End Sub
Das Makro läuft ewig. Sie können es allerdings mit der Tastenkombination Strg-Unterbr,
bzw. Ctrl-Break abbrechen.
Informationen über weitere Kontrollstrukturen entnehmen Sie bitte der Online-Hilfe.
3.7. Fehlerbehandlung
Es ist möglich, dass während der Ausführung eines Makros ein Fehler auftritt. Angenommen
Ihr Workspace enthält 1000 History-Dateien, und Sie geben im Makro die Anweisung, die
1001. Datei zu öffnen:
hf = HistoryFiles(1001)
hf.Open
Vision Analyzer Makro-Kochbuch
11
Es kommt zu einer Fehlermeldung in der Statuszeile und die weitere Ausführung des Makros
wird beendet. Um die Möglichkeit zu haben, irgendwelche Aktionen durchzuführen, bevor
das Makro stoppt, gibt es die On Error-Anweisung. Beispiel:
Sub Main
On Error Goto CheckError
hf = HistoryFiles(1001)
hf.Open
exit sub
CheckError:
MsgBox "Could not open history file!"
End Sub
Wenn ein Fehler auftritt, wird hier die Ausführung bei der Marke "CheckError" weitergeführt.
Beachten Sie bitte die Anweisung "exit sub" vor der "CheckError"-Marke. Sie beendet das
Makro an dieser Stelle und verhindert so, dass der Code nach der "CheckError"-Marke
ausgeführt wird, wenn kein Fehler vorliegt.
Diese Konstruktion bietet für das Beispielproblem kaum Vorteile, kann aber in bestimmten
Fällen nützlich sein.
Weitere Informationen über Fehlerbehandlungsroutinen entnehmen Sie bitte der Online-Hilfe.
3.8. Optimierung der Ablaufgeschwindigkeit
Makros können durch das Vermeiden von unnötigen Objekt-Referenzierungen erheblich
beschleunigt werden. In der Praxis traten Fälle mit Beschleunigungen um den Faktor 100 auf.
Der wichtigste Ort, in dem Optimierungen greifen, ist das Innere einer Schleife. Hier wird
jeder programmierte Zeitverlust mit der Anzahl der Schleifendurchläufe multipliziert.
Nachfolgend sehen Sie ein Beispiel für eine nicht optimierte Schleife. Der Unterstrich ("_")
als letztes Zeichen einer Zeile wird verwendet, wenn eine Anweisung sich über mehrere
Zeilen erstreckt.
Sub Main
Set nhn = New NewHistoryNode
nhn.Create "New", ActiveNode
For i = 1 To 100
nhn.RemoveMarker 0, hn.Dataset.Markers(i).Position, 1, _
ActiveNode.Dataset.Markers(i).Type, ActiveNode.Dataset.Markers(i).Description
nhn.AddMarker 0, ActiveNode.Dataset.Markers(i).Position, 1, "Stimulus", _
ActiveNode.Dataset.Markers(i).Description + "A"
Next
nhn.Finish
End Sub
Es folgt ein Makro mit der gleichen Funktionalität, etwa 100-mal schneller und nebenbei auch
noch übersichtlicher. Beachten Sie, dass die Objektkette "ActiveNode.Dataset.Markers" nur
einmal außerhalb der For ...Next-Schleife referenziert wird.
Sub Main
Set nhn = New NewHistoryNode
nhn.Create "New", ActiveNode
Set Mks = ActiveNode.Dataset.Markers
' Use variable as object reference.
For i = 1 To 100
Set mk = Mks(i)
nhn.RemoveMarker 0, mk.Position, 1, mk.Type, mk.Description
nhn.AddMarker 0, mk.Position, 1, "Stimulus", mk.Description + "A"
Next
nhn.Finish
End Sub
12
Eine einfache Faustregel zur Beschleunigung lautet:
Entfernen Sie so viele Punkte (".") wie möglich aus der Schleife, sofern diese
Objektverweise beschreiben.
Es kommt bei Objektverweisen oft zu komplexen Erstellung von Objekten innerhalb des
Analyzers. Sie werden dann innerhalb der Schleife immer wieder gelöscht und neu erstellt,
wenn Sie nicht einmalig einer Variablen zugewiesen werden, wie in
Set Mks = ActiveNode.Dataset.Markers
' Use variable as object reference.
geschehen. Das führt manchmal zu dramatischen Geschwindigkeitseinbußen.
Einige Punkte in der Schleife betreffen Eigenschaften, die einfache Variablen im Objekt
repräsentieren, z.B. "mk.Position". Hier ist der zu erwartende Geschwindigkeitsgewinn bei
einer Zuweisung zu einer Variablen so gering, dass die Nachteile der größeren
Unübersichtlichkeit überwiegen.
Vision Analyzer Makro-Kochbuch
13
4. Objektmodell des Analyzers
Hier gehen wir im Schnellgang durch das Analyzer-Objektmodell. Für Details beachten Sie
bitte das ebenfalls im Lieferumfang enthaltene Ole-Automation-Referenzhandbuch.
Nachfolgend finden Sie zur Orientierung die grafische Darstellung der Objekthierarchie des
Analyzers. Ganz oben in der Hierarchie steht das "Application"-Objekt.
Application
CurrentWorkspace
NewHistoryNode
HistoryExplorer
HistoryFiles (HistoryFile)
HistoryNodes (HistoryNode)
DataSet
Channels (Channel)
ChannelPosition
Markers (Marker)
HistoryNodes (HistoryNode)
Segments (Segment)
DataSet
...
Windows (Window)
...
Transformation
Windows (Window)
HistoryNode
...
Workspaces (Workspace)
Legende
Objekt und Auflistung
Nur Objekt
Abbildung 4-1: Objekt-Hierarchie des Analyzers
Von der Application-Klasse gibt es nur ein Objekt, das das Programm als ganzes
repräsentiert. Es ist das Standardobjekt, was bedeutet, dass die Methoden und Eigenschaften
dieses Objekts direkt ansprechbar sind, d.h. z.B. "HistoryFiles" ist gleichbedeutend mit
"Application.HistoryFiles".
14
Die Objektauflistung "HistoryFiles" repräsentiert alle History-Dateien im aktuellen
Workspace. Eine einzelne History-Datei ist als "HistoryFile" repräsentiert.
Jede History-Datei enthält eine Auflistung "HistoryNodes", die normalerweise ein Objekt
vom Typ "HistoryNode" enthält. Bei den primären History-Dateien ist das der Datenknoten
mit dem Namen "Raw Data". Jedes Objekt der Objektklasse "HistoryNode" enthält seinerseits
wieder eine Auflistung "HistoryNodes" mit Verweisen auf abgeleitete Datensätze. So können
Sie durch eine komplette History-Datei mit all ihren Verästelungen navigieren.
Das "HistoryNode"-Objekt besitzt auch ein weiteres Objekt, das "Dataset"-Objekt. Das
"Dataset"-Objekt enthält eine Auflistung aller Marker im Datensatz, das "Markers"-Objekt
und eine Auflistung der Kanäle, das "Channels"-Objekt. Jedes einzelne "Channel"-Objekt
schließlich liefert Ihnen endlich den Zugang zu jedem einzelnen Datenpunkt. Der Zugriff auf
den ersten Datenpunkt des ersten Kanals eines Historie-Knotens lässt sich also wie folgt
beschreiben:
Dataset.Channels(1).DataPoint(1)
Da "Channels" das Standardelement von "Dataset" ist und außerdem "DataPoint" das
Standardelement von "Channels" ist, lässt sich der Begriff wie folgt verkürzen:
Dataset(1)(1)
Weitere Aspekte von "HistoryNode"-, "Dataset"- und "Channel"-Objekten werden wir unten
im Kapitel "Beispiele aus der Praxis" aufgreifen.
Etwas abseits steht noch das Objekt "NewHistoryNode". Es dient der Erstellung neuer
History-Knoten. Da es bei Bedarf neu erzeugt wird, wird es in der Objekthierarchie separat
dargestellt. Sein Einsatz wird im nächsten Kapitel ausführlicher beschrieben.
Vision Analyzer Makro-Kochbuch
15
5. Datenmanipulation mit Makros – Grundlagen
Unter Datenmanipulation verstehen wir in diesem Kapitel das Ändern der eigentlichen Daten,
aber auch das Entfernen und Hinzufügen von Markern bzw. das Ändern von Eigenschaften
wie Kanalnamen oder –Positionen. Tatsächlich wird keine physikalische Änderung von Daten
durchgeführt. Es wird stattdessen ein neuer abgeleiteter Datensatz erzeugt, wie wir das auch
bei den Transformationen im Analyzer kennen. Sie können diesen Datensatz auch genauso
wieder löschen, wie es mit anderen Datensätzen möglich ist. Ihrer Experimentierfreude sind
also auch hier wieder keine Grenzen gesetzt.
Bevor Sie Makros zur Datenmanipulation einsetzen, sollten Sie allerdings überprüfen, ob Ihre
Problemstellung nicht von einem der vorhandenen Transformationsmodule gelöst werden
kann.
Für die Erzeugung eines neuen Datensatzes benötigen wir ein "NewHistoryNode"-Objekt.
Am einfachsten erzeugen wir es mit der folgenden Zeile.
Dim nhn as New NewHistoryNode
Im Wesentlichen sind zwei Prozeduren für die Erstellung und Fertigstellung des Knotens
zuständig:
"Create" und "Finish". Zwischen diesen beiden Prozeduren können die Kanalnamen, Marker
und Daten gesetzt werden. Wurde "Create" erfolgreich aufgerufen und die Daten nicht vom
Parent vererbt, wird ein Datensatz mit Standardeinstellungen erstellt. Dieser kann nun
manipuliert werden. "Finish" schließt den Erstellungsprozess ab.
Die neu erzeugten Historieknoten können eingeschränkt auch in Historievorlagen eingesetzt
werden.
Dafür muss der neue Knoten als Child-Knoten der vordefinierten Variablen "ActiveNode"
erzeugt werden. Diese Variable ist immer definiert, wenn der Basic-Interpreter läuft und zwar
wie folgt:
Dim ActiveNode As HistoryNode
Der Knoten repräsentiert das aktuell offene Datenfenster. Ist kein Datenfenster geöffnet, so
enthält dieser Knoten keine Daten.
Um also ein vorlagenfähigen Datensatz zu erzeugen, sollten Sie alle offenen Datenfenster
schließen, bis auf das Fenster, das als Parent für den neuen Child-Knoten dienen soll. Dann
wird der Code des Basic-Makros ausgeführt. Bitte beachten Sie, dass die Ausführung des
Makros automatisch beendet wird, wenn die NewHistoryNode.Finish()-Methode ausgeführt
wird.
Beim Erstellen des neuen Knotens wird der gesamte Makro-Code in den Knoten kopiert. Jetzt
können Sie den Knoten wie eine gewöhnliche Transformation auf andere Knoten ziehen, um
die Operation zu wiederholen. Der Knoten kann auch in eine Historie-Vorlage aufgenommen
werden.
Sehen Sie sich mit der rechten Maustaste am Knoten die sogenannten "Operation Infos" an, so
wird zusätzlich der Code angezeigt, der zur Erstellung des Knotens geführt hat.
16
Nachfolgend finden Sie einen Beispielcode, der nur den ersten Kanal in "xxx" umbenennt,
ansonsten alles unverändert lässt. Die neuen Daten werden im Knoten "BasicTest" unter dem
aktuell offenen History-Knoten abgelegt. Dieser Code dient hier nur der Demonstration.
Wenn Sie tatsächlich Kanäle umbenennen wollen, können Sie dies einfacher mit der
Transformation "Edit Channels" durchführen.
Sub Main
Dim nhn as New NewHistoryNode
nhn.Create "BasicTest", ActiveNode
nhn.SetChannelName 1, "xxx"
nhn.Finish
End Sub
Wie Sie sehen, erfordert es nur einen minimalen Aufwand, einen neuen Datensatz, bzw.
History-Knoten anzulegen.
Wir haben eben einen Datensatz erzeugt, der alle Daten seines Vorgängers übernimmt. In
diesem Falle fällt für das Makro nur sehr wenig Arbeit an, so dass es sehr schnell ausgeführt
wird. Auch ist der Platzbedarf des neuen Datensatzes gering, da tatsächlich nur ein Verweis
auf die Daten gespeichert wird.
Erzeugen Sie im Gegensatz dazu wirklich neue Daten, so werden diese in der History-Datei
gespeichert. Wir empfehlen deshalb, Manipulationen an Daten nach Möglichkeit nur nach der
Mittelung durchzuführen, da hier erheblich weniger Daten anfallen, als beim Roh-EEG. Das
wirkt sich auch auf die Geschwindigkeit positiv aus, denn die Makrosprache ist relativ
langsam. Wenn Sie also Operationen auf Rohdaten ausführen möchten, so sollten Sie
überprüfen, ob ein Transformationsmodul diese Arbeit erledigen kann (z.B. "Formula
Evaluator"). Auch eine Mischform aus Makros und Transformationen ist eingeschränkt
möglich, wie im folgenden Kapitel, Unterkapitel "Dynamische Parametrisierung"
beschrieben.
Anders sieht es aus, wenn Sie nur Marker setzen, löschen oder umbenennen wollen. Da
Marker intern in einer Tabelle gespeichert werden, kann ein Makro hier auch bei Roh-EEGs
recht schnell agieren und der resultierende Datensatz benötigt nur wenig Platz. Dasselbe gilt
für das Ändern von Eigenschaften, wie Kanalnamen oder -positionen.
Die verschiedenen Arten neuer History-Knoten werden im Kapitel "Beispiele aus der Praxis"
erläutert. Die genaue Syntax für die Erstellung der Knoten entnehmen Sie bitte dem OleAutomation-Referenzhandbuch im Kapitel "Objektklassen", Unterkapitel "NewHistoryNode".
Vision Analyzer Makro-Kochbuch
17
6. Beispiele aus der Praxis
6.1.
Automatisierung
6.1.1. Komprimieren aller History-Dateien
Wenn Sie viel mit Ihren Daten experimentieren, Knoten in History-Dateien anlegen und
wieder löschen, so bleiben üblicherweise mehr oder weniger große Lücken, gewissermaßen
Löcher, in den Dateien übrig. Dies führt dazu, dass History-Dateien unnötig groß werden
können. Das folgende Makro durchläuft alle History-Dateien der aktuellen Workspace und
komprimiert sie, d.h. es entfernt alle Lücken.
Datei "Compress All.vabs":
' Compress all history files
Sub Main
For each hf in HistoryFiles
hf.Compress
Next
End Sub
6.1.2. Drucker-Batchlauf
Das folgende Makro sucht nach dem Datensatz "Average" in allen History-Dateien. Wenn
einer gefunden wird, wird er ausgedruckt. Befinden sich mehrere Datensätze mit dem
gleichen Namen in der History-Datei, wird hier nur der zuerst gefundene Datensatz
ausgedruckt.
Datei "PrintAverages.vabs":
' Search in each history file for a node named "Average". If found, print it.
Sub Main
For Each hf In HistoryFiles
hf.Open
Set hn = hf.FindNode("Average")
If Not hn Is Nothing Then
' "Average" node found?
hn.Show
' When the node is shown, at least one window is attached.
hn.Windows(1).Print
Wait 2
End If
hf.Close
Next
End Sub
6.1.3. Umbenennen eines History-Knotens in allen History-Dateien
Angenommen, Sie haben nach zwei verschiedenen Kriterien segmentiert, haben also z.B.
einen Knoten "Segmentation" und einen Knoten "Segmentation2". Danach haben Sie noch
einige Operationen durchgeführt bis Sie zum Average kamen. Ihre History-Datei enthält nun
zwei verschiedene Knoten mit demselben Namen "Average". Sie haben die Operation mit 162
History-Dateien gemacht und hätten nun gerne ein Grandaverage über das von "Segmentation
2" abgeleitete Average. Sie stellen fest, es geht nicht! Das Grandaverage-Modul nimmt nur
einen Knotennamen an, wie z.B. "Average" und verwendet dann den ersten Knoten, den es in
der History-Datei findet. Sie können nun entweder eine History-Vorlage aus einer der Dateien
erstellen und das zweite "Average" in "Average 2" umbenennen und dann die Vorlage laufen
18
lassen oder in jeder vorhandenen History-Datei den zweiten "Average"-Knoten umbenennen
oder das folgende Makro ausführen.
Datei "RenameToAverage2.vabs":
' Search for "Average" below "Segmentation 2" and rename it to "Average 2".
' This macro assumes that there is no branch below "Segmentation 2".
Sub Main
Dim hf As HistoryFile
Dim hn As HistoryNode
For Each hf In HistoryFiles
hf.Open
Set hn = hf.FindNode("Segmentation 2")
If Not hn Is Nothing Then
' "Segmentation 2" found?
Dim nChildren As Long
' Check for children of node.
Dim hn2 As HistoryNode
Set hn2 = hn
nChildren = hn2.HistoryNodes.Count
If nChildren > 1 Then
' branch found.
MsgBox "Branch found in " & hf.DisplayName & "!", "Warning"
End If
Do While nChildren > 0
Set hn2 = hn2.HistoryNodes(1)
If StrComp(hn2.Name, "Average", 1) = 0 Then ' Case insensitive comparison.
' Rename it.
hn2.Name = "Average 2"
End If
nChildren = hn2.HistoryNodes.Count
If nChildren > 1 Then
' branch found.
MsgBox "Branch found in " & hf.DisplayName & "!", "Warning"
End If
Loop
End If
hf.Close
Next
End Sub
Das obige Makro hat einen Nachteil: es dürfen nach "Segmentation 2" keine Verzweigungen
auftreten, d.h. "Segmentation 2" und jeder nachfolgende Knoten darf nur einen Child-Knoten
haben. Ist das nicht der Fall, wird eine Warnung ausgegeben und eventuell ein "Average"Knoten übersehen.
Um auch beliebige Verzweigungen zu berücksichtigen, können Sie das folgende Makro
verwenden, das etwas komplizierter aufgebaut ist. Es verwendet eine sogenannte Rekursion,
d.h. es wird eine Funktion definiert, die sich selbst aufruft. Informationen über den Aufbau
von Funktionen erhalten Sie im Kapitel "Tipps für Fortgeschrittene". Die Funktion
"FindSubNode" sucht nach einem Knoten mit einem vorgegebenen Namen unterhalb eines
vorgegebenen Knotens. Sie kann auch für andere Anwendungen von Interesse sein.
Datei "RenameToAverage2Rec.vabs":
' Search for "Average" below "Segmentation 2" and rename it to "Average 2".
Sub Main
Dim hf As HistoryFile
Dim hn As HistoryNode
For Each hf In HistoryFiles
hf.Open
Set hn = hf.FindNode("Segmentation 2")
If Not hn Is Nothing Then
' "Segmentation 2" node found?
Dim hnAverage As HistoryNode
Set hnAverage = FindSubNode(hn, "Average")
If Not hnAverage Is Nothing Then
hnAverage.Name = "Average 2"
End If
End If
hf.Close
Next
End Sub
Vision Analyzer Makro-Kochbuch
19
' This function searches recursively for a history node with the given name below the given
' node.
Function FindSubNode(hn As HistoryNode, sName As String) As HistoryNode
Dim hnChild As HistoryNode
For Each hnChild In hn.HistoryNodes
If StrComp(hnChild.Name, sName, 1) = 0 Then
' Case insensitive comparison.
Set FindSubNode = hnChild
Exit Function
End If
Set FindSubNode = FindSubNode(hnChild, sName)
' Recursive call
If Not FindSubNode Is Nothing Then
' Found?
Exit Function
End If
Next
End Function
20
6.1.4. Grafikexport mit Report nach Winword
Das folgende Makro kopiert den Inhalt des aktuellen Datenfensters ins Clipboard, startet MS
Word 2000, kopiert die Daten hinein, und überlässt Ihnen dann weitere Manipulationen.
Datei "CopyToWord.vabs":
' Copy contents of a window to the clipboard.
' and then paste it into a new word document.
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Set hn = ActiveNode
hn.Show
' Now it should be the active window.
hn.Windows(1).Copy
' Word must be on the machine.
' The following commands are Word commands.
Set Word = CreateObject("Word.Application")
Word.Visible = True
Word.Documents.Add
Word.Selection.TypeText hn.HistoryFile.DisplayName & "-" & hn.Name
Word.Selection.TypeParagraph
Word.Selection.Paste
End Sub
Der kleinere Teil des Makros beinhaltet Analyzer-Kommandos, der größere WordAutomation-Kommandos, die wir hier nicht weiter erörtern werden. Greifen Sie dafür bitte
auf die von Microsoft mitgelieferte Word-Dokumentation zurück.
Erwähnenswert ist die Funktion "CreateObject", mit deren Hilfe Sie fremde Applikationen
starten können, um sie dann mit OLE-Automation fernzusteuern. Dies gilt für die MicrosoftOffice-Programme (Word, Excel, Powerpoint, Access, Outlook) und viele andere (Visio,
SPSS usw.).
Wenn Sie einen einfachen Report für einen Datensatz benötigen, können Sie das folgende
Makro verwenden. Es funktioniert ebenfalls mit Word 2000. Die Word-Applikation wird
wieder aus dem Makro heraus ferngesteuert. Das Makro erzeugt eine neue Datei mit den
Namen der aktuellen History-Datei und des aktuellen Datensatzes. Die neue Datei wird im
Exportverzeichnis des aktuellen Workspaces abgelegt. Das Exportverzeichnis legen Sie im
Analyzer unter File > Edit Workspace fest.
Datei "WordReport.vabs":
' Copy contents of the active window to the clipboard.
' Create a word document.
' Write title.
' Paste clipboard contents.
Sub Main
On Error GoTo CheckError
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Set hn = ActiveNode
hn.Windows(1).Copy
' When the active node has valid data it also has at
' least one attached window.
' Save new document in export folder.
Dim sOutput As String
' Build output file name.
sOutput = CurrentWorkspace.ExportFileFolder & "\" & hn.HistoryFile.DisplayName & _
"-" & hn.Name & ".doc"
Vision Analyzer Makro-Kochbuch
21
' MS Word 97 must be on the machine.
' The following commands are Word commands.
' Look at the office documentation for the object model of word.
Set WordDoc = CreateObject("Word.Document")
WordDoc.Select
Dim Sel As Object
Set Sel = WordDoc.Application.Selection ' Reference word selection object.
' Save current font.
With Sel.Font
OldBold = .Bold : OldName = .Name
.Bold = True : .Name = "Arial"
End With
' Write caption.
Sel.TypeText hn.HistoryFile.DisplayName & "-" & hn.Name
Sel.TypeParagraph
' Restore font
With Sel.Font
.Bold = OldBold : .Name = OldName
End With
Sel.Paste
Sel.MoveEnd
Sel.TypeParagraph
WordDoc.SaveAs sOutput
' Save document.
WordDoc.Close
Exit Sub
CheckError:
MsgBox Err.Description, vbExclamation, "Error"
End Sub
22
6.2.
Datenmanipulation
6.2.1. Marker entfernen, setzen, umbenennen
Makros können vorhandene Marker entfernen und neue Marker setzen. Die Umbenennung
eines Markers erfolgt durch das Entfernen und darauffolgende Setzen eines neuen Markers an
derselben Position.
Die Hauptanwendung für die Markermanipulation ist die Vorbereitung für eine spezielle
Segmentierung. Obwohl das Segmentierungsmodul des Analyzers mit der Advanced Boolean
Expression (ABE) eine sehr mächtige Methode der intelligenten Segmentierung enthält, kann
es nicht alle denkbaren Segmentierungsalgorithmen berücksichtigten. Sie sollten aber
trotzdem vor dem Einsatz eines Makros überprüfen, ob die ABE eventuell Ihr Problem löst.
Hier kommt unser erstes Beispiel. Es sollen die ersten 5 Stimuli in einem Datensatz ignoriert
werden und dann die nächsten 500 Stimuli in die Mittelung eingehen. Das folgende Makro
löscht dafür einfach alle nicht benötigten Stimuli aus dem Datensatz. Befinden sich nicht
genügend Stimuli im Datensatz, wird eine Fehlermeldung ausgegeben.
Makro "500Stimuli.vabs":
' Remove all stimulus markers from 1 to 5 and > 505.
' -> keep exactly 500 stimuli.
Sub Main
Dim nhn As New NewHistoryNode
nhn.Create "500 Stim", ActiveNode
Dim Mks As Markers
Dim mk As Marker
Dim i As Long
Set Mks = ActiveNode.Dataset.Markers
For Each mk In Mks
If mk.Type = "Stimulus" Then
i = i + 1
If i < 6 Or i > 505 Then
nhn.RemoveMarker mk.ChannelNumber, mk.Position, mk.Points, mk.Type, _
mk.Description
End If
End If
Next
If i < 505 Then
' Not enough markers?
MsgBox i & " Markers in Dataset!", "Macro 500Stimuli"
Exit Sub
End If
nhn.Finish
End Sub
Wenn in die Mittelung nur jeder dritte Stimulus "S 1" eingehen soll, haben Sie zwei
Möglichkeiten, dies zu bewerkstelligen.
1. Löschen Sie alle anderen Stimuli "S 1" aus dem Datensatz und segmentieren dann nach
den verbliebenen Stimuli.
2. Benennen Sie jeden dritten "S 1"-Stimulus um, und segmentieren Sie dann nach Stimuli
mit dem neuen Namen.
Das Makro "ThirdS1a.vabs" verwendet die erste Methode:
' Search for "S 1" stimulus markers and erase them if they are not
' divisible by 3, i.e. keep every third stimulus marker "S 1".
Sub Main
On Error GoTo CheckError
Dim sDescription As String
Vision Analyzer Makro-Kochbuch
23
sDescription = "S
1"
' Change the string for a different stimulus, be carefully with
' spaces in the name, "S 1" contains two spaces.
Dim nhn As New NewHistoryNode
nhn.Create "Third S1a", ActiveNode
Dim Mks As Markers
Set Mks = ActiveNode.Dataset.Markers
Dim mk As Marker
Dim i As Long
For Each mk In Mks
If mk.Type = "Stimulus" And mk.Description = sDescription Then
i = i + 1
If i Mod 3 Then ' Not divisible by 3?
nhn.RemoveMarker 0, mk.Position, mk.Points, mk.Type, mk.Description
End If
End If
Next
nhn.Finish ' Finish creation.
Exit Sub
CheckError:
' Error
MsgBox Err.Description
End Sub
Wenn Sie eine ähnliche Fragestellung haben, so können Sie die sechste Zeile ändern, um
einen anderen Stimulus zu verwenden ("sDescription = ..."), sowie die Zeile Nummer 17 ("if i
Mod 3 then") um ein anderes Teilungsverhältnis einzustellen.
Das Makro "ThirdS1b.vabs" verwendet die zweite Methode, d.h. es benennt jeden dritten "S
1"-Marker um:
' Search for "S 1" stimulus markers and rename them if they are
' divisible by 3, i.e. rename every third stimulus marker "S 1" to "1000Hz".
Sub Main
On Error GoTo CheckError
Dim sDescription As String
sDescription = "S 1"
' Change the string for a different stimulus, be carefully with
' spaces in the name, "S 1" contains two spaces.
Dim nhn As New NewHistoryNode
nhn.Create "Third S1b", ActiveNode
Dim Mks As Markers
Set Mks = ActiveNode.Dataset.Markers
Dim mk As Marker
Dim i As Long
For Each mk In Mks
If mk.Type = "Stimulus" And mk.Description = sDescription Then
i = i + 1
If i Mod 3 = 0 Then
' Divisible by 3?
' Rename marker: remove / add
nhn.RemoveMarker 0, mk.Position, mk.Points, mk.Type, mk.Description
nhn.AddMarker 0, mk.Position, mk.Points, mk.Type, "1000Hz"
End If
End If
Next
nhn.Finish ' Finish creation.
Exit Sub
CheckError:
' Error
MsgBox Err.Description
End Sub
6.2.2. Erzeugen neuer Daten
Im folgenden Makro wird ein neuer Datensatz als Child-Knoten eines vorhandenen erzeugt.
Dieser enthält die gleichgerichteten Daten des Ursprungsdatensatzes. Da die Daten nicht
vererbt werden, müssen Eigenschaften und Marker explizit gesetzt werden. Hier werden sie
durch die Prozeduren "CopyProperties" und "CopyMarkers" vom Ursprungsdatensatz kopiert.
Dieses Prozeduren können aufgrund ihrer Kapselung sehr einfach auf andere Makros
übertragen werden.
24
Datei "Rectify Data.vabs":
' Rectify data of the active node.
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Dim ds As Dataset
Set ds = ActiveNode.Dataset
' Limit operation to small data sets, i.e. Averages etc.
If ds.Length > 10000 Then
MsgBox "Data set contains " & ds.Length & _
" data points (macro is limited to 10000 data points)."
Exit Sub
End If
Dim nhn As New NewHistoryNode
' Create new data set.
nhn.Create "Rectify", ActiveNode, "", False, ds.Type, ds.Channels.Count, ds.Length, _
ds.SamplingInterval
' Description of operation (operation info)
nhn.Description = "Rectify all channels"
' Copy properties.
CopyProperties ds, nhn
' Copy markers.
CopyMarkers ds, nhn
' Read / modify / write data
Dim fData() As Single
Dim Chs As Channels
Set Chs = ds.Channels
Dim ch As Channel
For i = 1 To Chs.Count
Set ch = Chs(i)
' Read
ch.GetData 1, ds.Length, fData
' Modify
For j = 1 To ds.Length
fData(j) = Abs(fData(j))
Next
' Write
nhn.WriteData i, 1, ds.Length, fData
Next
nhn.Finish
End Sub
' Copy properties from source node to target node.
Sub CopyProperties(dsSrc As Dataset, nhnTarget As NewHistoryNode)
Dim i As Long
Dim Chs As Channels
Set Chs = dsSrc.Channels
Dim ch As Channel
For i = 1 To Chs.Count
Set ch = Chs(i)
nhnTarget.SetChannelName i, ch.Name
nhnTarget.SetRefChannelName i, ch.ReferenceChannel
nhnTarget.SetChannelUnit i, ch.Unit
Dim pos As ChannelPosition
Set pos = ch.Position
nhnTarget.SetChannelPosition i, pos.Radius, pos.Theta, pos.Phi
Next
nhnTarget.SegmentationType = dsSrc.SegmentationType
nhnTarget.Averaged = dsSrc.Averaged
End Sub
' Copy markers from source node to target node.
Sub CopyMarkers(dsSrc As Dataset, nhnTarget As NewHistoryNode)
Dim mk As Marker
Dim Mks As Markers
Set Mks = dsSrc.Markers
For Each mk In Mks
nhnTarget.AddMarker mk.ChannelNumber, mk.Position, mk.Points, _
mk.Type, mk.Description, mk.Invisible
Next
End Sub
Vision Analyzer Makro-Kochbuch
25
Das folgende Makro erstellt eine neue sekundäre History-Datei, die alle "FP1"-Kanäle aller
"Average"-Knoten der primären History-Dateien im aktuellen Workspace erhält.
Datei "Collect FP1.vabs":
' Look in each primary history file for history node "Average" with channel "Fp1".
' If the node and the channel exist, add the channel to a new secondary
' history file called "Collect Fp1", history node "Fp1".
Option Explicit
Sub Main
Dim sFiles() As String
' Container for valid history file names.
Dim hf As HistoryFile
Dim hn As HistoryNode
Dim nCount As Long, nLength As Long, nType As Long
Dim fSamplingInterval As Double
' First count number of files that match the criterions.
For Each hf In HistoryFiles
If hf.LinkedData Then ' Primary history file?
hf.Open
Set hn = hf.FindNode("Average")
If Not hn Is Nothing Then
If Not hn.Dataset("Fp1") Is Nothing Then
If nCount = 0 Then
' Use first data set length as reference
nLength = hn.Dataset.Length
nType = hn.Dataset.Type
fSamplingInterval = hn.Dataset.SamplingInterval
End If
' Only data sets with the same length.
If nLength = hn.Dataset.Length Then
nCount = nCount + 1
ReDim Preserve sFiles(1 To nCount)
' Resize container of names.
sFiles(nCount) = hf.DisplayName
End If
End If
End If
hf.Close
End If
Next
' Now we know the number of channels for the new history node.
Dim nhn As New NewHistoryNode
HistoryFiles.KillFile "Collect Fp1"
' Kill secondary history file if it exists.
' Create a new history file called "Collect Fp1" with the node "Fp1"
nhn.Create "Fp1", Nothing, "Collect Fp1", False, nType, nCount, nLength, fSamplingInterval
Dim i As Long
Dim fData() As Single
For i = 1 To nCount
nhn.SetChannelName i, sFiles(i) & "-Fp1"
' Set name of new channel
' Copy data.
Set hf = HistoryFiles(sFiles(i))
hf.Open
Set hn = hf.FindNode("Average")
Dim ch As Channel
Set ch = hn.Dataset("Fp1")
ch.GetData 1, nLength, fData
nhn.WriteData i, 1, nLength, fData
hf.Close
Next
nhn.Finish
End Sub
6.2.3. Einlesen von Stimulatordaten aus externen Dateien
Das folgende Makro liest Stimulus / Reaktionsdaten aus einer Textdatei. Es wird hierbei
davon ausgegangen, dass die zugehörigen Stimulus-Marker auch im EEG vorhanden sind.
Die Korrektheit der Reaktion dient als Informationsbasis um die Stimulusmarker
entsprechend umzubenennen. Die Markerbeschreibungen werden um "-c" erweitert, wenn die
Reaktion korrekt war ("correct") oder um "-i", wenn die Reaktion falsch ("incorrect") war.
26
Die Textdatei enthält fünf durch Leerzeichen getrennte Spalten mit Nummern: Die erste
Spalte enthält die Ordinalnummer der Stimuli, die vierte Spalte Informationen über die
Richtigkeit der Reaktion, wobei 0 für falsch und 1 für korrekt steht. Die anderen Spalten
werden ignoriert. Wenn eine Zeile mit einem nichtnumerischen Zeichen (außer Leerzeichen)
beginnt, so wird sie ignoriert. Führende Leerzeichen in den Zeilen werden ebenfalls ignoriert.
Beispiel einer Zeile:
12 0 0 1 0
Hier haben wir den zwölften Stimulus (Spalte 1) und die Reaktion ist korrekt (Spalte 4).
Stimulusdateien mit diesem Aufbau sind häufig anzutreffen. Sind Ihre Dateien anders
aufgebaut, so können Sie das Makro leicht anpassen.
Die Stimulus-Info-Datei muss sich im Rohdatenverzeichnis des aktuellen Workspaces
befinden. Ihr Basisname entspricht dem Basisnamen der zugehörigen EEG-Datei, die Endung
lautet ".stm". Beispiel:
Roh-EEG: E0000001.eeg, zugehörige Stimulus-Info-Datei: E0000001.stm.
Durch diese Namensregel lässt sich das Einlesen der Reaktionsdaten in Templates
automatisieren.
Datei "ReadResponses.vabs":
'
'
'
'
'
'
'
'
'
'
'
'
This macro reads stimulus / response information from a text file.
It is assumed that the stimuli are also recorded in the EEG.
The correctness of the response is used to rename the stimulus markers.
A marker's description is expanded with "-c" if the response is correct and with
"-i" if it is incorrect.
The text file contains five columns with numbers:
The first column contains the stimulus number, the fourth column the correctness where
1 indicates "correct" and 0 indicates "incorrect". The other columns are ignored. If
a line starts with a non digit character, the line is skipped. Leading spaces are ignored.
The file must be in the raw data folder and has the extension ".stm". It's base name must
be the same as the base name of the raw eeg file. For example when the raw file is called
"E0000001.eeg" the name of the corresponding stimulus information file is "E0000001.stm".
Const sExtension As String = ".stm" ' Extension of stimulus information file.
' Change if your info files have a different extension.
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Dim sFolder As String ' Folder / directory of stimulus information file.
' Set folder (directory) of the stimulus information file to the raw file folder.
' Change the following line if your info files are located in a different folder.
sFolder = CurrentWorkspace.RawFileFolder
' Build the full file name
Dim sStimFile As String
sStimFile = sFolder & "\" & ActiveNode.HistoryFile.Name & sExtension
Dim nFile As Integer
nFile = FreeFile
' Get a free file handle number.
Open sStimFile For Input As nFile ' Open stimulus info file
Dim tblCorrectness() As Long
' Array of correctness flags
Dim nStimCount As Long
' Number of stimulus information in the stim. info file
' Read in lines and fill tblCorrectness array.
Do Until EOF(nFile)
Dim sLine As String
Line Input #nFile, sLine
sLine = Trim(sLine)
' Remove leading spaces.
' Check wether the first character is a number.
If IsNumeric(Left(sLine, 1)) Then
' Retrieve the different numbers.
Dim cols(1 To 5) As Single ' Array of numbers for each column.
Vision Analyzer Makro-Kochbuch
27
Dim i As Long, nPos As Long, nStim As Long, nCorrectness As Long
For i = 1 To 5
nPos = InStr(sLine, " ")
If nPos > 0 Then
cols(i) = CSng(Left(sLine, nPos - 1))
sLine = LTrim(Mid(sLine, nPos))
else
cols(i) = CSng(sLine)
End If
Next
nStim = CLng(cols(1))
' Stimulus number is in column 1.
' Handle ascending, descending and no order of stimuli.
If nStim > nStimCount Then
ReDim Preserve tblCorrectness(1 To nStim)
nStimCount = nStim
End If
tblCorrectness(nStim) = CLng(cols(4)) ' Correctness flag is in column 4.
End If
Loop
Close nFile
' Now we have all informations we need to set the stimulus markers.
Dim nhn As New NewHistoryNode
nhn.Create "Correctness", ActiveNode
Dim Mks As Markers
Dim mk As Marker
Set Mks = ActiveNode.Dataset.Markers
Dim nStimuliFound As Long, nCorrectResponses As Long, nIncorrectResponses As Long
For Each mk In Mks
If mk.Type = "Stimulus" Then
nStimuliFound = nStimuliFound + 1
' Leave loop if no more entries are in tblCorrectness.
If nStimuliFound = nStimCount Then Exit For
Dim sDescription As String
sDescription = mk.Description
If tblCorrectness(nStimuliFound) > 0 Then
' Correct response?
sDescription = sDescription & "-c"
nCorrectResponses = nCorrectResponses + 1
Else
sDescription = sDescription & "-i"
nIncorrectResponses = nIncorrectResponses + 1
End If
' Call procedure to rename the marker.
RenameMarkerDescription mk, nhn, sDescription
End If
Next
' Write descriptions for operation infos.
' Operation, inherited by templates
nhn.Description = "Checked responses for correctness and coded stimulus markers with " & _
"'-c' (correct) or '-i' (incorrect)" & vbCrLf & vbCrLf
' Operation results, not inherited by templates
nhn.Description2 = "Correct responses found:
" & nCorrectResponses & vbCrLf & _
"Incorrect responses found: " & nIncorrectResponses
nhn.Finish
End Sub
' It is not possible to rename a marker description directly. This procedure does
' this by removing a marker, and then adding a new on.
Sub RenameMarkerDescription(mk As Marker, nhn As NewHistoryNode, sNewDescription As String)
With mk
nhn.RemoveMarker .ChannelNumber, .Position, .Points, .Type, .Description
nhn.AddMarker .ChannelNumber, .Position, .Points, .Type, sNewDescription
End With
End Sub
6.2.4. Einlesen von Kanalpositionen aus externen Dateien
Das folgende Makro liest Elektrodenpositionen aus einer Positionsdatei und setzt sie in einem
neuen Datensatz.
28
Die Positionsdatei hat den folgenden Zeilenaufbau:
<Elektrodennamen>, <Radius>, <Theta>, <Phi>
Beispiel:
Fp1,1,-92,-72
Fp2,1,92,72
Zeilen, die mit dem Kommentarzeichen # starten, werden ignoriert.
Die genaue Definition der Elektrodenpositionen, wie sie im Analyzer verwendet werden,
entnehmen Sie bitte dem Anhand des Benutzerhandbuchs.
Die Positionsdatei muss sich im Rohdatenverzeichnis des aktuellen Workspaces befinden. Ihr
Basisname entspricht dem Basisnamen der zugehörigen EEG-Datei, die Endung lautet ".pos".
Beispiel:
Roh-EEG: E0000001.eeg, zugehörige Positionsdatei: E0000001.pos.
Datei "ReadPositions.vabs":
' This macro reads electrode positions from a text file.
' The text file has the following line format
' (of course without the leading comment character "'"):
' <Electrode Name>,<Radius>,<Theta>,<Phi>
' Example:
'
' Fp1,1,-92,-72
' Fp2,1,92,72
'
' The file must be in the raw data folder and has the extension ".pos". It's base name must
' be the same as the base name of the raw eeg file. For example when the raw file is called
' "E0000001.eeg" the name of the corresponding position file is "E0000001.pos".
Option Explicit
Const sExtension As String = ".pos" ' Extension of electrode position file.
' Change if your info files have a different extension.
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Dim sFolder As String ' Folder / directory of stimulus information file.
' Set folder (directory) of the position file to the raw file folder.
' Change the following line if your position files are located in a different folder.
sFolder = CurrentWorkspace.RawFileFolder
' Build the full file name
Dim sPosFile As String
sPosFile = sFolder & "\" & ActiveNode.HistoryFile.Name & sExtension
Dim nFile As Integer
nFile = FreeFile
' Get a free file handle number.
Open sPosFile For Input As nFile ' Open position info file
' Create new node.
Dim nhn As New NewHistoryNode
nhn.Create "Read Pos", ActiveNode
' Read in lines.
Do Until EOF(nFile)
Dim sLine As String
Line Input #nFile, sLine
sLine = Trim(sLine)
' Remove leading spaces.
' Check wether the first character is a comment character.
If Not Left(sLine, 1) = "#" Then
' No comment?
' Retrieve the different columns.
Dim cols(1 To 4) As String
' Array of strings for each column.
Dim i As Long, nChannel As Long, nPos As Long
For i = 1 To 4
Vision Analyzer Makro-Kochbuch
29
nPos = InStr(sLine, ",")
If nPos > 0 Then
cols(i) = Left(sLine, nPos - 1)
sLine = LTrim(Mid(sLine, nPos + 1))
Else
cols(i) = sLine
End If
Next
' Do we have a corresponding channel in the data set?
nChannel = GetChannelIndex(ActiveNode, cols(1))
If nChannel > 0 Then
nhn.SetChannelPosition nChannel, CLng(cols(2)), CLng(cols(3)), _
CLng(cols(4))
End If
End If
Loop
Close nFile
' Write descriptions for operation infos.
' Operation, inherited by templates
nhn.Description = "Read electrode positions from external file." & vbCrLf & vbCrLf
' Operation results, not inherited by templates
nhn.Description2 = "Position info file: " & sPosFile & vbCrLf
nhn.Finish
End Sub
' Get the index of the channel that matches the given label in the given history node.
Function GetChannelIndex(hn As HistoryNode, sLabel As String) As Long
Dim Chs As Channels
Dim ch As Channel
Set Chs = hn.Dataset.Channels
Dim i As Long
For i = 1 To Chs.Count
If StrComp(Chs(i).Name, sLabel, 1) = 0 Then ' Found?
GetChannelIndex = i
Exit Function
End If
Next
End Function
Eine modifizierte Version dieses Makros mit dem Namen "ReadPosXYZ.vabs" liest
Koordinaten im XYZ-Format und konvertiert diese in das interne Koordinatensystem um.
6.2.5. Exportieren von Frequenzdaten in eine ASCII-Datei
Das folgende Makro exportiert aus dem aktuell dargestellten Frequenzdatensatz das AlphaBand, das hier von 7,5 bis 12,5 Hz definiert ist. Es werden alle Werte, die in diesen Bereich
fallen, exportiert. Mit wenig Aufwand ließe sich stattdessen auch nur der mittlere Wert
exportieren. Es wird hier automatisch überprüft, ob eventuell komplexe Frequenzdaten
vorliegen. In diesem Falle werden die Beträge exportiert.
Das Makro erzeugt eine neue Datei basierend auf den Namen der aktuellen History-Datei und
des aktuellen Datensatzes. Die neue Datei wird im Exportverzeichnis des aktuellen
Workspaces abgelegt. Das Exportverzeichnis legen Sie im Analyzer unter File > Edit
Workspace fest.
Datei "ExportAlpha.vabs":
' Export a frequency interval into an ASCII file.
Option Explicit
' Define band:
Const fIntervalStart = 7.5
' Start in Hertz
Const fIntervalLength = 5
' Length in Hertz
Sub Main
If Not ActiveNode.DataAvailable Then
30
MsgBox "This macro needs an open data window."
Exit Sub
End If
Dim ds As Dataset
Set ds = ActiveNode.Dataset
If ds.Type <> viDtFrequencyDomain And ds.Type <> viDtFrequencyDomainComplex Then
MsgBox "This macro expects data in the frequency domain."
Exit Sub
End If
' Build filename based on the export folder, the history file and the history node.
Dim sFilename As String
sFilename = CurrentWorkspace.ExportFileFolder & "\" & ActiveNode.HistoryFile.DisplayName _
& "_" & ActiveNode.Name & "_Alpha.txt"
' Check for the interval.
Dim nFirstPoint As Long, nPoints As Long
' First data point
nFirstPoint = fIntervalStart / ds.SamplingInterval + 1
nPoints = fIntervalLength / ds.SamplingInterval
If nFirstPoint + nPoints - 1 > ds.Length Then ' Out of range?
MsgBox "The requested interval is out of range."
Exit Sub
End If
Dim nFile As Long ' File handle
nFile = FreeFile
' Create output file.
Open sFilename For Output As nFile
Dim ch As Channel
For Each ch In ds.Channels
' Write channel names first.
Print #nFile, ch.Name;
Dim fData() As Single
ch.GetData nFirstPoint, nPoints, fData
Dim i As Long
Dim fValue As Single
For i = 1 To nPoints
' Complex data ? -> convert.
If ds.Type = viDtFrequencyDomainComplex Then
' fData() contains nPoints * 2 values if complex
fValue = Sqr(fData((i - 1 ) * 2 + 1)^2 + fData((i - 1 ) * 2 + 2)^2)
Else
fValue = fData(i)
End If
Print #nFile, " " & fValue;
Next
Print #nFile ' CrLf
Next
Close nFile
End Sub
Soll dieses Makro in einer History-Vorlage verwendet werden, um immer wieder automatisch
das Alpha-Band zu exportieren, können Sie den folgenden Trick anwenden. Fügen Sie
zwischen den letzten beiden Zeilen die Anweisungen zum Erstellen eines neuen HistoryKnotens ein. Dieser Knoten dient nur als Heimat für das Makro, ändert aber nichts an den
Daten ("ExportAlpa2.vabs"):
Close nFile
' Build a new data set as home of the macro. This allows the usage of the macro
' in a history template.
Dim nhn As New NewHistoryNode
nhn.Create "Export Alpha", ActiveNode
nhn.Description = "Export Alpha Band" & vbCrLf
nhn.Description2 = "Exported to '" & sFilename & "'"
nhn.Finish
End Sub
Vision Analyzer Makro-Kochbuch
31
6.3. Dynamische Parametrisierung
Einige Transformationen können über das "Transformation"-Objekt aufgerufen werden. Da
das Makro die Parameter für die Transformationen, z.B. basierend auf dem Ergebnis einer
FFT o.ä., zur Laufzeit berechnen und übergeben kann, sprechen wir von einer dynamischen
Parametrisierung.
Die Liste der Transformationen, die Sie mit einem Makro aufrufen können, sowie ihre
Parameter-Syntax finden Sie im Kapitel "Aufrufbare Transformationen" des Ole-AutomationReferenzhandbuchs.
Die Vorteile der dynamischen Parametrisierung mit Hilfe vorhandener Transformationen
gegenüber einer vollständigen Implementierung der Algorithmen im Makro sind:



Algorithmen müssen nicht neu entwickelt werden.
Die Datenberechung ist erheblich schneller, es können auch Rohdaten schnell
transformiert werden.
Der Platzbedarf in der History-Datei ist normalerweise gering, da die meisten
Transformationen ihre Daten erst bei Anforderung berechnen und nicht in der HistoryDatei speichern.
Einige sehr interessante Möglichkeiten ergeben sich aus der Kombination von Makros und
der Formelauswerter-Transformation. Sie könnten z.B. neue Kanäle aus vorhandenen
berechnen und hierbei die tatsächlich gemessenen Kanalpositionen verwenden, statt auf
Standardpositionen zurückzugreifen.
Das folgende Beispiel verwendet die Filter-Transformation. Hierbei sollen Rohdaten
basierend auf einer FFT-Analyse im Alpha-Band mit einem Bandpass gefiltert werden. Zuerst
wird dafür, ebenfalls ausgehend von den Rohdaten, die folgende Transformationssequenz
durchgeführt: Segmentation, Artifact Rejection, FFT, Average.
Es soll nun die Frequenz im Alpha-Band des Kanals "O1", die die stärkste Amplitude
aufweist, für eine Bandpass-Filterung (+/-3Hz) aller Kanäle des Rohdatensatzes verwendet
werden. Die nachfolgende Abbildung zeigt die Relation zwischen den Knoten.
Abbildung 6-1: Relation zwischen den Knoten
Das Makro erzeugt den Knoten "Alpha Band Filter" basierend auf einer Analyse des Knotens
"Average". Im Makro finden Sie den Aufruf "Transformation.TryLater". Dieser Befehl sollte
verwendet werden, wenn Daten aus einem Nebenzweig des History-Baums benötigt werden.
Es ist nämlich möglich, dass beim Einsatz dieses Baumes in einer History-Vorlage der Zweig
32
"Alpha Band Filter" zuerst berechnet wird. In diesem Falle fehlen aber noch die FFT-Daten.
"TryLater" veranlasst den Vorlagen-Prozessor, mit dem nächsten Zweig fortzufahren und
später noch einmal zu versuchen, den Knoten "Alpha Band Filter" zu berechnen. Sollte dies
wiederholt nicht möglich sein, obwohl keine neuen Knoten mehr berechnet werden können,
gibt der Vorlagen-Prozessor auf.
Datei " DynParameterization.vabs":
' Example for Dynamic Parameterization
' This macro looks for a history node 'Average' that contains an averaged FFT.
' If found, it looks in the Alpha band for the maximum amplitude in channel "O1".
' Then it uses the frequency at the maximum amplitude as input parameter for
' a bandpass (+/-3Hz) to filter the active data set with the 'Filters' transformation.
Option Explicit
' Define band:
Const fIntervalStart = 7.5
' Start in Hertz
Const fIntervalLength = 5
' Length in Hertz
Const fBandwidth = 6
' Bandwidth (+/-3Hz)
Const sTestChannel = "O1"
' Test channel, i.e. channel where the Alpha band is checked.
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
On Error Resume Next
' Look for node 'Average' that is in frequency domain
Dim FFTAverageNode As HistoryNode
Set FFTAverageNode = ActiveNode.HistoryFile.FindNode("Average")
Do
' No more history node? -> try later if in template mode.
If FFTAverageNode Is Nothing Then
Transformation.TryLater
' If the macro has not terminated here, we are not in template mode.
MsgBox "Missing history node 'Average' (FFT average)"
Exit Sub
End If
Dim ds As Dataset
Set ds = FFTAverageNode.Dataset
' Frequency domain?
If ds.Type = viDtFrequencyDomain Or ds.Type = viDtFrequencyDomainComplex Then
Exit Do
End If
' Not frequency domain? -> Look for the next history node with the same name.
Set FFTAverageNode = ActiveNode.HistoryFile.FindNextNode
Loop
' Now we have our averaged FFT history node.
' Lets get the data from the test channel.
Dim ch As Channel
Set ch = ds(sTestChannel)
If ch Is Nothing Then
Message "Missing test channel '" & sTestChannel & "'"
Exit Sub
End If
' Check for the interval.
Dim nFirstPoint As Long, nPoints As Long
nFirstPoint = fIntervalStart / ds.SamplingInterval + 1
nPoints = fIntervalLength / ds.SamplingInterval
If nFirstPoint + nPoints - 1 > ds.Length Then ' Out of range?
Message "The requested interval is out of range."
Exit Sub
End If
' Look for maximum value in the defined interval.
Dim fData() As Single
ch.GetData nFirstPoint, nPoints, fData
Dim i As Long
Dim fValue As Single, fMax As Single
Dim nMaxPosition As Long
Vision Analyzer Makro-Kochbuch
33
fMax = -1
nMaxPosition = 0
For i = 1 To nPoints
' Complex data ? -> convert.
If ds.Type = viDtFrequencyDomainComplex Then
' fData() contains nPoints * 2 values if complex
fValue = Sqr(fData((i - 1 ) * 2 + 1)^2 + fData((i - 1 ) * 2 + 2)^2)
Else
fValue = fData(i)
End If
If fValue > fMax Then ' New maximum found?
fMax = fValue
nMaxPosition = i - 1
End If
Next
' Convert position to frequency
Dim fFrequency As Single
fFrequency = (nFirstPoint + nMaxPosition - 1) * ds.SamplingInterval
' Build parameter string
Dim sParameters As String
sParameters = "Lowcutoff=" & SingleToString(fFrequency - fBandWidth / 2) _
& ",24;highcutoff=" & SingleToString(fFrequency + fBandWidth / 2) & ",24"
' Perform the transformation
Transformation.Do "Filters", sParameters, ActiveNode, "Alpha Band Filter"
Exit Sub
CheckError:
Resume Next
End Sub
' The function converts a single value to
' is always a dot ('.'), even in European
Function SingleToString(fValue As Single)
SingleToString = Replace(Str(fValue),
End Function
34
a string. In opposite to 'Str' the decimal delimeter
countries.
As String
",", ".")
7. Tipps für Fortgeschrittene
7.1. Variablendeklaration
Wie bereits im Kapitel "Kurzer Streifzug durch Basic" erwähnt, können Sie optional
Variablen explizit deklarieren:
Dim fData as Single
Sie können aber auch den Zwang zur Deklaration einführen. Dafür verwenden Sie die
Anweisung:
Option Explicit
die Sie als erste Zeile des Makros oberhalb von "Sub Main" einfügen. In diesem Falle
erhalten Sie eine Fehlermeldung, wenn Sie das Makro ausführen wollen, und es undeklarierte
Variablen verwendet. Der Vorteil liegt darin, dass Tippfehler bei Variablennamen nicht mehr
möglich sind, die sonst zur impliziten Erstellung von Variablen geführt hätten.
Beispiel:
Dim fValue as Single
fValue = 1
fValue = fVolue + 1
Hier haben wir in der letzten Zeile statt "fValue + 1" "fVolue + 1" geschrieben. Ohne "Option
Explicit" gibt es keine Fehlermeldung, sondern nur ein falsches Resultat. "fVolue" wird
implizit erzeugt und auf den Wert 0 gesetzt.
Solche Fehler sind bei größeren Makros äußerst schwer zu finden, so dass sich der Einsatz
von "Option Explicit" dort meistens bezahlt macht.
Vision Analyzer Makro-Kochbuch
35
7.2. Eigene Dialogboxen
Neben den Ein- und Ausgabemöglichkeiten "InputBox" und "MsgBox" können Sie auch
komplexere Dialogboxen erzeugen. Beginnen Sie dazu ein neues Makro mit Macro > New.
Anschließend wählen Sie Edit > UserDialog. Es erscheint ein Dialog, der Ihnen die
Erstellung einer Dialogvorlage ermöglicht.
Abbildung 7-1: Editor für Dialogvorlage
Klicken Sie nun mit der Maus auf das folgende Symbol.
Anschließend klicken Sie auf das gepunktete Feld rechts daneben. Es entsteht eine Textbox
mit dem Text ".TextBox1".
Wiederholen Sie die Aktion mit dem folgenden Symbol.
Ihr Dialog sollte nun eine Textbox und einen OK-Button enthalten. Sie können mit der Maus
Position und Größe der Elemente, einschließlich der Dialogbox selbst ändern. Betätigen Sie
anschließend den folgenden Button.
Sie befinden sich wieder im Texteditor. Das Programm hat den Code für den Dialog
automatisch eingefügt. Er sieht etwa wie folgt aus:
Sub Main
Begin Dialog UserDialog 400,203 ' %GRID:10,7,1,1
36
TextBox 70,49,270,21,.TextBox1
OKButton 110,112,90,21
End Dialog
Dim dlg As UserDialog
Dialog dlg
End Sub
Führen Sie nun das Makro aus, so erscheint ein Dialog mit der Textbox und dem OK-Button.
Sie können Text eingeben und den OK-Button betätigen.
Wie kommen Sie aber nun an den eingegebenen Text? Ganz einfach, die automatisch
deklarierte Variable "dlg" ist ein Objekt mit der Eigenschaft "TextBox1". "dlg.TextBox1"
liefert also den Text, wie das folgende erweiterte Makro zeigt.
Sub Main
Begin Dialog UserDialog 400,203 ' %GRID:10,7,1,1
TextBox 70,49,270,21,.TextBox1
OKButton 110,112,90,21
End Dialog
Dim dlg As UserDialog
Dialog dlg
' Start dialog.
MsgBox dlg.TextBox1
' Show user input.
End Sub
Die Zeile "MsgBox dlg.TextBox1" gibt den eingegebenen Text wieder aus.
Sie können auch einen voreingestellten Text bestimmen, der vom Anwender optional
überschrieben werden kann. Dafür weisen Sie "dlg.TextBox1" einen Text zu, bevor der
Dialog angezeigt wird:
Sub Main
Begin Dialog UserDialog 400,203 '
TextBox 70,49,270,21,.TextBox1
OKButton 110,112,90,21
End Dialog
Dim dlg As UserDialog
dlg.TextBox1 = "Hello world"
'
Dialog dlg
'
MsgBox dlg.TextBox1
'
End Sub
%GRID:10,7,1,1
Assign default text.
Start dialog.
Show user input.
Wollen Sie den Dialog erweitern oder verändern, so bewegen Sie den Cursor zwischen
"Begin Dialog ..." und "End Dialog" und wählen dann wieder Edit > UserDialog. Sie können
nun weitere Elemente in den Dialog einbauen, wie Bilder, Radio-Buttons, Auswahllisten und
vieles mehr. Betätigen Sie die Taste "F1", um sich Informationen über die verschiedenen
Elemente des Dialog-Editors zu verschaffen.
Nach Beendigung des Dialog-Editors können Sie in der Online-Hilfe weitere Informationen
über die Möglichkeiten des benutzerdefinierten Dialogs erhalten.
Vision Analyzer Makro-Kochbuch
37
7.3. Funktionen / Prozeduren
Sie können eigene Funktionen und Prozeduren definieren, um die Übersichtlichkeit eines
Makros zu erhöhen oder seine Länge zu reduzieren.
Im folgenden Beispiel wird der Prästimulusinterval ausgegeben. Dafür sucht die Funktion
"FindTimeZero" einen Marker des Typs "Time 0" und liefert dessen Position zurück.
' Print time 0 of the active node.
Sub Main
If Not ActiveNode.ContainsData Then
MsgBox "No active node found."
Exit Sub
End If
Dim ds As Dataset
Set ds = ActiveNode.Dataset
' Sampling interval is in microseconds -> convert to ms.
MsgBox "Prestimulus: " & (FindTimeZero(ds) - 1) * ds.SamplingInterval / 1e3 & "ms"
End Sub
' Find position of time 0 marker (prestimulus interval length)
Function FindTimeZero(ds As Dataset) As Long
Dim mk As Marker
FindTimeZero = 1
For Each mk In ds.Markers
' Case insensitive comparison.
If StrComp(mk.Type, "Time 0", 1) = 0 Then
FindTimeZero = mk.Position
Exit Function
End If
Next
End Function
Das folgende Beispiel definiert die Prozedur "RenameMarkerDescription", die die
Beschreibung eines Markers ändert.
' Rename all stimulus markers with the description "S 1" to "Hand".
Sub Main
Dim nhn As New NewHistoryNode
nhn.Create "S1->Hand", ActiveNode
Dim Markers As Markers
Dim mk As Marker
Set Markers = ActiveNode.Dataset.Markers
For Each mk In Markers
If mk.Type = "Stimulus" And mk.Description = "S 1" Then
RenameMarkerDescription mk, nhn, "Hand"
End If
Next Mk
nhn.Finish
End Sub
' It is not possible to rename a marker description directly. This procedure does
' this by removing a marker, and then adding a new one.
Sub RenameMarkerDescription(mk As Marker, nhn As NewHistoryNode, NewDescription As String)
nhn.RemoveMarker mk.ChannelNumber, mk.Position, mk.Points, mk.Type, mk.Description
nhn.AddMarker mk.ChannelNumber, mk.Position, mk.Points, mk.Type, NewDescription
End Sub
38
7.4. Unterdrückung von Dialogen in History-Vorlagen
Wenn Sie, ähnlich wie beim Analyzer in den meisten Transformationen, Eingabeparameter
vom Anwender annehmen wollen, wenn er das Makro direkt aufruft, nicht jedoch wenn das
Makro in einer History-Vorlage gespeichert ist, können Sie zum Speichern der Parameter die
Eigenschaft "NewHistoryNode.Description" verwenden. Wenn ein neues "NewHistoryNode"Objekt beim Aufruf eines Makros erzeugt wird, so ist "Description" leer, im VorlagenKontext jedoch gefüllt mit dem Text, den Sie bei der Ausführung des Makros gesetzt haben.
Sie können also die Benutzereingabe in "Description" speichern. Nach dem Erstellen des
neuen Knotens mit "NewHistoryNode.Create()" überprüfen Sie, ob "Description" leer ist.
Wenn ja, lassen Sie den Anwender eine Eingabe tätigen, sonst befinden Sie sich im VorlagenKontext und werten "Description" aus. Das folgende Beispiel illustriert das Verfahren.
Datei "RenameMarkersInteractive.vabs"
' Rename markers
' User input for old/new name, skipped input in template processing
Sub Main
If Not ActiveNode.DataAvailable Then
MsgBox "This macro needs an open data window."
Exit Sub
End If
Dim nhn As New NewHistoryNode
Dim sOldName As String, sNewName As String
nhn.Create "Renamed Markers", ActiveNode
If nhn.Description = "" Then
' Interactive mode
sOldName = InputBox("Enter markers name", "Rename Markers")
sNewName = InputBox("Enter new name", "Rename Markers")
nhn.Description = "Rename Markers" & vbCrLf & "Old name: " & sOldName & _
vbCrLf & "New name: " & sNewName
Else ' Template mode
' Retrieve names from description text
Dim nPos As Integer, sTemp As String
sTemp = nhn.Description
nPos = InStr(sTemp, "Old name: ")
If nPos > 0 Then
sTemp = Mid(sTemp, nPos + Len("Old name: "))
nPos = InStr(sTemp, vbCrLf)
sOldName = Left(sTemp, nPos - 1)
sNewName = Mid(sTemp, nPos + 2 + Len("New name: "))
End If
End If
If sOldName = "" Then
MsgBox "Missing old name"
Exit Sub
ElseIf sNewName = "" Then
MsgBox "Missing new name"
Exit Sub
End If
Dim Markers As Markers
Dim mk As Marker
Set Markers = ActiveNode.Dataset.Markers
For Each mk In Markers
If mk.Description = sOldName Then
RenameMarkerDescription mk, nhn, sNewName
End If
Next Mk
nhn.Finish
End Sub
...
Vision Analyzer Makro-Kochbuch
39
7.5. Debugging ("Entwanzen", Fehlersuche)
Unter Debugging verstehen wir hier die Fehlersuche in einem Makro. Neben der einfachen
visuellen Inspektion des Makrotextes können Sie das Makro zeilenweise durchlaufen,
jederzeit Variablenwerte abfragen und Haltepunkte setzen, an denen das Makro pausiert.
Anhand des folgenden Beispiels werden wir diese Schritte demonstrieren.
Sub Main
i = 1
i = i + 1
MsgBox i
End Sub
Tippen Sie das Makro ein. Betätigen Sie anschließend die Taste F8. Das Fenster ist gespalten
in einen oberen und einen unteren Bereich. Oben befinden sich vier Reiter mit den Titeln
"Immediate", "Watch", "Stack" und "Loaded". Uns interessiert in dieser Demonstration nur
der "Immediate"-Reiter. Die erste Zeile des Makros ist gelb markiert. Diese Markierung zeigt
die aktuelle Makrozeile an. Betätigen Sie nun die F8-Taste noch zweimal. Die Markierung
befindet sich nun in der dritten Zeile, "i = i + 1". Klicken Sie nun mit der Maus in das
"Immediate"-Fenster. Geben Sie den folgenden Text ein:
? i
Betätigen Sie anschließend die Enter-Taste. Es erscheint "1%". "%" steht für Ganzzahl.
Wichtig ist aber vor allen Dingen, dass Sie den Inhalt der Variablen auslesen können.
Betätigen Sie nun die F8-Taste und fragen Sie noch einmal nach dem Wert von i. Jetzt
erscheint der Wert "2%". Wenn Sie sonst nichts weiter wissen wollen, so betätigen Sie die
Taste F5 und das Programm läuft durch bis zum Ende.
Um einen Haltepunkt zu setzen, bewegen Sie den Cursor z.B. in die vierte Zeile und drücken
Sie die Taste F9. Die Zeile wird dunkelrot hinterlegt. Starten Sie nun das Makro mit F5. Es
stoppt in der vierten Zeile. Mit einer weiteren Betätigung von F5 setzt das Makro die
Ausführung fort.
Wir haben eben die Debug-Session mit den Funktionstasten durchgespielt. Sie können
alternativ auch die Menüs Macro und Debug, bzw. deren Untermenüs verwenden. Auch die
Werkzeugleiste bietet die entsprechenden Befehle an. Allerdings geht es mit den
Funktionstasten am schnellsten.
Sie können übrigens nicht nur Variablenwerte abfragen, sondern auch den Analyzer
erforschen und Kommandos schicken. Betätigen Sie dazu den Menüpunkt View > Always
Split. Jetzt ist das Fenster auch gespalten, wenn kein Makro ausgeführt wird. Klicken Sie mit
der Maus in das "Immediate"-Fenster und tippen Sie
? HistoryFiles(1).Name
und betätigen anschließend die Enter-Taste. Der Name Ihrer ersten History-Datei erscheint.
Die Eingabe "HistoryFiles(1).Open" öffnet die Datei und "HistoryFiles(1).Close" schließt sie
wieder. Sie können also hier die Wirkung der verschiedenen Automation-Befehle interaktiv
testen.
Weitere Informationen zum Thema Debugging erhalten Sie in der Online-Hilfe.
40
Herunterladen