AutoCAD... AutoLISP... VisualLISP...

  [37] image_button inaczej

index  

  Niniejszy tekst napisałem ładnych parę lat temu, jednak wydaje się że może być również teraz przydatny. Pomimo tego że język definicji okien dialogowych DCL jest już dość stary, ma jednak niezaprzeczalne zalety: Jest krótki i zwięzły, nie wymaga żadnych innych plików, gwarantuje działanie we wszystkich wersjach AutoCAD-a, definicje okien zajmują bardzo niewiele miejsca, i można je kompilować w plikach vlx. Tutaj omawiam "trójwymiarowe" przyciski w DCL-u.

  Standardowe wycinki okna dialogowego typu image i image_button pozwalają na przedstawianie grafiki (slajdu AutoCAD-a) w oknie dialogowym. Wycinek image_button pełni jednocześnie role przycisku, pozwalającego na sterowanie programem. Niestety wygląd tych wycinkow (i okien dialogowych je wykorzystujących) pozostawia wiele do życzenia. Uważam ze płaski obraz wyświetlany (najcześciej) na czarnym tle (choć mieści sie w kanonie AutoCAD-a), nie zawsze odpowiada funkcjonalności okna dialogowego. Dotyczy to szczegłlnie wycinka image_button (przycisku obrazkowego). W wielu przypadkach zastosowanie takich przycisków, powoduje ze okno dialogowe wygląda co najmniej archaicznie (jak w starym dobrym DOS-ie ;)). Z tego powodu narodził sie pomysł takiego wykorzystania image_button, aby symulowal on wypukły przycisk graficzny. Normalnie przycisk jest "wypukły", po wybraniu urządzeniem wskazującym, zmienia wygląd na "wciśniety". Oczywiście przy zastosowaniu jezyka AutoLISP do obsługi, i języka DCL do definicji okna. Poniżej przykłady moich dwóch okien dialogowych, wykorzystujących opisane tutaj procedury:
Okno "Otwory" jest rodzajem panelu sterowania. Wybranie jednego z czterech przyciskow (ikon) powoduje zamkniecie okna i wykonywanie jednej z czterech cześci programu. Ikony pełnią role przycisków zamykających - standardowo żaden z nich nie jest w danej chwili wybrany.
W drugim oknie - "Znaki chropowatości" - ikony tworzą grupe sekwencyją. Jeden (i tylko jeden) z nich jest zawsze wybrany ("wciśnięty") - wskazanie innego powoduje anulownie poprzedniego (staje sie "wypukły") i "wciśniecie" wybranego:
Działanie podobne jest do reakcji wycinkow typu radio_button_row lub radio_button_column (tutaj boxed_radio_row "Parametr:". Wydaje sie ze oba prezentowane okna, w porównaniu z podobnymi funkcjonalnie, a wykorzystującymi płaski slajd są bardziej atrakcyjne i zrozumiale dla użytkownika.

»  Jak to zrobić?

W pliku dcl zdefiniujemy własny wycinek przycisku. Wykorzystamy standardowy wycinek o nazwie image_button. Określimy proporcje wycinka, oraz kolor - bedzie to kolor okna dialogowego:
dcl_icon_button : image_button {
  aspect_ratio = 0.75;
  color = dialog_background;     
}
Następnie wykorzystamy ten wycinek w definicji naszego okna testowego, ktore składać się będzie z rzędu trzech takich przyciskow, przycisku zamykającego OK, oraz wycinka errtile. Definicja okna dialogowego wyglada tak:
dcl_1 : dialog { label = "Test 1";
  : row {
    : dcl_icon_button  {width = 15 ; key= "b_1" ;}
    : dcl_icon_button  {width = 15 ; key= "b_2" ;}
    : dcl_icon_button  {width = 15 ; key= "b_3" ;}
   }
  ok_only ;
  errtile ;
 }
Teraz utworzymy plik o nazwie dcl.lsp. Bedzie to plik naszego programu testującego. Ponizej definicja polecenia TEST1 służącego do wyświetlenia wcześniej zdefiniowanego okna dialogowego. Po załadowaniu pliku i wywołaniu polecenia powinno ukazać się okno dialogowe przedstawione ponizej:
(defun C:TEST1 (/ Dcl)
  (setq Dcl 
    (load_dialog "dcl")
  )
  (new_dialog "dcl_1" Dcl)
  (start_dialog)
  (unload_dialog Dcl)
)
Po załadowaniu pliku i wywołaniu polecenia powinno ukazać się okno dialogowe przedstawione ponizej:
Jak widać są to zwyczajne płaskie wycinki typu image_button. Teraz musimy zająć się ich "uplastycznieniem" czyli stworzeniem pozorów ich wypukłości. Sprowadzać się to będzie do stworzenia "ramki" wokół przycisku piktogramowego.
Jedna linia pozioma i jedna pionowa tej ramki bedzie symulować odbicie światła padającego na przycisk, a dwie pozostale jego cień. Wykorzystamy do tego celu funkcje o nazwie vector_image slużącą do rysowania wektora w wycinku typu image i image_button. Przycisk będzie wyglądał jakby był wypukły, a zamieniając kolory uzyskamy efekt przycisku wklęsłego. Ponieważ mechamizm ten należy zastosowac indywidualnie do każdego wycinka, zdefiniujemy osobną funkcję. Posiadać ona będzie dwa argumenty. Pierwszy z nich (MOD) może mieć wartość 0 lub 1. Wartośc 0 oznacza przycisk wypukły (niewybrany) , a 1 wklęsły (wybrany). Argument KEY jest nazwą wycinka. Funkcja przedstawiona jest ponizej:
(defun jk:DLG-icon_On_Off (MOD KEY / X Y Init)
  (setq Init '((0 . 7)(1 . -16)))
  (start_image KEY)  
  (setq X (dimx_tile KEY)  Y (dimy_tile KEY))
  (vector_image 2 2 2 (- Y 2) (cdr (assoc MOD Init)))
  (vector_image 2 2 (- X 2) 2 (cdr (assoc MOD Init)))
  (vector_image (- X 2) 2 (- X 2)(- Y 2) 
    (cdr (assoc (abs (1- MOD)) Init))
  )
  (vector_image 2 (- Y 2)(- X 2)(- Y 2)
    (cdr (assoc (abs (1- MOD)) Init))
  )
  (end_image)    
)
Następnym krokiem będzie zdefiniowanie funkcji przypisanej do image_button. Będzie ona sekwencyjnie wywoływała poprzednio zbudowaną funkcje - działanie jej bedzie tworzyło złudzenie "wciśnięcia" wybranego image_button. Pozostałe przyciski bedą przybierały automatycznie wygląd "wypukły" - czyli niewybrany. Dodam ze jest to jednen ze sposobow - inny polegałby na kontrolowaniu poprzednio wybranego przycisku i tylko on byłby aktalizowany. Funkcja posiada dwa agumenty. Pierwszy to lista wszystkich image_button a drugi to nazwa wybieranego aktualnie. W skrocie: wybranie jednego z trzech image_button powoduje "uwypuklenie" wszystkich i "wciśniecie" wybranego. Funkcja wyglada tak:
(defun Btn_test1 (LstBtns ActiveBtn)
  (foreach X LstBtns
    (jk:Dlg-icon_On_Off 0 X)
  )
  (jk:Dlg-icon_On_Off 1 ActiveBtn)
  (set_tile "error" ActiveBtn)
)
Zmodyfikujemy teraz polecenie TEST1. Kolorem czerwonym zaznaczony są nowy kod programu. Utworzona jest lista wycinkow - BtnList, oraz wywołanie funkcji Btn_test1 powodujące wybranie pierwszego image_button. Kolejne (dla kazdego image_button) wywołania funkcji action_tile powodują zmiane wyglądu wybranego wycinka. UWAGA - w miejscu zielonych gwiazdek należy wstawić procedurę wyświetlania slajdów dla każdego image_button. Tutaj zostało to pominięte.
(defun C:TEST1 ( / Dcl BtnList)
  (setq Dcl (load_dialog "dcl"))
  (new_dialog "dcl_1" Dcl)

  (setq BtnList '("b_1" "b_2" "b_3"))
  ;;; * * * * * * ; 
  (Btn_test1 BtnList "b_1")
  (action_tile "b_1" "(Btn_test1 BtnList $key)")
  (action_tile "b_2" "(Btn_test1 BtnList $key)")
  (action_tile "b_3" "(Btn_test1 BtnList $key)")

  (start_dialog)
  (unload_dialog Dcl)
)
Po załadowaniu i wywołaniu polecenia TEST1 ukaże się okno w którym możemy dokonywać wyboru poszczególnych image_button. Będą się one zachowywały jak "prawdziwe" przyciski. Oczywiście przedstawiony przykład ogranicza sie tylko do obslugi wycinkow - wynikiem jest wyświetlenie nazwy wybranego wycinka w polu errtile.
W normalnym programie należy w to miejsce wstawić odpowiednią procedurę w zależności od potrzeb programu. Tym sposobem wykonaliśmy cześć programu czyli wybór sekwencyjny - pozostało nam teraz omówienie sposobu wykorzystania tych przyciskow, jako przyciskow zamykających okno - czyli utworzenie panelu sterującego. Wykorzystamy istniejącą definicje okna dialogowego, utworzymy teraz w pliku dcl.lsp definicje polecenia TEST2:
(defun C:TEST2 ( / Dcl )
  (setq Dcl (load_dialog "dcl"))
  (new_dialog "dcl_1" Dcl)
  (foreach X
    '("b_1" "b_2" "b_3")
    (jk:Dlg-icon_On_Off 0 X)
  )
  (action_tile "b_1" 
    "(jk:Dlg-icon_On_Off 1 $key)(done_dialog 2)")
  (action_tile "b_2" 
    "(jk:Dlg-icon_On_Off 1 $key)(done_dialog 2)")
  (action_tile "b_3" 
    "(jk:Dlg-icon_On_Off 1 $key)(done_dialog 2)")
  (start_dialog)
  (unload_dialog Dcl)
)
Ponieważ polecenie TEST2 jest skonstruowane w podobny sposob co TEST1, wykorzystuje też funkcje wcześniej zdefiniowane nie wymaga ono zatem szczegółowego omawiania.
W skrocie: po wyświetleniu okna wywoływana jest funkcja jk:DLG-icon_On_Off z argumentem 0 dla wszystkich wycinków (wszystkie są wypukłe - żaden nie jest wybrany). Naciśnięcie dowolnego image_button powoduje "wciśnięcie" przycisku i zamknięcie okna. Oczywiście reakcja związana z zamknięciem okna jest zależna od programu - tutaj nic sie nie dzieje.
Na tym zakończyliśmy omówienie zagadnienia związanego z tworzeniem "trójwymiarowych" przyciskow piktogramowych w DCL-u. Dodać należy że przedstawione rozwiązanie nie jest pozbawione wad. Pamietać trzeba że operacje wyświetlające slajdy mogą być czasochlonne. Tutaj dodatkowo po wyświetleniu slajdu rysowne są rowniez wektory. Dlatego tez sposób ten jest dobry dla okien w których ilośc przyciskow jest racjonalnie wyważona. Nawet na szybkich komputerach wyświetlenie okna dialogowego z np 24 elementami typu image_button, wyświetlenie w każdym z nich slajdu, nastepnie utworzenie ramek, byloby juz widoczne dla użytkownika - mogłoby to być już irytujące. W swojej praktyce stosuję to rozwiązanie dla 3 - 8 przyciskow z nieskomplikowaną grafiką (stosunkowo prostym slajdem).