Nasze pierwsze programy zawierały proste menu wykonujące konkretne czynności. Czasem jednak jest wymagana znacznie większa interakcja z użytkownikiem, to musi coś wpisać, to coś przełączyć, wcisnąć przycisk… Wyobraźmy sobie, że zapragnęło nam się napisać program, który pozwala na dodanie dwóch liczb.
Najprostsze wejście
Najprostszą funkcją, która pozwala pobrać dane od użytkownika jest “input_text”. W pierwszym parametrze przyjmuje ona nagłówek pola. Wydaje się więc, że dobrym podejściem do rozwiązania naszego problemu będzie coś takiego:
x = input_text("Type first number")
y = input_text("Type second number")
alert("#{x} plus #{y} equals #{x+y}")
Program niby działa, ale nie działa. Kiedy podamy liczby 2 i 2, uzyskamy wynik 22. Dzieje się tak dlatego, że interpreter nie wie, że chcemy dodawać liczby. Ostatecznie w polu tekstowym mogła się znaleźć równie dobrze liczba, co zdanie “Ala ma kota”. Skoro więc poprosiliśmy o dodanie dwóch łańcuchów znaków, program uczynił dokładnie to.
Problem typowania zmiennych jest jednym z częściej występujących błędów, który zdarza się popełnić nawet w ogromnych projektach. Często jego diagnostyka jest znacznie utrudniona, gdyż objawy nie muszą być oczywiste.
Na szczęście konwersja napisów na liczby nie stanowi żadnego problemu, używamy tu funkcji o nazwie “to_i” (to integer). Sprawdźmy!
x = input_text("Type first number").to_i
y = input_text("Type second number").to_i
alert("#{x} plus #{y} equals #{x+y}")
Znacznie lepiej, komputer nauczył się, że 2+2 to 4!
Wskazówka
Zależnie od potrzeb, zmianę typu (tzw. “rzutowanie”) można wykonać na różnych etapach działania programu. Można także w osobnych zmiennych zapisywać tę samą wartość jako liczbę i ciąg znaków. Poniższe przykłady są równie poprawne.
x = input_text("Type first number")
y = input_text("Type second number")
alert("#{x} plus #{y} equals #{x.to_i+y.to_i}")
x = input_text("Type first number")
y = input_text("Type second number")
z=x.to_i+y.to_i
alert("#{x} plus #{y} equals #{z}, but not #{x+y}")
Takie okienka są fajne, ale…
Zasypywanie użytkownika serią pól tekstowych i menu wcale nie musi być przyjemne. Co jeśli osoba korzystająca z naszej aplikacji chce dodać do siebie dwie skomplikowane liczby, ale po podaniu pierwszej orientuje się, że coś zrobiła źle? A co jeśli chce zachować ich wartość.
Zarówno w Eltenie, jak i większości powłok systemów operacyjnych odpowiedzią są formularze. Składają się z serii pól, po których użytkownik może swobodnie się poruszać, zaś pewne akcje wykonane są dopiero po potwierdzeniu chęci wykonania danej czynności.
W Eltenie do tworzenia formularzy służy klasa “Form”. Formularze mogą mieć wiele różnego rodzaju elementów, takich jak pola tekstowe, listy, pola wyboru, drzewa czy przyciski. Każde z tych pól tworzone jest oddzielnie, a następnie grupowane w formularz, po którym przemieszczamy się z użyciem klawisza tabulacji.
Kalkulator z prawdziwego zdarzenia
Spróbujmy napisać prosty formularz, który zastąpi nasz kalkulator.
form=Form.new([
edt_x = EditBox.new("First number"),
edt_y = EditBox.new("Second number"),
btn_sum = Button.new("Add")
], 0, false, true)
btn_sum.on(:press) {
alert("#{edt_x.text} plus #{edt_y.text} equals #{edt_x.text.to_i+edt_y.text.to_i}")
form.resume
}
form.wait
Formularz jest, działa… To teraz go omówmy.
form=Form.new([
edt_x = EditBox.new("First number"),
edt_y = EditBox.new("Second number"),
btn_sum = Button.new("Add")
], 0, false, true)
Powyższa składnia może w wielu punktach wprowadzać w błąd. Ostatecznie pojawiają się nam tu jakieś dziwne nawiasy kwadratowe, przecinki, w ogóle dzieje się sporo. Spróbujmy więc rozpisać to w sposób mniej zgrabny, ale prostszy do zrozumienia.
edt_x = EditBox.new("First number")
edt_y = EditBox.new("Second number")
btn_sum = Button.new("Add")
form=Form.new([edt_x, edt_y, btn_sum], 0, false, true)
Teraz wygląda to znacznie lepiej. W pierwszej kolejności tworzymy dwa pola tekstowe, przypisując je do zmiennych “edt_x” oraz “edt_y”. Tworzymy tu nowy obiekt EditBox, który w konstruktorze przyjmuje jeden parametr – etykietę pola.
Następnie tworzymy przycisk, który przypisujemy do zmiennej “btn_sum”. Przycisk to obiekt klasy Button, który w konstruktorze przyjmuje tylko jeden parametr, mianowicie etykietę.
Na samym końcu tworzymy formularz, który:
- W pierwszym parametrze przyjmuje tablicę pól,
- W drugim numer pola (licząc od zera), które domyślnie będzie zaznaczone,
- W trzecim możemy zadeklarować, by formularz był tzw. udawanym formularzem, czyli nie miał dźwięków, my takiego zachowania nie chcemy, dajemy false (fałsz),
- Czwarty ma charakter zgodności ze starszymi wersjami Eltena i w naszych przykładach będzie za każdym razem miał wartość true.
Tak wyglądała sprawa tworzenia pól, mamy formularz, ale nic nie robi, nawet się nie wyświetla. Teraz więc tworzymy przechwycenie zdarzenia.
Każde pole formularza, jak i sam formularz mogą emitować szereg różnych zdarzeń, na przykład oznajmiając wciśnięcie przycisku, wpisanie tekstu do pola edycji, zmianę wartości pola wyboru, wciśnięcie klawisza… Każde z tych zdarzeń możemy przechwytywać. Robimy to poprzez funkcję on, która jako parametr przyjmuje identyfikator zdarzenia. Identyfikator ten jest symbolem, a więc ciągiem poprzedzonym dwukropkiem. Identyfikator “:press” odpowiada wciśnięciu przycisku. Sama obsługa zdarzenia jest deklarowana w nawiasach klamrowych, podobnie jak wcześniej działanie danych elementów menu.
btn_sum.on(:press) {
alert("#{edt_x.text} plus #{edt_y.text} equals #{edt_x.text.to_i+edt_y.text.to_i}")
form.resume
}
Jak widać, pola tekstowe posiadają atrybut text, który zawiera wpisane w nich w danej chwili znaki. Podobnie jak powyżej, konwertujemy je na liczby i podajemy użytkownikowi ich sumę. Nieco większe zaskoczenie może budzić następna linijka. Służy ona do uwolnienia formularza, innymi słowy sprawia, że program go zamknie. Pozostaje jeszcze formularz w ogóle pokazać, do czego służy funkcja “wait”.
form.wait
Funkcja ta zamraża działanie aplikacji i wyświetla formularz aż do momentu wywołania funkcji “resume”.
Flagi pól tekstowych
Obecnie do pól z naszymi liczbami możemy wpisać dowolny tekst. Do manipulowania zachowaniami różnych elementów w programowaniu służą różnego rodzaju flagi. Idea flag opiera się na operacjach bitowych, których w tej chwili nie będziemy wyjaśniać, jako że stoi za nimi dość złożony aparat matematyczny. Na razie wystarczy nam wiedza, że poszczególne flagi łączymy znakiem pionowej kreski (“|”). Jedną z takich flag w Eltenie jest “EditBox::Flags::Numbers”, która sprawi, że do naszego pola można podawać jedynie liczby.
Możemy więc zmienić deklarację naszych pól, o tak:
form=Form.new([
edt_x = EditBox.new("First number", EditBox::Flags::Numbers),
edt_y = EditBox.new("Second number", EditBox::Flags::Numbers),
btn_sum = Button.new("Add")
], 0, false, true)
Jak widać, dodaliśmy tu do konstruktora drugi parametr, którym jest kolekcja flag. Od teraz nasze pole będzie akceptować wyłącznie liczby.
A gdybyśmy chcieli nieco rozbudować nasz formularz?
Może wcale nie chcemy, by nasz program zamykał się po wykonaniu obliczenia? Możemy rozdzielić przyciski dodawania i kończenia pracy.
form=Form.new([
edt_x = EditBox.new("First number", EditBox::Flags::Numbers),
edt_y = EditBox.new("Second number", EditBox::Flags::Numbers),
btn_sum = Button.new("Add"),
btn_close = Button.new("Close"),
], 0, false, true)
btn_sum.on(:press) {
alert("#{edt_x.text} plus #{edt_y.text} equals #{edt_x.text.to_i+edt_y.text.to_i}")
edt_x.set_text("")
edt_y.set_text("")
}
btn_close.on(:press) {
form.resume
}
form.cancel_button = btn_close
form.accept_button = btn_sum
form.wait
W kodzie dokonaliśmy kilku zmian.
Przede wszystkim stworzyliśmy nowy przycisk do zamykania formularza, dzięki czemu możemy wykonać w jednej sesji dowolnie wiele operacji matematycznych. Przypisaliśmy też przyciski do atrybutów “accept_button” oraz “cancel_button” obiektu “form”. Dzięki temu formularz poprawnie przetwarza teraz wciśnięcie klawisza escape i enter w dowolnym polu. Jak pewnie już łatwo się domyślić, ostatnie zmiany, czyli wywołanie na obydwu polach tekstowych funkcji “set_text” z parametrem pustego łańcucha znaków, czyści ich zawartość.
Historia obliczeń
Wyobraźmy sobie, że użytkownik pragnie wykonywać wiele obliczeń, ale nie chce zapomnieć, co liczył. W tym wypadku warto dać mu do dyspozycji pole, w którym zapiszemy historię wszystkich dotychczasowych działań. Na szczęście nie jest to bardzo trudne.
form=Form.new([
edt_x = EditBox.new("First number", EditBox::Flags::Numbers),
edt_y = EditBox.new("Second number", EditBox::Flags::Numbers),
btn_sum = Button.new("Add"),
edt_history = EditBox.new("History", EditBox::Flags::ReadOnly|EditBox::Flags::MultiLine),
btn_close = Button.new("Close"),
], 0, false, true)
btn_sum.on(:press) {
sum=edt_x.text.to_i+edt_y.text.to_i
alert("#{edt_x.text} plus #{edt_y.text} equals #{sum}")
edt_history.set_text(edt_history.text+"\n#{edt_x.text}+#{edt_y.text}=#{sum}")
edt_x.set_text("")
edt_y.set_text("")
}
btn_close.on(:press) {
form.resume
}
form.cancel_button = btn_close
form.accept_button = btn_sum
form.wait
Dodaliśmy nowe pole, które zawiera historię operacji. Posiada ono dwie flagi – “EditBox::Flags::MultiLine” oraz “EditBox::Flags::ReadOnly”, które pozwalają odpowiednio na tworzenie pola wieloliniowego i tylko do odczytu.
Podsumowanie
Mamy już całkiem sprawny, poprawnie liczący i przyjemnie prezentujący się program do dodawania dwóch liczb, a więc nasz pierwszy przykład z konsoli udało nam się przenieść na zgrabny interfejs, przy okazji zastępując jedną linię kodu dwudziestoma. Tak jednak zwykle jest, że interfejs graficzny mniejszych projektów jest niepomiernie dłuższy od samej ich logiki. ## Zadanie domowe Nasz formularz pozwala na dodawanie liczb. Czy nie byłoby ciekawiej dodać przycisków odpowiednio odejmujących, mnożących i dzielących? Oczywiście w wypadku dzielenia trzeba się upewnić, że w drugim polu nie znajdzie się 0, a jeśli… odpowiednio okrzyczeć użytkownika.
Program do pobrania
Jak zwykle gotowy program z przykładem z tego artykułu dostępny jest do pobrania. Pobierz plik zip z programem
Nobody’s gonna write in English as the majority of the community is still Polish. If you do not unerstand something, use the translation service.
angielski please. ok, i was translating it with automatic translator.