Мінімальне DB/GUI додаток на PicoLisp

Кілька тижнів тому моя дружина попросила невеликий додаток — онлайнову базу даних для адрес та контактних даних членів сім'ї, родичів, друзів і так далі.

Як правило, в PicoLisp база даних містить об'єкти різних класів. Для їх обробки в GUI повинна бути можливість для пошуку, створення і видалення об'єктів, редагування їхніх властивостей.

Типове PicoLisp-додаток реалізує наступні функції:
  • Пункт меню для кожного типу об'єкта або функції
  • Діалогове вікно пошуку, яке з'являється при виборі пункту меню
  • Пошук по потрібним критеріям у діалоговому вікні
  • Потім клацніть на знайдений об'єкт або на кнопку "Новий" для створення нового об'єкта
  • З'явиться форма, де ви можете редагувати цей об'єкт
  • Форма має кнопку "Видалити", щоб видалити цей об'єкт
Але у випадку простої бази даних адрес це зайве. Є тільки один клас об'єктів.

На щастя, є простий спосіб. По-перше, немає необхідності для меню. Ми можемо перейти безпосередньо до адрес. Все інше можна обробити одним компонентом GUI,
+QueryChart
.

Повний лістинг в кінці статті.

Модель данихВизначимо клас "Person" (для цілей цієї статті злегка скороченій формі) як:
(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
При необхідності клас можна легко розширити.

Замість окремих властивостей вулиці, індексу, міста і т. д. у нас є одна властивість у вільному форматі для повної адреси. Ми визначимо два неуникальных індексу, один ім'я користувача і один для адреси. Індекс "name" підтримує нечіткий пошук (з використанням алгоритму Soundex для схожих імен).

Опції графічного інтерфейсуУ нас є тільки одна GUI-функція під назвою
work
. Вона починається зі стандартних функцій
app
(= встановити сесію),
action
(= обробка подій форми) і
html
(= генерувати HTML-сторінки), що типово для кожного додатка PicoLisp. Необов'язкова функція
<ping>
використовує JavaScript для створення події keep-alive.
(de work ()
(app)
(action
(html 0 Ttl "@lib.css" NIL
(<ping> 2)
Потім, в якості запобіжного елементарної безпеки, вона показує поле для введення пароля в першій формі, з жорстко зашитим паролем "mypass".
(ifn *Login
(form NIL
(gui 'pw '(+PwField) 20 ,"Password")
(gui '(+Button) ,"login"
'(ifn (= "mypass" (val> (: home pw)))
(error ,"Permission denied")
(on *Login)
(url "!work") ) ) )
(Реальне додаток використовує повноцінну автентифікацію користувача/пароля (за допомогою библотеки «lib/adm.l»). Ми опустили її тут тільки для стислості)

У будь-якому випадку вона використовує глобальну змінну *Login, яка встановлюється натисканням кнопки «login» у разі збігу пароля. В цьому випадку відображається друга, головна форма.
(form NIL
(<grid> "--."
"Name" (gui 'nm '(+DbHint +TextField) '(nm +Prs) 20)
(searchButton '(init> (: home query)))
"Address" (gui 'adr '(+DbHint +TextField) '(adr +Prs) 20)
(resetButton '(nm adr query)) )
Вона відображає два пошукових поля «Ім'я» і «Адреса», і дві кнопки «Пошук» і «Скидання». Поля пошуку використовують префікс-клас +DbHint для відображення списку, що випадає з відповідними іменами, вже наявними в БД. Кнопка «Скидання» очищає всі поля.

Тепер перейдемо до "серцю" GUI цього додатка. Ми використовуємо клас
+QueryChart
для пошуку записів, так і для їх створення та редагування.

+QueryChart
використовується у всіх діалогах пошуку. Він використовує Pilog-запит для пошуку по заданому набору критеріїв, і відображає можливо необмежену кількість результатів, поки є відповідні елементи, і користувач продовжує натискати кнопки прокрутки.
(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) ) ) )
Перший аргумент (12) дає початкову кількість збігів для заповнення таблиці. Другий аргумент — Pilog-запит, який використовує значення пошукових полів «Ім'я» та «Адреса» для нечіткого і часткового пошуку. Дивіться http://software-lab.de/doc/select.html для детальнішої інформації.

Потім слідують три стандартних аргументу для класу
+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)) ) ) ) )
а саме: кількість стовпців (6) і функції
put
та
get
.

Клас
+Chart
викликає ці функції всякий раз, коли щось відбувається в GUI. put-функція перетворює логічне зміст рядка таблиці (тут адресу об'єкта) фізична відображення імені, адреси email-а і т. д.:
'((This) (list (: nm) (: adr) (: em) (: tel) (: dob)))
Аргумент
This
для put-функції — об'єкт, і він розгортається в список значень рядка таблиці.

get
-функція виконує зворотну дію, транслюючи значення в рядку властивості об'єкта. Вона приймає в
L
список значень з GUI (рядки, числа, дати і т. п., введені користувачем), а в
D
— адреса об'єкта в БД.
'((L D)
Потім вона перевіряє, у вираженні
cond
, чи існує об'єкт
D
.Якщо так, вона зберігає значення
L
у властивостях відповідного об'єкта, оновлюючи таким чином БД за необхідності:
(D
(mapc
'((K V) (put!> D K V))
'(nm adr em tel dob)
L )
D )
Якщо об'єкт не існує, але перша колонка таблиці містить ім'я (яке користувач тільки що ввів), то створюється новий об'єкт БД з цим ім'ям:
((car L)
(new! '(+Prs) 'nm (car L)) ) ) ) )
От і все! Це вся логіка, необхідна для створення і редагування записів.

+Chart
або
+QueryChart
— внутрішній об'єкт, що реалізує логіку цього графічного інтерфейсу.Тепер нам потрібні фізичні компоненти для взаємодії з користувачем.Помістимо в таблицю
(<table> NIL (choTtl "Entries" '+Prs)
з відповідними заголовками
(quote
(NIL "Name")
(NIL "Address")
(NIL "E-Mail")
(NIL "Telephone")
(NIL "Date of birth") )
слідом ідуть 12 рядків з полями тексту, email і телефону
(do 12
(<row> 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
# © 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
(<ping> 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
(<grid> "--."
"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)) ) ) ) )
(<table> NIL (choTtl "Entries" '+Prs)
(quote
(NIL "Name")
(NIL "Address")
(NIL "E-Mail")
(NIL "Telephone")
(NIL "Date of birth") )
(do 12
(<row> 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


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.