Mobile Times Business Club
Startseite : Mobility : OPL Kurs
OPL OPL Kurs, Teil 6 Aktualisierung
2007-03-01
01: Einführung
02: Menüs und mehr
03: Basics, Zufall & Mathetrainer
04: Spielend lernen - Würfel, Bandit & Lotto
05: Datenbanken - Neu/Ändern/Löschen
06: Datenbanken - Suchen/Gehen Zu
07: Gezielte Textausgabe
08: Fensterl'n mit dem Psion
09: Zeichnen mit Grips
Datenbanken - Suchen/Gehen Zu
"Eine Datenbank ohne Suchfunktion ist ein gar nutzloses Ding"!
    Wir wollen unsere Datenbank vom letzten Workshop mit einer entsprechenden Routine (und anderen mehr) aufpäppeln.
    Vieler Worte werden wir dabei nicht bedürfen, denn die Verfahrensweisen sind Ihnen ja inzwischen hinlänglich bekannt. Aber ohne deftige OPL Tips verlassen Sie auch diesen Workshop nicht.
    Der Quellcode enthält nur die neuen Elemente und zwei Änderungen an bereits bestehenden Prozeduren.
    Wenn Sie die neuen Routinen dem alten Quelltext einfach hinzufügen, sollten Sie das neue Gesamtprogramm sofort ins Laufen bringen.

Änderungen
Es geht um lediglich zwei Änderungen, die erforderlich werden, weil das Menü um zwei Einträge erweitert werden soll. Neben der Suchroutine werden wir nämlich noch das Unterprogramm "Gehe zu" schreiben.
    Zuerst korrigieren Sie in "Showmenu%:" die entsprechende mCARD-Zeile mit den im Programmlisting angeführten Begriffen und deren Tasten-Kürzel. Die Funktionsprinzipien haben wir bereits beschrieben.
    Damit "DoMenu:(k%)" auch auf diese Änderungen reagieren kann, müssen danach die beiden Buchstaben "s" (Suchen) und "g" (Gehe zu) deren Definition des Strings "menuops$" beigefügt werden.

Suchen
Unsere erste neue Routine heißt "wahls%:". Sie könnte wesentlich kürzer ausfallen, wenn man auf die "Komfortfunktionen" verzichten würde. Gemeint sind das Anhalten der Anzeige, wenn der Bildschirm voll ist, und Fehlermeldungen bei leerer Datenbank. Aber ein wenig benutzerfreundlich soll unser Programm schon sein. Das ist auch schlicht der Grund dafür, weshalb die Routine etliche IF... ELSE... ENDIF Abfragen enthält.
    Nehmen Sie den Quelltext zur Hand: Zuerst wird die Frage gestellt, ob die Datenbank überhaupt Daten enthält.
    Dies geschieht durch "IF COUNT", was sich wie ein unvollendet gebliebener Fragesatz anhört. Sie hätten wahrscheinlich so etwas wie "IF COUNT<>0" erwartet. OK. Reden wir darüber!

IF - trickreich genutzt
"IF" wird üblicherweise herangezogen, wenn es darum geht, einen Entscheid zu treffen, um danach alternative Programmteile auszuführen. Diese Entscheidung trifft "IF" zwar - aber erst aufgrund einer vorangegangenen Vergleichsoperation, die die Antwort in Form einer Zahl liefert. Beispiel:
IF (a% > b%) .. ELSE .. ENDIF
    Die intern vorgeschaltete Vergleichsoperation prüft, ob die Aussage (a%>b%) wahr oder falsch ist und liefert für die wahre Aussage eine "-1", ansonsten eine "0" als Ergebnis. "IF" zieht nun zu seiner Entscheidung nur noch diesen Zahlenwert heran. In der Praxis gelten bequemerweise alle von Null abweichenden Zahlenwerte (also nicht nur "-1") als logisches WAHR. Nur die Null erlaubt die Interpretation FALSCH.
    Das erspart uns letztendlich die Vergleichsoperation bei COUNT, wenn es um die Prüfung des Füllzustandes der Datenbank geht. So findet das schlichte "IF COUNT" schließlich seine Erklärung. (Zur Erinnerung: COUNT ist eine Funktion, die bei Aufruf die aktuelle Datensatzanzahl zurückgibt/enthält).
    Hat nun "COUNT" den Wert Null, enthält die Datenbank keine Datensätze. Unsere Suchroutine schreibt in diesem Falle die geeignete Fehlermeldung auf den Bildschirm.

Beschaffungsmaßnahme
Bewahrt die Datenbank mindestens einen Datensatz auf, ist auch die Benutzung der Suchfunktion möglich. Vor dem eigentlichen Suchvorgang hat die Routine aber erst einmal die Beschaffung des Suchbegriffes gestellt:
    Vorsorglich werden alte Werte aus den Stringvariablen "name$" und "tel$" gelöscht ("delkndva:"). Es folgt das Aufblenden der Eingabemaske für den Suchbegriff ("edi-Such%:(title$)"). Der Suchbegriff soll am Ende in der globalen Variablen "name$" landen. Die wird schon mal mit dem Vorgabewert "*" besetzt. Das ist das Wildcard-Zeichen für die Suche und Auflistung aller Datenbankinhalte. Durch diese Aktion wird lediglich die Faulheit des Nutzers unterstützt, der jetzt nur noch [Enter] drücken muß, um eine Listing aller Datensätze am Bildschirm zu bekommen.
    In diesem und in allen Fällen, in denen in der Eingabemaske die [Enter]-Taste betätigt wird, ist der Rückgabewert der Routine "13" (für [Enter]) und der in das Eingabefeld eingetragene Begriff wird in die Variable "name$" übernommen - so war es auch geplant. Abbruch der Eingaberoutine mit [Esc] liefert eine Null zurück, die Variable "name$" behält den Wert "*". Das hat aber überhaupt keine Folgen, da letztendlich die Suchroutine wegen der Null sowieso abgebrochen wird.
    Da als Rückgabewert das Ergebnis von DIALOG ohne weitere Auswertung benutzt wird, kann man hier im Quelltext wieder sparen, indem man "RETURN" und "DIALOG" direkt verknüpft. (Die andere Schreibweise wäre z.B.: d%=DIALOG:RETURN d%)

Finden
Zurückgekehrt in die Prozedur "wahls%", führt also ein Rückgabewert von Null zum sang- und klanglosen Abbrechen der Suche. Aber alle anderen Werte veranlassen den Suchvorgang mit dem in "name$" eingetragenen Begriff.
    Bevor es im Programmtext weitergeht, hier einige allgemeine Bemerkungen zu "FIND", dem Befehl, mit dessen Hilfe die Suche vorgenommen wird. Der sucht nach Aufruf unspezifisch in allen Feldern der Datenbank. Für Mini-Datenbanken wie unsere ist das sicherlich kein Handicap. Für gehobenere Anwendungen muß man sich als Programmierer mit dem Befehl "FINDFIELD" auseinandersetzen und auch wissen, daß es einige Fehler in der Implementierung gibt, d.h. der Befehl reagiert unter bestimmten Bedingungen anders oder nur eingeschränkt im Vergleich zu seinem eigentlich geplanten Anwendungszweck.
    "FIND" arbeitet ich bis zu ersten Fundstelle durch und macht den Datensatz mit dem gefundenen Suchbegriff zum aktuellen. Der Suchvorgang ist damit also erst einmal beendet. Bei größeren Datensammlungen ist aber mit mehreren Fundstellen zu rechnen. Also veranlaßt man die Wiederaufnahme der Suche, indem man den "Zeiger" auf den Folgedatensatz positioniert ("NEXT") und erneuert ein "FIND" veranlaßt. Das macht man solange, bis die Datenbank vollständig durchkämmt ist. Ob dieser Fall eingetreten ist, kann mit dem Befehl "EOF" (end of file) getestet werden. Wenn das Ende des Datenbankbestandes erreicht ist, liefert EOF den Wert "-1" (also WAHR) zurück, ansonsten Null (also FALSCH).
    Sie erkennen bestimmt spätestens jetzt, daß man den Suchvorgang sinnvollerweise in eine Schleife packt, die am ersten Datensatz startet und erst nach dem letzten Datensatz aufhört. Genau das haben wir getan. Vor der DU/UNTIL-Schleife wird der interne Zeiger noch auf den ersten Datensatz positioniert und ab geht's.

f%=FIND(name$)
Der in "name$" gespeicherte Suchbegriff wird dem Suchbefehl mitgegeben. Ist dieser gefunden, wird die zugehörige Datensatznummer der Integervariablen f% zugeordnet. In unserem Falle erfolgt dann die Ausgabe auf den Bildschirm, aber nur, wenn f% nicht Null ist. Läßt man hier die IF-Abfrage weg, funktioniert das Programm zwar immer noch richtig, gibt aber eine "unsauber" aussehende Zeile mit der Datensatznummer Null und leeren Feldern aus, wenn kein Eintrag zum Suchbegriff gefunden wurde.
    Nach der Bildschirmausgabe wird der Zähler n% um eins heraufgesetzt. Der Zählerstand wird sofort in der folgenden Zeile ausgewertet. Die Rechenoperation bewirkt, daß die Bildschirmausgabe nach der Ausgabe von 10 Zeilen angehalten wird. Erst ein Tastendruck löscht den Bildschirm, baut die Überschrift neu auf und erlaubt erneut die Darstellung von 10 Zeilen. Auf diese Art wird das Durchscrollen der Bildschirmdarstellung vermieden.
    Nach der Bildschirmausgabe wird der innere Zeiger der Datenbank mit "NEXT" um eine Position weitergesetzt. Durch den erneuten Schleifendurchlauf beginnt die Suchaktion ab der neuen Position von vorn, bis am Ende die Abbruchbedingung "EOF" erreicht ist.
    Wenn bei der Suche kein passender Eintrag gefunden wird, bleibt f% immer Null. Dadurch wird aber auch die Zählvariable n% nie erhöht und bleibt ebenfalls Null. Dieser Umstand wird nun hinter der Schleife benutzt, um die passende Meldung "... nicht gefunden" abzugeben. (Zur Erinnerung, wieso f% und n% hier den Start-Wert Null haben: Lokale Variablen werden bei jedem Aufruf der Prozedur, in der sie stehen, neu auf den Wert Null initialisiert)

FIND richtig benutzen
Üblicherweise sucht man nach bekannten Begriffen, darf sich dabei aber nicht verschreiben. Kennt man bei großen Datenbankumfang nicht mehr alle Einträge genau oder man sucht nach mehreren gemeinsamen Namensstämmen (wie: Alle Schmidts mit "tt" und "dt"), lassen sich Wildcards einsetzen. Das ist das Fragezeichen ("?"), das für einen einzelnen unbekannten Buchstaben steht und der Stern ("*"), der für eine Buchstabengruppe beliebiger Länge stehen kann.
    Da die Suche nicht auf bestimmte Felder begrenzt ist (Name oder Telefonnummer) , können Sie genausogut nach Telefonnummern suchen lassen. Beachten Sie bei eigenen OPL-Experimenten: Es handelt sich nicht um Zahlen, sondern immer um Strings - sowohl bei der Eingabe als auch bei der Suche!
    Groß- und Kleinschreibung wird durch "FIND" nicht berücksichtigt.

Gehe zu
Diese kleine Routine soll helfen, Datensätze gezielt anzusteuern, um Veränderungen vornehmen zu können.
    Dazu läßt man zuerst die Suchroutine laufen und erfährt dabei die Nummer des Datensatzes, den man weiterbearbeiten will. Das Aufrufen der Routine und das Eintragen der Datensatznummer in die entsprechende Suchmaske führen schließlich um Ziel.
    Die meisten Befehle aus den Routinen "wahlg%" und "ediGoto&:(title$)" können Sie mit dem hier erworbenen Wissen selber interpretieren.
    Auf einen Fallstrick wollen wir Sie allerdings noch aufmerksam machen: Beachten Sie, daß bei der Abfrage der Datensatznummer mit Long-Integer Zahlen gearbeitet wird und daher auch die Variable "d&" in "wahlg%"das Long-Integer-Format haben muß!
    Das Feld, das die Eingabe der Datensatznummer erlaubt, wird mit dem dLONG-Befehl nach folgendem Schema aufgebaut:
dLONG
eingabevariable&"text_links",min.wert&,max.wert&

Gratulation!
Sie haben nun eine Datenbank, mit der man sogar richtig arbeiten kann. Mit dem erworbenen Wissen sind Sie aber auch in der Lage, eine ganz persönliche Datenbank zu erstellen.
DB 2
PROC showmen%:
   ...
   mCARD "Kunden","Neu",%n,"Ändern",%a,"Suchen",%s,"Gehe zu",%g,"Löschen",%l
   ...
ENDP

PROC DoMenu:(k%)
   ...
   menuops$="enagls"
   ...
ENDP
DBall
PROC startup:
   global filenam$(130), basenam$(10), name$(20), tel$(20)
   FONT 268436068,1
   inifile:
   main:
endp

proc inifile:
   basenam$="\MEINE_DB"
   trap mkdir(basenam$)
   filenam$ = basenam$ + "\" + "KUNDEN.ODB"
   if not exist(filenam$)
      trap create filenam$, A, name$, tel$
   else
      trap open filenam$, A, name$, tel$
   endif
   showknd:
endp

proc main:
   global taste%, mod%
   do
      taste%=get : mod%=KMOD
      if taste%=290
         showmen%:
      elseif (mod% AND 4)
         taste%=taste% + 96
         DoMenu:(taste%)
      elseif (taste% AND $FF00)
         if taste%=259
            rollen:(taste%)
         elseif taste%=258
            rollen:(taste%)
         endif
      endif
   until 0
endp

proc showmen%:
   local keycode%
   minit
   mcard "Datei","Beenden",%e
   mCARD "Kunden","Neu",%n,"Ändern",%a,"Suchen",%s,"Gehe zu",%g,"Löschen",%l
   keycode% = menu
   domenu:(keycode%)
endp

proc domenu:(k%)
   local k$(1)
   local menuops$(10)
rem   menuops$="enal"
   menuops$="enagls"
   k$=chr$(k% and $FF)
   if loc(menuops$,k$)
      @%("wahl"+k$):
   endif
endp

proc wahln%:
   local erg%
   use a
   delkndva:
   erg% = ediknd%:("Kunden anlegen")
   if erg% = 0
      delkndva:
   elseif erg% = 13
      a.name$ = name$ : a.tel$ = tel$
      append : giprint "Hinzugefügt"
      showknd:
   endif
endp

proc wahla%:
   local erg%
   use a
   erg% = ediknd%:("Kundedaten ändern")
   if erg% = 0 or erg%=27
      delkndva:
   elseif erg% = 13
      a.name$ = name$ : a.tel$ = tel$
      update : giprint "Gändert"
      last : showknd:
   endif
endp

proc ediknd%:(title$)
   local erg%
   do
      dinit title$
      dedit name$,"Kundenname:"
      dedit tel$,"Telefonnummer:"
      dbuttons "Nicht speichern",27,"Speichern",13
      erg% = dialog
   until erg% = 0 or erg%=13
   return erg%
endp

proc wahll%:
   local erg%
   use a
   if count > 0
      dinit "Kunden löschen"
      dtext "","Wirklich löschen"
      dbuttons "Ja",%j,"Nein",%n
      erg% = dialog
      if erg%=%n
         return
      elseif erg% = %j
         erase : position 1 : showknd:
      endif
   endif
endp

proc wahle%:
   use a : close : stop
endp

proc delkndva:
   name$ = "" : tel$ = ""
endp

proc showknd:
   local c%
   cls : db2knd:
   print "K U N D E N D A T E N"
   print "-----------"
   print "Kundenname: "+chr$(9)+name$
   print "   Telefon: "+chr$(9)+tel$
   do
      print
      c% = c% + 1
   until c% = 9
   if count <> 0
      print "Datensatz ";pos;"/";count
   else
      print "Datensatz 0/0"
   endif
   print
   print "<-" + " Zurück/Vor " + "->" + " oder Menü-Taste"
   gborder 0
endp

proc db2knd:
   use a : name$ = a.name$ : tel$ = a.tel$
endp

proc rollen:(keycode%)
   if count > 1
      if taste% = 259
         if pos = 1
            giprint "1. Datensatz"
         else
            back : showknd:
         endif
      elseif taste% = 258
         if pos = count
            giprint "Letzter Datensatz"
         else
            next : showknd:
         endif
      endif
   endif
endp

PROC wahls%:
   REM Eintrag Suchen
   LOCAL f%, erg%, n%
   IF COUNT   REM es gibt Einträge!
      delkndva:
      IF ediSuch%:("Suche nach ..")
         FIRST
         CLS
         PRINT "Index-Name-Telefon:" : PRINT
         DO
            f%=FIND(name$)
            IF f%
               PRINT f%," - ";a.name$;" - ",a.tel$
               n%=n% + 1
               IF n%=10*(n%/10)
                  PRINT : PRINT "Taste für weiter!"
                  GET : CLS
                  PRINT "Index-Name-Telefon:" : PRINT
               ENDIF
            ENDIF
            NEXT
         UNTIL EOF
         IF n%=0
            PRINT " .. nichts gefunden!"
         ENDIF
         PRINT : PRINT "Suche beendet. Taste drücken."
         GET
      ELSE
         REM Abbruch der Suche ohne Meldung
      ENDIF
   ELSE
      CLS
      PRINT "Keine Daten! Taste."
      GET
   ENDIF
   FIRST
   showknd:
ENDP

PROC ediSuch%:(title$)
   name$="*"
   dINIT title$
   dEDIT name$,"Kundenname:"
   dBUTTONS "Abbruch",27,"Fertig",13
   RETURN DIALOG
ENDP

PROC wahlg%:
   REM Gehe zu (Datensatz)
   LOCAL f%, ds&, n%
   IF COUNT
      ds&=ediGoto&:("Gehe zu Datensatz")
      POSITION ds&
   ELSE
      CLS : PRINT "Kein Daten! Taste." : GET
   ENDIF
   showknd:
ENDP

PROC ediGoto&:(title$)
   REM Datensatznummer f. "wahlg%:" bereitstellen
   LOCAL eingabe&
   eingabe&=1
   dINIT title$
   dLONG eingabe&,"Datensatz-Nr.?",1,COUNT
   dBUTTONS "Fertig",13
   DIALOG
   RETURN eingabe&
ENDP


OPL am Psion von Rudolf Pöchacker (http://members.xoom.com/poechacker/)
Valid HTML 4.01! Copyright © 2002-2007 Mobile Times Business Club
Zurück zum Menü
Nach Oben