Minimale DB / GUI-Anwendung auf PicoLisp

Ursprünglicher Autor: Alexander Burger
  • Übersetzung
  • Tutorial
Von einem Übersetzer: Wir gleichen
weiterhin den Mangel an Informationen auf Russisch über den interessantesten Lisp-Dialekt aus. Vorheriger Artikel: Entwicklung von Webanwendungen in PicoLisp Project Homepage
: http://picolisp.com
Vor ein paar Wochen bat meine Frau um einen kleinen Antrag - eine Online-Datenbank mit Adressen und Kontaktdaten von Familienmitgliedern, Verwandten, Freunden usw.

In der Regel enthält eine Datenbank in PicoLisp Objekte verschiedener Klassen. Um sie in der GUI zu verarbeiten, sollten Objekte gesucht, erstellt und gelöscht und ihre Eigenschaften bearbeitet werden können.

Eine typische PicoLisp-Anwendung implementiert die folgenden Funktionen:
  • Menüpunkt für jede Art von Objekt oder Funktion
  • Das Suchdialogfeld, das angezeigt wird, wenn Sie ein Menüelement auswählen
  • Suchen Sie im Dialogfeld nach Kriterien
  • Klicken Sie anschließend auf das gefundene Objekt oder auf die Schaltfläche "Neu", um ein neues Objekt anzulegen
  • Ein Formular wird angezeigt, in dem Sie dieses Objekt bearbeiten können.
  • Das Formular verfügt über eine Schaltfläche zum Löschen, um dieses Objekt zu entfernen.

Bei einer einfachen Adressdatenbank ist dies jedoch nicht erforderlich. Es gibt nur eine Klasse von Objekten. Zum Glück gibt es einen einfachen Weg. Erstens ist kein Menü erforderlich. Wir können direkt zu den Adressen gehen. Alles andere kann mit einem GUI - Komponente behandelt werden +QueryChart.

Vollständige Auflistung am Ende des Artikels.

Datenmodell

Definieren Sie die Klasse "Person" (im Sinne dieses Artikels leicht abgekürzt) als:
   (class +Prs +Entity)
   (rel nm (+Sn +IdxFold +String))        # Name
   (rel adr (+IdxFold +String))           # Address
   (rel em (+String))                     # E-Mail
   (rel tel (+String))                    # Telephone
   (rel dob (+Date))                      # Date of birth
Bei Bedarf kann die Klasse problemlos erweitert werden.

Anstelle von Einzelobjekten für Straße, PLZ, Ort usw. Wir haben eine Eigenschaft im freien Format für die vollständige Adresse. Wir definieren zwei nicht eindeutige Indizes, einen für den Benutzernamen und einen für die Adresse. Der "Name" -Index unterstützt Fuzzy-Suchen (mit dem Soundex-Algorithmus für ähnliche Namen).

GUI-Funktionen

Wir haben nur eine GUI-Funktion aufgerufen work. Es beginnt mit Standardfunktionen app(= Sitzung einrichten), action(= Ereignisformular verarbeiten) und html(= HTML-Seiten generieren), die für jede PicoLisp-Anwendung typisch sind. Eine optionale Funktion verwendet JavaScript, um ein Keep-Alive-Ereignis zu erstellen.
   (de work ()
      (app)
      (action
         (html 0 Ttl "@lib.css" NIL
            ( 2)
Als elementare Sicherheitsmaßnahme wird dann das Kennwortfeld in der ersten Form mit dem fest verdrahteten Kennwort "mypass" angezeigt.
            (ifn *Login
               (form NIL
                  (gui 'pw '(+PwField) 20 ,"Password")
                  (gui '(+Button) ,"login"
                     '(ifn (= "mypass" (val> (: home pw)))
                        (error ,"Permission denied")
                        (on *Login)
                        (url "!work") ) ) )
(Die reale Anwendung verwendet die vollständige Benutzer- / Kennwortauthentifizierung (unter Verwendung der Bibliothek lib / adm.l). Wir haben sie hier nur aus Gründen der Kürze weggelassen.)

In jedem Fall wird die globale Variable * Login verwendet, die durch Drücken der Anmeldetaste festgelegt wird Passwort stimmt überein. In diesem Fall wird das zweite Hauptformular angezeigt.
               (form NIL
                  ( "--."
                     "Name" (gui 'nm '(+DbHint +TextField) '(nm +Prs) 20)
                     (searchButton '(init> (: home query)))
                     "Address" (gui 'adr '(+DbHint +TextField) '(adr +Prs) 20)
                     (resetButton '(nm adr query)) )
Es zeigt zwei Suchfelder, "Name" und "Adresse", und zwei Schaltflächen "Suchen" und "Zurücksetzen". Suchfelder verwenden die Präfixklasse + DbHint, um eine Dropdown-Liste mit übereinstimmenden Namen anzuzeigen, die bereits in der Datenbank vorhanden sind. Die Schaltfläche Zurücksetzen löscht alle Felder.

Kommen wir nun zum "Herzen" der Benutzeroberfläche dieser Anwendung. Wir verwenden die Klasse +QueryChart, um nach Datensätzen zu suchen sowie diese zu erstellen und zu bearbeiten.

+QueryChartwird in allen Suchdialogen verwendet. Es verwendet eine Pilog-Abfrage, um nach einem bestimmten Satz von Kriterien zu suchen, und zeigt eine unbegrenzte Anzahl von Ergebnissen an, sofern geeignete Elemente vorhanden sind, und der Benutzer drückt weiterhin die Bildlauftasten.
                  (gui 'query '(+QueryChart) 12
                     '(goal
                        (quote
                           @Nm (val> (: home nm))
                           @Adr (val> (: home adr))
                           (select (@@)
                              ((nm +Prs @Nm) (adr +Prs @Adr))
                              (tolr @Nm @@ nm)
                              (part @Adr @@ adr) ) ) )
Das erste Argument (hier 12) gibt die anfängliche Anzahl der Übereinstimmungen an, mit denen die Tabelle gefüllt werden soll. Das zweite Argument ist eine Pilog-Anfrage, die die Werte der Suchfelder "Name" und "Adresse" für eine unscharfe und teilweise Suche verwendet. Weitere Informationen finden Sie unter http://software-lab.de/doc/select.html .

Dann folgen drei Standardargumente für die Klasse.+Chart
                     6
                     '((This) (list (: nm) (: adr) (: em) (: tel) (: dob)))
                     '((L D)
                        (cond
                           (D
                              (mapc
                                 '((K V) (put!> D K V))
                                 '(nm adr em tel dob)
                                 L )
                              D )
                           ((car L)
                              (new! '(+Prs) 'nm (car L)) ) ) ) )
nämlich: die Anzahl der Spalten (hier 6) und die Funktion putund get.

Die Klasse +Chartruft diese Funktionen auf, wenn in der GUI etwas passiert. Die Put-Funktion konvertiert den logischen Inhalt der Tabellenzeile (hier die Adresse des Objekts) in die physikalische Anzeige von Name, Adresse, E-Mail usw .:
                     '((This) (list (: nm) (: adr) (: em) (: tel) (: dob)))
Das Argument Thisfür die put-Funktion ist ein Objekt und wird zu einer Werteliste für die Tabellenzeile erweitert.

get-function führt die entgegengesetzte Aktion aus und übersetzt die Werte in der Zeichenfolge in die Eigenschaften des Objekts. Es nimmt in die LListe der Werte aus der GUI (Zeichenfolgen, Zahlen, Daten usw. vom Benutzer eingegeben) und in D- die Adresse des Objekts in der Datenbank.
                     '((L D)
Anschließend wird geprüft, condob das Objekt im Ausdruck vorhanden ist D. Wenn ja, werden die Werte aus Lden Eigenschaften des entsprechenden Objekts gespeichert und die Datenbank bei Bedarf aktualisiert:
                           (D
                              (mapc
                                 '((K V) (put!> D K V))
                                 '(nm adr em tel dob)
                                 L )
                              D )
Wenn das Objekt nicht vorhanden ist, aber die erste Spalte der Tabelle den Namen enthält (den der Benutzer gerade eingegeben hat), wird ein neues Objekt mit diesem Namen in der Datenbank erstellt:
                           ((car L)
                              (new! '(+Prs) 'nm (car L)) ) ) ) )
Das ist alles! Dies ist die gesamte Logik, die zum Erstellen und Bearbeiten von Datensätzen erforderlich ist.

+Chartoder +QueryChart- ein internes Objekt, das die Logik dieser grafischen Oberfläche implementiert. Jetzt benötigen wir physische Komponenten, um mit dem Benutzer zu interagieren. Fügen Sie sie in eine Tabelle ein
                  ( NIL (choTtl "Entries" '+Prs)
с соответствующими заголовками
                     (quote
                        (NIL "Name")
                        (NIL "Address")
                        (NIL "E-Mail")
                        (NIL "Telephone")
                        (NIL "Date of birth") )
следом идут 12 строк с полями текста, email и телефона
                     (do 12
                        ( NIL
                           (gui 1 '(+TextField) 30)
                           (gui 2 '(+TextField) 40)
                           (gui 3 '(+MailField) 20)
                           (gui 4 '(+TelField) 15)
                           (gui 5 '(+DateField) 10)
                           (gui 6 '(+DelRowButton)
                              '(lose!> (curr))
                              '(text "Delete Entry @1?" (curr 'nm)) ) ) ) )
Обратите внимание на кнопку +DelRowButton в последней колонке.Она может быть использована для удаления записи из БД. Она вызывает диалог с подтверждением, действительно ли пользователь хочет удалить запись. Впрочем, при удалении нескольких строк, она не будет запрашивать пользователя в следующий раз.

И в самом конце отображаются четыре стандартные кнопки прокрутки
                  (scroll 12) ) ) ) ) )
Они позволяют построчно и постранично прокручивать содержимое таблицы.

Инициализация и запуск

По соглашению, PicoLisp-приложение предоставляет две функции, main и go.Функция main должна инициализировать рабочее окружение, а go должна запускать цикл обработки событий GUI.
(de main ()
   (locale "UK")
   (pool "adr.db") )
(de go ()
   (server 8080 "!work") )
locale главным образом нужен для правильной обработки поля +TelField с номерами телефонов. Вы можете предоставить свои настройки локализации в каталоге loc/.

Если вы скопируете код ниже в файл "minDbGui.l" или скачаете с http://software-lab.de/minDbGui.l, можете запустить его таким способом:
   $ pil minDbGui.l -main -go -wait
либо в режиме отладки:
   $ pil minDbGui.l -main -go +


Исходный код программы
   # 11jan15abu
   # (c) Software Lab. Alexander Burger
   (allowed ()
      "!work" "@lib.css" )
   (load "@lib/http.l" "@lib/xhtml.l" "@lib/form.l")
   (class +Prs +Entity)
   (rel nm (+Sn +IdxFold +String))        # Name
   (rel adr (+IdxFold +String))           # Address
   (rel em (+String))                     # E-Mail
   (rel tel (+String))                    # Telephone
   (rel dob (+Date))                      # Date of birth
   (de work ()
      (app)
      (action
         (html 0 Ttl "@lib.css" NIL
            ( 2)
            (ifn *Login
               (form NIL
                  (gui 'pw '(+PwField) 20 ,"Password")
                  (gui '(+Button) ,"login"
                     '(ifn (= "mypass" (val> (: home pw)))
                        (error ,"Permission denied")
                        (on *Login)
                        (url "!work") ) ) )
               (form NIL
                  ( "--."
                     "Name" (gui 'nm '(+DbHint +TextField) '(nm +Prs) 20)
                     (searchButton '(init> (: home query)))
                     "Address" (gui 'adr '(+DbHint +TextField) '(adr +Prs) 20)
                     (resetButton '(nm adr query)) )
                  (gui 'query '(+QueryChart) 12
                     '(goal
                        (quote
                           @Nm (val> (: home nm))
                           @Adr (val> (: home adr))
                           (select (@@)
                              ((nm +Prs @Nm) (adr +Prs @Adr))
                              (tolr @Nm @@ nm)
                              (part @Adr @@ adr) ) ) )
                     6
                     '((This) (list (: nm) (: adr) (: em) (: tel) (: dob)))
                     '((L D)
                        (cond
                           (D
                              (mapc
                                 '((K V) (put!> D K V))
                                 '(nm adr em tel dob)
                                 L )
                              D )
                           ((car L)
                              (new! '(+Prs) 'nm (car L)) ) ) ) )
                  (
NIL (choTtl "Entries" '+Prs) (quote (NIL "Name") (NIL "Address") (NIL "E-Mail") (NIL "Telephone") (NIL "Date of birth") ) (do 12 ( NIL (gui 1 '(+TextField) 30) (gui 2 '(+TextField) 40) (gui 3 '(+MailField) 20) (gui 4 '(+TelField) 15) (gui 5 '(+DateField) 10) (gui 6 '(+DelRowButton) '(lose!> (curr)) '(text "Delete Entry @1?" (curr 'nm)) ) ) ) ) (scroll 12) ) ) ) ) ) (de main () (locale "UK") (pool "adr.db") ) (de go () (server 8080 "!work") ) # vi:et:ts=3:sw=3

Jetzt auch beliebt: