Horoskopgenerator

Nein, um jene Horoskope, die Sie gemeinhin in Zeitschriften finden, geht es hier nicht. Wir stellen eine Datenbanklösung vor, die echte Geburtshoroskope auf astrologischer Basis erzeugen kann, wobei eine Deutung dieser allerdings nicht stattfindet. Ob Sie daran glauben, oder nicht, spielt weniger eine Rolle. Hier geht es um einige Techniken, die sich auch in anderen Datenbanken einsetzen lassen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1602_Horoskope.zip.

Geburtshoroskope

In Bild 1 finden Sie gleich ein Beispiel für ein Geburtshoroskop, wie es das Formular frmHoroskope der Beispieldatenbank generiert. Im Block links oben geben Sie Daten einer Person ein, zu der das Horoskop erstellt werden soll. Das sind neben dem Namen das Geburtsdatum und die Geburtszeit, und schließlich noch der Geburtsort. Aus diesem berechnet die Datenbank mithilfe der Tabelle tblOrte den Längen– und Breitengrad, die Sie gegebenenfalls aber auch manuell eingeben können. Ein Klick auf die Schaltfläche Update berechnet nun die Positionen der Horoskopelemente, listet diese tabellarisch auf, und zeigt sie in der Grafik rechts im Formular an. Sollte die Materie für Sie neu sein, wovon wir einmal ausgehen, so folgen hier einige Erläuterungen zur Horoskopberechnung und -gestaltung.

Das Formular frmHoroskope erstellt echte Geburtshoroskope unter Zuhilfenahme einer speziellen DLL

Bild 1: Das Formular frmHoroskope erstellt echte Geburtshoroskope unter Zuhilfenahme einer speziellen DLL

Für ein Geburtshoroskop werden die Positionen der Planeten des Sonnensystems auf astronomischer Grundlage berechnet. Ausschlaggebend hierfür sind Geburtsdatum und -zeit, da sich die Planeten schließlich dauernd fortbewegen. Als Ergebnis erhalten Sie den Winkel, welcher sich vom sogenannten Frühlingspunkt als Nullkoordinate zum Planeten längs über die Ekliptik aufspannt. Die Ekliptik ist quasi jene Bahn, die um die Erde herumführt, und auf welcher sich die Planeten bewegen. Der Winkel kann daher Werte von 0 bis 360 Grad annehmen. Dieser Kreis wird linear in zwölf Bereiche unterteilt, die sich den verschiedenen Tierkreiszeichen zuordnen. Tatsächlich stehen in diesen Bereichen nur ungefähr die entsprechenden Sternbilder, weil auch diese sich mit der Zeit fortbewegen, die Astrologie behält die 30-Grad-Unterteilung aber stringent bei.

Die Berechnung ergibt also für jeden Planeten eine Position, die einem Tierkreiszeichen zugeordnet ist. Damit bekommt der Planet nach astrologischer Auslegung einen bestimmten Charakter. So befindet sich die Sonne von Herbert Klammer in Bild 1 etwa im Winkel von 141,2 Grad zum Ursprung und ist damit dem Zeichen Löwe zugeteilt. Und die Sonnenposition bestimmt nach dem Alltagsgebrauch das generelle Sternzeichen. Herbert ist also Löwe. Würde ein Astrologe dies deuten, so käme er zur Auffassung, dass Herbert in seinem Wesen recht selbstbewusst daher kommt, sich gerne im Mittelpunkt sieht, und auch motorisch ein gewisses Kraftpotential aufweist. Er ist nicht so leicht aus der Fassung zu bringen und das geborene Alpha-Tier.

ähnlich werden auch die weiteren Planetenpositionen gedeutet, wobei jeder Planet für eine bestimmte Charakteristik steht. übrigens werden Sonne und Mond in der Astrologie genau so behandelt, wie Planeten. Hinzu kommt noch als wesentlicher Bestandteil des Horoskops der Aszendent, welcher in der Grafik durch die rote Linie symbolisiert ist. Das ist kein Planet, sondern jene Position, welche zum Geburtszeitpunkt eben am Horizont im Osten aufging. Auch diese kann einem Sternzeichen zugeordnet werden. Der Aszendent soll, so die Astrologie, ähnlich gewichtig sein, wie die Sonnenposition, und gäbe Auskunft über die Außenwirkung einer Person.

Nicht in unsere Lösung eingebaut sind die sogenannten Häuser des Horoskops. Das wären, ausgehend vom Aszendenten, ebenfalls zwölf Bereiche des die Erde entlang der Ekliptik umgebenden Universums. Sie sollen Rückschlüsse über einzelne Lebensbereiche einer Person zulassen. Wir lassen das weg, weil die Lösung dann noch komplexer ausgefallen wäre, als sie es ohnehin schon ist.

Schließlich haben Sie möglicherweise auch schon von den vier Elementen Erde, Wasser, Luft und Feuer gehört. Jedes Tierkreiszeichen korreliert mit einem dieser Elemente. Befindet sich zum Geburtszeitpunkt in einem Tierkreisbereich ein Planet, so verstärkt dieser den Charakter des zugeordneten Elements. Man kann die Elemente somit über eine Gewichtungsformel aufaddieren und kommt so zu den prozentualen Anteilen, die links unten im Formular zu sehen sind. Auf sie bezieht sich die Minideutung in der untersten Zeile übrigens.

Für das Horoskop sind an sich nur die Werte der Planetenpositionen relevant. übersichtlicher ist jedoch deren Darstellung in einer Grafik. Jeder Planet besitzt ein Symbol und jedes Tierkreiszeichen ebenso. In der Abbildung befindet sich etwa der Jupiter links im Sternzeichen Fische. Wenn Sie herausfinden möchten, welches Symbol welchem Element entspricht, so klicken Sie einfach mit der Maus auf entweder ein Sternzeichen im äußeren Ring, oder auf das Symbol eines Planeten im inneren Bereich. Rechts unten im Info-Label wird dann der Name des Elements ausgegeben.

Verwendete Techniken

Schauen wir uns im überblick an, wie die Lösung in der Beispieldatenbank realisiert wurde.

Die Grundlage der Datenbank sind natürlich ihre Tabellen. Hier gibt es eine Tabelle tblHoroskope, welche die Personendaten aufnimmt – man hätte sie genauso gut tblPersonen nennen können. Zwei weitere Tabellen listen als Nachschlagetabellen die Planeten und Sternzeichen namentlich auf. Zwischen diesen und der Horoskoptabelle besteht über die n:m-Tabelle tblHoroskopZeichen eine Beziehung. Schließlich vervollständigt eine Elemente-Tabelle tblElemente, verknüpft mit den Tierzeichen und Planeten, das Datenmodell.

Das Formular frmHoroskope baut auf diesen Tabellen auf, wobei nicht tblHoroskope die Datenherkunft darstellt, sondern ein Abfrage qry_Horoskopzeichen. Warum das so ist, das sehen wir noch später. Neben den Textfeldern für die Werte der zugrundeliegenden Daten finden sich noch ungebundene Textfeldern, deren Inhalt per VBA berechnet wird. Für die Grafik ist ein Bildsteuerelement verantwortlich, welches von einem roten Liniensteuerelement für den Aszendenten überlagert wird. Einzelne Textfelder, die einen speziellen Astrologie-Font als Schriftart aufweisen, werden zur Laufzeit über der Grafik positioniert, wofür die VBA-Routinen des Formularmoduls herangezogen werden.

Die Berechnung der Ephemeriden, also der astronomischen Planetenpositionen zu einem bestimmten Zeitpunkt, ist einen Angelegenheit, die über VBA-Code nicht zu bewältigen ist, Für die iterative Lösung des als Mehrkörperproblem in der Physik bekannten Themas kommen ungeheuer komplexe differenzielle Gleichungssystem zum Einsatz, für die die Performance von VBA nicht ausreicht. Zum Glück gibt es eine frei verfügbare und ständig weiterentwickelte Komponente des Astrodienst Zürich, die sogenannte Swiss Ephemerisis. Die kommt in Gestalt einer einzigen DLL daher, welche über API-Programmierung angesprochen wird. Sie berechnet mit nur einem Funktionsaufruf alle Positionen der Planeten und weitere Elemente. Wenn an die Genauigkeit dieser Berechnung höhere Anforderungen gestellt werden, so bedient sie sich optional weiterer Komponenten, nämlich einiger Datendateien, die vorberechnete Tabellen enthalten.

DLL und die für den relevanten Zeitraum verantwortlichen Dateien sind der Beispieldatenbank beigefügt.

Die Datenbank kommt ansonsten ohne weitere Bibliotheken aus. Zusätzliche Verweise sind also nicht zu setzen.

Installation

Es gibt keine Installation. Die DLL muss nicht auf dem Rechner registriert werden. Es reicht aus, dass sie sich im Ordner parallel zur ACCDB befindet. Lediglich die Schriftart AstroFont99 muss installiert werden. Dazu rechtsklicken Sie die Datei Awf99.ttf im Explorer und wählen aus dem Kontextmenü Installieren aus. Die Schriftart wird damit in den Font-Ordner von Windows kopiert und im System registriert.

API-Programmierung

Mit diesem Thema hatten wir es in Access [basics] noch nicht zu tun. Grund dafür ist, dass es sich dabei um eine eher fortgeschrittene Technik handelt, vor der viele VBA-Entwickler zurückschrecken. Dabei ist die Sache einfacher, als man denkt. Zwar könnte man damit nicht nur eine komplette Ausgabe von Access [basics] füllen, sondern ohne weiteres ein ganzes Buch, doch die Grundlagen sind schnell erläutert.

Unter API-Programmierung (Application Programming Interface) versteht man landläufig den Zugriff auf Funktionen in externen Bibliotheken. Das sind in der Regel die System-DLLs von Windows, die manche Funktionen nach außen hin veröffentlichen. Gerade hier liegt der Hund begraben. Denn zum einen gibt es kein einfaches Verzeichnis dieser Funktionen. Es ist die Windows-SDK-Dokumentation der Microsoft-MSDN-Seiten im Internet zu Rate zu ziehen. Zum anderen ist die Zielgruppe dieser Dokumentation der C-Entwickler. Beispiele und Funktionssyntax in den Artikel sind allesamt in der Sprache C gehalten, der Inhalt auf Englisch. Verständlicherweise ist das für VBA-Entwickler schwer verdauliche Kost.

Neben den Windows-eigenen Funktionen lassen sich nach dem gleichen Prinzip jedoch auch DLLs von Drittherstellern ansprechen. Hier ist eine ausführliche Dokumentation natürlich noch wichtiger. Und vor allem benötigen Sie die Syntax der API-Funktionen auch in der Sprache VB(A). Denn das übersetzen einer C-Syntax nach VBA ist eine Angelegenheit, die Experten vorbehalten ist.

Nehmen wir einfach, statt uns in abstrakten Theorien zu ergehen, ein Beispiel zur Hand. Sie möchten etwa über eine API-Funktion die Titelzeile des Access-Hauptfensters auslesen. Dafür kommt die Funktion GetWindowText in der user32.dll, beheimatet im Systemordner von Windows, infrage. Damit sie angesprochen werden kann, benötigen wir zunächst zwingend eine VBA-Deklaration dieser Funktion. Sie finden sie in der ersten Zeile von Listing 1.

Public Declare Function GetWindowText _
     Lib "user32.dll" _
     Alias "GetWindowTextA" _
     (ByVal hwnd As Long, _
     ByVal lpString As String, _
     ByVal cch As Long) As Long
Sub AccessFensterTitel()
     Dim hwndAcc As Long
     Dim sTitle As String
     Dim ret As Long
     
     hwndAcc = Application.hWndAccessApp
     sTitle = String(255, 0)
     ret = GetWindowText(hwndAcc, _
                         sTitle, 255)
     If ret > 0 Then
         Debug.Print Left(sTitle, ret)
     End If
End Sub

Listing 1: API-Routine zum Auslesen des Fenstertitels von Access

Eine API-Deklaration findet immer im Kopf eines Moduls statt und wird durch den Ausdruck Declare Function oder Declare Sub eingeleitet. Je nachdem, ob die Funktion dann nur in diesem Modul oder von überall her aufgerufen werden soll, stellen Sie entweder ein Public oder ein Private voran.

Darauf folgt der Name der Funktion, welcher hier GetWindowText lautet. Würden Sie die DLL damit aufrufen, so käme es zu einer Fehlermeldung DLL-Einsprungspunkt GetWindowText in user32.dll nicht gefunden. Tatsächlich gibt es diese Funktion in der DLL auch gar nicht! Vielmehr existieren zwei: einmal eine für ANSI-Strings ausgelegte und einmal eine für Unicode-Strings. Deren Namen laute dann aber GetWindowTextA und GetWindowTextW – das W steht für Wide Character. Der folgende Ausdruck Alias trägt dem Rechnung. Zwar sprechen Sie die Funktion im Code später mit GetWindowText an, der Aufruf wird jedoch über den Alias an GetWindowTextA der DLL weitergeleitet. Bliebe noch zu erwähnen, dass API-Funktionen case sensitive sind. Sie haben also auf korrekte Groß- und Kleinschreibung der Zeichen zu achten.

Vor dem Alias, aber nach dem Funktionsnamen, steht über den Ausdruck Lib noch die Bibliothek, in der die Funktion gefunden werden kann. Hier handelt es sich um die user32.dll. Für den hier eingesetzten String gilt Folgendes: Entweder setzen Sie den Namen einer DLL-Datei ein, die sich im Suchpfad von Windows befindet, oder Sie geben den kompletten Pfad der Datei an. Die Dateierweiterung können Sie auch weglassen, wenn es sich um eine DLL handelt, da Windows dann automatisch von einer DLL ausgeht. Diese Angaben wären also alle korrekt:

user32
user32.dll
c:\windows\system32\user32.dll

Der Suchpfad von Windows umfasst im Wesentlichen die Systemordner. Befindet sich eine Bibliotheksdatei außerhalb dieses Suchpfads, so ist der komplette Pfad anzugeben – mit einer Ausnahme: DLLs, die Access für sein eigenes Funktionieren schon geladen hat, können ebenfalls ohne Ordnerpfad angesprochen werden.

Was sich nun in der umgebrochenen Deklarationszeile anschließt ist die Liste der erwarteten Parameter und der Rückgabewert der Funktion, genau so, wie es sich bei einer VBA-Funktion verhält. Wir haben hier den Parameter hwnd des Datentyps Long vor uns, lpString ist die String-Variable, die nach dem Aufruf den Fenstertitel erhalten soll, und cch des Typs Long gibt an, wie lange der Text maximal sein darf. Die Rückgabe der Funktion ist ebenfalls vom Typ Long. Bei Erfolg ist deren Wert größer, als 0, und gibt dann die Anzahl der in den String eingesetzten Zeichen wider.

Extrem wichtig ist auch der übergabetyp der Parameter. Es ist, im Gegensatz zu den meisten VBA-Funktionen, nicht egal, ob eine ByVal– oder eine ByRef-übergabe stattfindet. Eine Erläuterung, warum das so ist, sprengte allerdings den Rahmen dieses Beitrags.

Einmal deklariert, lässt sich die API-Funktion über deren Namen von beliebiger Stelle Ihres Codes aus aufrufen.

In der Routine AccessFensterTitel, die Sie etwa aus dem VBA-Direktfenster aus aufrufen, wird der Variablen hwndAcc zunächst das sogenannte Fenster-Handle von Access zugewiesen. Das steht über die Eigenschaft hwndAccessApp des Application-Objekts immer zur Verfügung. Anhand dieses Fenster-Handles berechnet die user32.dll schließlich den Fenstertitel in der übergebenen Variablen sTitle. Der Wert dieser Variablen wird davor jedoch noch mittels String()-Funktion auf 255 Null-Zeichen gebracht. Hier wird bereits eine Abweichung zu VBA-Routinen sichtbar. Während ein String hier meist von der Funktion selbst eingerichtet wird, sieht das häufig bei API-Funktionen anders aus. Diese gehen davon aus, dass der übergebene String bereits alloziert ist, also schon eine feste Speichergröße aufweist. Würden Sie sTitle ungefüllt übergeben, so enthielte die Variable nur einen Null-String der Länge 0. Die API-Funktion versuchte dennoch, den Fenstertitel an die Adresse des Strings zu schreiben – mit fatalen Folgen! Es käme zum Schreiben in ungeschützte Speicherbereiche, was Access meist mit einem Absturz quittiert.

Zurück zu unserer Prozedur. Die Variable ret nimmt die Anzahl an Zeichen entgegen, die die API-Funktion produziert hat. Ist diese größer, als 0, dann lief alles glatt, und der relevante Teil des Strings wird über Left und Debug.Print in das VBA-Direktfenster ausgegeben.

Nun werden Sie wohl kaum die Syntax der API-Funktionen selbst anfertigen wollen. Zwar kann es nicht schaden, sich die Dokumentation etwa zu GetWindowText im Internet zu Gemüte zu führen, um weitere Details zu erfahren, aber das übersetzen von C nach VBA überlassen Sie einfach Anderen. Ein Werkzeug, das sich dafür gut eignet, ist der API-Viewer von ActiveVB (siehe Bild 2), eine Standalone-Anwendung, die praktisch alle Windows-Funktionen auflistet, die Ihnen über den Weg laufen werden. über die Suchzeile navigieren Sie zum Funktionsnamen und doppelklicken ihn, worauf die Deklaration in das rechte Fenster übertragen wird. Das können Sie mehrmals hintereinander durchführen. Zuletzt klicken Sie auf das Copy-Symbol, was den API-Viewer veranlasst, alle gewünschten Deklarationen als Text in die Zwischenablage zu kopieren. Fügen Sie diese danach in ein VBA-Modul Ihrer Wahl ein.

Der API Viewer enthält die gebräuchlichsten Windows-API-Deklarationen

Bild 2: Der API Viewer enthält die gebräuchlichsten Windows-API-Deklarationen

Ephemeridenberechnung per API-Funktion

Die Planetenposition werden in der Beispieldatenbank mithilfe der zusätzlichen DLL swedll32.dll berechnet. Die von dieser Bibliothek exportierten Funktionen sind auf den Seiten der Entwickler gut dokumentiert. Auch hier haben wir es allerdings mit dem üblichen C-Slang zu tun. Der Hersteller lässt uns aber nicht im Regen stehen, denn es steht auch ein VB6-Projekt bereit, welches schon die API-Deklarationen enthält.

Möchten Sie weiterlesen? Dann lösen Sie Ihr Ticket!
Hier geht es zur Bestellung des Jahresabonnements des Magazins Access [basics]:
Zur Bestellung ...
Danach greifen Sie sofort auf alle rund 400 Artikel unseres Angebots zu - auch auf diesen hier!
Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:

Schreibe einen Kommentar