W programach, nawet najprostszych nakładkach potrzebne są okienka dialogowe. W LISP okienka dialogowe można tworzyć za pośrednictwem mechanizmów DCL. DCL pozwala na tworzenie jedynie bardzo prostych okienek. Możliwe jest użycie pól tekstowych, przycisków, list, opcji, suwaków i tekstów. Wszystkie są szczegółowo opisane w helpie, więc nie będę się tu skupiał nad dokładnym opisaniem każdego elementu okienka (czasem można się spotkać z określeniem, że są to "wycinki", ja może zostanę przy określeniu "elementy" albo "kontrolki"). Chciałbym opisać zasadę działania okienek, jak zacząć pracę z nimi, kilka interesujących możliwości, które nie są oczywiste przy pierwszym spotkaniu z DCL. W poszukiwaniu szczegółów zapraszam do lektury helpa lub dyskusji na forum.
Opinie na temat DCL są ogólnie podzielone. Wielu twórców nakładek ma złe zdanie na temat DCL, ja też się ku temu zdaniu przychylam. DCL ma wiele ograniczeń, które powodują, że jeśli mam do stworzenia nakładkę z okienkami, wybieram VBA.
Istnieją środowiska pozwalające na rozszerzenie możliwości DCL, jest to OpenDCL i ObjectDCL. Dają one o wiele większe możliwości, jak np.: Tworzenie okienek niemodalnych, Okienka podobne do okna właściwości, Dodatkowe konktolki jak np. zakładki, kalendarz, kontrolki obiektowe, podgląd bloków i inne. Ten kurs ma być jednak poświęcony podstawom LISP, a ja nie znam tych rozszerzeń na tyle, żeby móc doradzać którekolwiek z nich, więc nie będę się więcej wgłębiał w dodatkowe DCLe.
DCL wymaga dwóch plików. Konieczny jest plik tekstowy z rozszerzeniem DCL, zawierający definicję okienka oraz plik LSP zawierający całą obsługę okienka.
Najprostszy, przykład wygląda następująco:
Przykład nic nie robi, wciśnięcie przycisku spowoduje jedynie zamknięcie okna. Obsługą w LISP, zajmiemy się później. Możemy jednak na tym przykładzie zobaczyć podstawowe zasady tworzenia okienek DCL:
Okienka DCL mają układ tabelaryczny. Domyślnie, kolejne definicje kontrolek są wyświetlane w kolejnych wierszach. jeśli chcemy, żeby wyświetlały się w innym układzie, konieczne jest grupowanie ich w rzędy i kolumny, które mogą być zagnieżdżone.
Definicja każdego okienka musi zaczynać się właśnie od dialog. Nazwa poprzedzająca słowo kluczowe :dialog, identyfikuje jednoznacznie okienko. Chyba jedynym interesującym atrybutem, dla całego okienka jest label definiujący tekst wyświetlany w lewym górnym narożniku okienka, na niebieskim tle.
Dla całego okienka istotna jest też jego szerokość i wysokość. Jest ona automatycznie obliczana tak, żeby okienko obejmowało wszystkie elementy tworzące okienko.
Pole tekstowe to element, w którym wpisać możemy dowolny tekst.
Najbardziej interesujące atrybuty tego elementu to:
key - identyfikator elementu. W czasie pracy z okienkiem aby odczytać wartość z pola. musimy znać jego key
action - nazwa funkcji, jaka ma się wykonać, po zakończeniu edycji wartości, czyli po zaprzestaniu pisania w polu. Funkcja taka może np. sprawdzać poprawność wpisanych danych
value - to domyślna treść, jaka zostanie wyświetlona przy pierwszym uruchomieniu okna.
Dodatkowe własności to:
label - etykieta, jest to tekst, jaki wyświetlał się będzie z boku pola tekstowego, może służyć opisaniu znaczenia pola
mnemonic - jako wartość wpisujemy tutaj jedną literę. Kiedy okno będzie aktywne, po wciśnięciu tej litery na klawiaturze, "kursor" zostanie przeniesiony do tego elementu.
alignment - wyrównanie, określa jak dany element ma być wyrównany w pionie i poziomie.
width - szerokość jest automatycznie obliczana na podstawie szerokości innych elementów, chyba, że dodatkowo jest ustawiony atrybut fixed_width. Spisujemy tutaj liczbę znaków, jakie jednocześnie mają być wyświetlane
edit_width - określa ilość znaków, jakie mają być jednocześnie wyświetlane, określa szerokość elementu.
edit_limit - określa maksymalną ilość znaków, jakie mogą być wpisane w polu. (domyślne jest to 132), maksymalnie można wpisać 256.
is_enabled - określa czy element jest domyślne dostępny, Jeśli zostanie ustawiony na nil. element będzie domyślne zablokowany, taki szary, jakby wyłączony.
upper_only lower_only - definiuje, czy wpisywane mogą być jedynie małe/wielkie litery
password_char - maska hasła. Znak podany w tym atrybucie, będzie się wyświetlał zamiast liter, które użytkownik będzie wpisywał
Jeśli chcemy, aby możliwe było wpisanie większej ilości tekstu, wyświetlanego w więcej, niż jednej linii, konieczne jest użycie innej kontrolki: multi_edit_box
Najważniejsze atrybuty przycisku to:
label - etykieta, czyli tekst wyświetlany na przycisku
key - Identyfikator przycisku, bardzo przydatny, jeśli chcemy w czasie działania programu przypisać funkcję do przycisku, wynika z tego, że w czasie działania programu, temu samemu przyciskowi można przypisać różne działanie, w zależności od zaistniałych warunków. Przypisanie takie, może być wykonane jedynie przed uruchomieniem okienka, ale przy odrobinie wyobraźni i wiedzy, o której później, da się zrobić dobre narzędzie.
action - nazwa funkcji, jaka ma się wykonać po naciśnięciu przycisku. Możliwe jest nie przypisywanie funkcji do przycisku w pliku DCL, da się to zrobić w czasie działania programu.
Jest to odpowiednik combobox znanego z innych jezyków programowania, czyli lista, gdzie wyświetlana jest jedna linia, a obok niej strzałka skierowana w dół, po kliknięciu której rozwija się cała lista dostępnych wartości
czyli zwykła lista z wierszami i kolumnami. wszystkie "komórki", są jednego typu, czyli teksty. Dla tych, którzy szukają czegoś jak datagrid z C++ mam przykrą wiadomość: DCL nie daje takiej możliwości.
Obsługa list jest dosyć skomplikowana, wymaga pracy na globalnej liście wartości, może okaże się to jasne na przykładzie. Na razie warto jedynie zaznaczyć, że wartość odczytana z listy, to indeks wybranego element w liście. Nie mam możliwości odczytania wartości wybranej, więc trzeba mieć dostępną listę wszystkich wartości i pobierać z niej wartość o danym indeksie.
Listę wartości dostępnych w kontrolkach można zdefiniować już w pliku DCL, ustawiając atrybut list na wartość np.: "Czerwony\nZielony\nNiebieski"; Jednak z mojego doświadczenie wynika, że częściej zdarza się zawartość list wypełniać w czasie działania programu. Moim zdaniem daje to też większe możliwości.
Istnieje możliwość wyświetlania obrazków w oknach DCL. Mogą to obrazki w formacie SLD czyli slajdów, lub można w czasie ładowania programu "rysować" w przestrzeni obrazka przy użyciu wektorów. Może więcej o tym później. Najprostsza definicja obrazka musi zawierać atrybuty key, szerokość i wysokość. Zawartość będziemy rysować w czasie działania programu.
Bardzo zbliżony do tego elementu jest inny: image_button jak łatwo się domyślić jest to połączenie przycisku i obrazka.
Poznaliśmy już podstawowe pojęcia z zakresu okienek dialogowych. Istnieje kilka innych, o których nie wspominałem. są to teksty, "ozdobniki" w formie pustych odstępów, i coś o czym na pewno zapomniałem.
Ale może przejdźmy do konkretów.
Na potrzeby naszego kursu, przygotowałem taki mały przykład, na podstawie parametrów, długości, szerokości i wysokości, narysowany zostanie prostopadłościan. Nasze okienko będzie wyglądało jak widać na poniższym obrazku:
Układ naszego przykładu ma następującą postać:
Pliki naszych przykładów będą miały następującą postać:
Natomiast plik obsługujący nasz przykład ma postać:
Mam nadzieję, że po przedstawieniu podstawowych informacji o DCL struktura okienka jest jasna, mamy w wierszach i kolumnach typowe kontrolki, i wszystko powinno być jasne. Skupmy się wiec na obsłudze okienka. W powyższym listingu zdefiniowanych mamy kilka podstawowych funkcji, jak zamiana listy na variant (l2v), obsługa integracji z użytkownikiem (FullGetpoint) (FullGetDist). Nie dotyczą one bezpośrednio DCL i były już wcześniej opisywane, więc nie poświęcajmy im więcej czasu. Interesujące jest dla nas teraz jak uruchomić okienko.
Najważniejsze funkcje, służące obsłudze DCL to:(load_dialog) - Wczytuje plik definicji okien do przestrzenie ZWCADa. Nie są wyświetlane żadne okna, w pliku może być kilka definicji okien. Funkcja zwraca identyfikator okna, należy go zapamiętać w zmiennej, będzie on później przydatny. (new_dialog) - Tworzy nowe okno, Jest ono utworzone, można dokonać inicjalizacji, wartości w listach, rysować w obrazkach, przypisywać działania do przycisków. Dotychczas okno nie jest jeszcze wyświetlone na ekranie. (start_dialog) - teraz okno zostaje wyświetlone, a funkcja wykonuje się tak długo, jako okno jest aktywne. Dopiero, kiedy zostanie zamknięte, funkcja zwróci wartość. (done_dialog) - funkcja zamykająca okno, Przestaje ono być wyświetlane. (unload_dialog) - funkcja zwalnia uchwyt pliku definicji okna, jest to konieczne na koniec pracy z oknem(term_dialog) - zamyka wszystkie okna. Działa analogicznie jak done dialog, ale dotyczy wszystkich aktywnych okien(set_tile) - ustawia wartość kontrolki, np. tekst wpisany w edit_box, lub aktwny wybór w liście (get_tile) - pobiera wartość z kontrolki(action_tile) - ustawia działanie jaki ma być wykonane po zmianie wartości kontrolki, lub funkcję uaktywnianą przez wciśnięcie przycisku.
3. Ładownie pliku definicji okien. Jako parametr wywołania funkcji wpisujemy ścieżkę i nazwę pliku DCL. Ścieżkę można pominąć, jeśli plik jest w katalogu widocznym przez ZWCADa, czyli w katalogu ZWCADa, w katalogu wyszczególnionym w opcjach.
W linii 2 mamy warunek, mający sprawdzić, czy plik nie jest wcześniej wczytany. Jest to dobre rozwiązanie, jeśli mamy kilka okien w jednym pliku. W takim przypadku wczytujemy plik tylko raz.
8. new_dialog tworzy okno, Jeśli w pliku dcl nie ma zdefiniowanego okienka o zadanej nazwie lub nie dało się otworzyć pliku definicji, warunek na początku linii obsłuży ten wyjątek, program nie wyświetli błędu, a jedynie przejdzie do polecenia (exit) czyli zakończy działanie funkcji.
9. Po utworzeniu okienka, inicjalizujemy wartości w liście i podglądzie. W tym celu utworzyłem oddzielne funkcje, ich szczegółowym opisem zajmę się później.
Jeśli nasze okienko będzie się wyświetlało nie tylko raz, ale po ponownym otwarciu chcemy, żeby zapamiętywane zostały poprzednio wpisane wartości musimy te wartości wpisać, i teraz jest najlepszy czas, żeby to zrobić. Wartości przechowujemy w zmiennych globalnych, więc po ponownym uruchomieniu okienka po prostu wpisujemy odpowiednie wartości we właściwe pola.
13-15. Możemy przetłumaczyć jako: Jeśli zmienna np. Dwidth, jest typu liczbowego, to jego wartość w postaci tekstowej wpisujemy do kontrolki DTxt. Funkcja (set_tile) przyjmuje dwa parametry wywołania: Uchwyt kontrolki, czyli to co w definicji DCL jest atrybutem key, oraz wartość, która ma być wpisana, oba parametry muszą być typu tekstowego.
16-30. Funkcje obsługujące przyciski.
(action_tlie), przypisuje działanie do przycisku. Podobnie jak poprzednio, oba parametry muszą być typu tekstowego. Pierwszy parametr, to identyfikator przycisku, czyli key, drugi, to tekst - nazwa funkcji, która ma być wywołana w momencie kliknięcia przycisku.
(done_dialog) - wyłącza okno. To NIE jest tak, że po prostu przestaje ono być wyświetlane, ale gdzieś tam tkwi w pamięci. Po prostu jest wyłączane. Jest to o tyle problematyczne, jeśli chcemy, żeby po naciśnięciu przycisku użytkownik wskazał punkt, lub dwa. W takim przypadku musimy właśnie zapisać wszystkie wartości w zmiennych, wyłączyć całe okienko , a kiedy użytkownik zrobi co ma zrobić, musimy przywrócić wszystkie wartości i ponownie wyświetlić okienko. Funkcja (done_dialog) przyjmuje jeden opcjonalny parametr, jest to wartość liczbowa. I to właśnie ona zostanie zwrócona przez funkcję (start_dialog) Dzięki takiemu możemy w dalszej części programu rozpoznać "powód" zamknięcia okienka. Nasz program będzie się inaczej zachowywał, kiedy zostanie zamknięty, by użytkownik mógł podać jakąś wartość, punkt itp., a inaczej, jeśli ma po prostu zakończyć działanie po wykonaniu tego, do czego był przeznaczony.
Na przykładzie obsługi przycisku "OK", możemy zauważyć, że jednemu przyciskowi może być przypisana nie tylko jedna funkcja, ale cała ich sekwencja. A na dodatek może ona być warunkowa. Ważne, że funkcja, lub cała ich sekwencja musi być typu tekstowego, stąd właśnie bierze się w liniach 23-27 spora liczba znaków \. Sens takiego zachowania, jest taki, że teksty muszą być ograniczone na początku i końcu znakiem", natomiast ID kontrolek również muszą być ograniczone tymi samymi znakami, zdarzyło się więc że teksty są zagnieżdżone. Gdyby po prostu zostawić znaki " w każdym miejscu, gdzie być powinny, drugi cudzysłów zostałoby to zinterpretowany jako koniec tekstu, i wywołałoby to błąd. Znak \ powoduje, że " znak stojący za nim nie jest interpretowany jako znak końca tekstu, a po prostu znak bez żadnego znaczenia. Mam nadzieję, że wystarczająco to wyjaśniłem.
32 start_dialog Funkcja wyświetlająca okno. Po wyświetleniu okna, do niego zostanie przekazane sterowanie, kolejne linie nie zostaną wykonane, tak długo, jak okno będzie aktywne. Funkcja zwraca taką wartość, jaka jest parametrem funkcji done_dialog zamykającej okno. Może się też zdarzyć tak, że okno nie jest zamknięte przez funkcję done_dialog, a term_dialog. Wówczas start_dialog zwróci -1.
Wątpliwości może jeszcze budzić zasadność i sens zapętlenia wszystkiego w while. Chodzi o to, że bez tego, okienko wyświetliłoby się tylko raz. Po jego zamknięciu nie byłoby możliwości jego ponownego wyświetlenia, jedynie przez ponowne uruchomienie funkcji a wówczas stracilibyśmy wpisane już wartości. Zamknięcie okienka bywa jednak konieczne np.: do podania długości, lub wskazania punktu wstawienia całego elementu. Dzięki konstrukcji while okno będzie się wyświetlało za każdym razem, tak długo, aż start_dialog zwróci wartość mniejszą niż 0. A wartość jaka będzie zwrócona, zależy od parametru funkcji done_dialog. Czyli do każdego przycisku przypisujemy akcję (done_dialog ) z innym parametrem, ale np. większym niż 0. Tylko jednemu przyciskowi przypisujemy done_dialog 0 lub -1 i ten element, będzie na stałe zamykał okno, pozostałe będą zamykały okno tylko tymczasowo.
Poniżej funkcji start_dialog przez cond możemy obsłużyć wszystkie przypadki czyli dla każdego przypadku zamknięcia okna wpiszemy poszczególne procedury.
Mam nadzieję, że idea i sposób postępowania są jasne. Ewentualne pytania, wątpliwości proszę kierować na forum, postaram się odpowiedzieć.
(start_list) - rozpoczyna operacje na liście, Parametrem wywołania funkcji jest atrybut key listy, którą chcemy manipulować. (start_list "ID") lub (start_list "ID" 3) jest to tryb domyślny(start_list "ID" 1 4) zamienia piąty element w liście (start_list "ID" 2) (add_list) ustawia wartość w aktywnej liście. To, która lista jest aktywna, i czy wartość ma być zamieniona, czy dopisana, określane jest w poprzedniej funkcji - (start_list) (end_list) - kończy działanie na aktywnej liście.(get_tile), (set_tile) te dwie funkcje pozwalają w czasie działania programu odczytać i ustawić, który element z listy jest aktywny - zaznaczony. (get_tile) zwraca indeks zaznaczonego elementu - ale ważne jest, że w formie tekstu np. "3" oznacza czwarty element listy. Analogicznie aby ustawić, że aktywny jest trzeci element z listy musimy użyć (set_tile "ID" "2")
Jak widzimy powyżej, istnieje możliwość dopisywania do listy, nadpisywania listy, nie ma natomiast możliwości usuwania elementu z listy. Moim zdaniem należy usunąć element z listy, którą chcemy wyświetlać, a następnie jeszcze raz wyświetlić całą listę.
Kolejna sprawa, to listy wielokolumnowe. Jeśli chcemy np. w liście wypisać współrzędne punków. Kolejne współrzędne powinny być wyrównane w pionie czyli potrzebne są nam kolumny. Da się coś takiego zrobić, ale jest to jeszcze bardziej nieporęczne, niż całe pozostałe mechanizmy list.
Jedyne co możemy wpisywać do kolumn, to teksty a na dodatek nie możemy wpisywać w określonych komórkach, a jedynie całe wiersze. Aby móc podzielić wiersz na kolumny, musimy podzielić nasz tekst znakiem \t w ogólnej filozofii LISP \t oznacza tabulator i to jest jedyny sposób na wyrównanie kolumn tabeli. Nie jest możliwe zaznaczanie jednej wybranej komórki w liście, jedyne co możemy zrobić to zaznaczyć cały wiersz, więc te kolumny, to takie niby są, ale są bardzo niefunkcjonalne.
Przy okazji list, warto zauważyć jeszcze, że wszystkie te zasady działania dotyczą list_box i popup_list
(start_image) - ustawia aktywny element. Określa przestrzeń w któej będziemy rysować(fill_image) - ustawia kolor wypełnienia. Dzięki temu tło może nie być czarne, tylko mieć dowolny inny kolor.(slide_image) - powoduje wyświetlenie slajdu w kontrolce.(vector_image) - rysuje wektor w przestrzeni obrazka. (end_image) - kończy działanie na aktywnym elemencie okna(dimx_tile) - zwraca szerokość elementu okna(dimy_tile) - zwraca wysokość elementu okna
Najczęściej jest to wykorzystywane do wyświetlania slajdów. Aby obrazek był dobrze dopasowany do dostępnej przestrzeni kontrolki, potrzebne są nam szerokość i wysokość kontrolki. Te informacje możemy odczytać właśnie przez funkcje dimx_tile i dimy_tile, Następnie użyć ich należy w funkcjach slide_image do określenia ja jakiej przestrzeni slajd ma być wyświetlony. Nigdy tego nie robiłem, ale myślę, że może być też możliwe tworzenie kolorowych okien, czyli jedno image_box a w nim kilka slajdów, a kto wiem może przez fill_image da się zrobić tak, że tło nie jest jednolite, a różnokolorowe.
Często istnieje konieczność ograniczenia wyobraźni użytkowników naszej nakładki. DCL daje podstawowe możliwości w tym zakresie. Możemy zdefiniować funkcję, która pobiera wartość z kontrolki, która wywołała tą funkcję, sprawdzić, czy dane są poprawne, czy są odpowiedniego typu,i mieszczą się w zadanym zakresie i ewentualnie je poprawić.
Używamy tu dwu istotnych>zmiennych
$value - wartość z aktywnej kontrolki
$key - identyfikator aktywnej kontrolki
W przedstawionym powyżej przypadku, pobierana jest wartość kontrolki, sprawdzane jest, czy jest ona większa od 0, jeśli nie, czyli jest mniejsza, lub wartość nie jest liczbą, to do kontrolki zostaje wpisana pusta wartość. Dzięki takiej funkcji, można jedną funkcją obsłużyć kontrolę stanu wielu elementów okna.
Istnieje możliwość ustawienia trybu w jakim wyświetlona będzie kontrolka. Dostępne są następujące tryby:
0. Odblokowanie - jeśli element był zablokowany, aktywując ten tryb, ustawimy, że element staje się dostępny
1. Zablokowanie - Powoduje, że element będzie zablokowany, taki wyszarzony i nic się nie da tam zmienić. Przydatne jest to, kiedy w zależności od wybrania jednej opcji, inne stają się nie istotne, nie mające wpływu. Żeby nie zaciemniać, lepiej jest je zablokować
Obrazowo chodzi o to, co zaznaczone na czerwono:
2. Aktywuje kontrolkę, czyli powoduje jakby przeniesienie kursora do określonej kontrolki.
3. Zaznacza zawartość kontrolki
4. Zmienia podświetlenie obrazka.
Copyright © 2000—2010 by
Usługi Informatyczne SZANSA - Gabriela Ciszyńska-Matuszek.
Autoryzowany Dystrybutor programu ZWCAD w Polsce od 2005 roku.
Wszelkie prawa zastrzeżone.