Obsługa klawiszy specjalnych
Transkrypt
Obsługa klawiszy specjalnych
13 Obsługa klawiszy specjalnych Oprócz klawiszy alfanumerycznych na klawiaturze peceta jest sporo klawiszy specjalnych. Najlepiej stosować klawisze specjalne zgodnie z ich typowym przeznaczeniem. 13.1. O klawiszach specjalnych Ogólnie rzecz biorąc, klawisze specjalne generują zamiast pojedynczego bajta (jak klawisz z literą) kody dwubajtowe. Zwyczajny klawisz, którego bajt może być zdeszyfrowany przy użyciu kodu ASCII, możemy wczytywać tak: Var Klawisz : Char; ... Klawisz := ReadKey; — i porównywać go z zadaną wartością tak: If (Klawisz = #27) Then ... lub tak: If (Klawisz = ‘A’) Then ... Aby program poprawnie reagował na klawisz (dowolny) naciśnięty przez użytkownika, możemy posłużyć się funkcją KeyPressed(). Ta funkcja tylko sprawdza, czy został naciśnięty jakikolwiek klawisz (technicznie rzecz biorąc — czy w buforze klawiatury oczekuje znak), ale nie wczytuje tego znaku z bufora. Oczekujący znak pozostaje „w kolejce” i najbliższa funkcja ReadKey() może go wczytać. Możemy zatem wywoływać funkcję ReadKey() tylko wtedy, gdy użytkownik rzeczywiście coś nacisnął. Jeśli nie, program może nadal działać. Na listingu zaprezentowano program przykładowy. SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 11 2010-04-25 10:44:03 Rozdział 13 t Obsługa klawiszy specjalnych Listing L1301.PAS Uses Crt; Var Znak : Char; BEGIN Repeat Begin Write(‘.’); If KeyPressed Then Znak := ReadKey; If (Znak = ‘A’) Then Break; End; Until (False); END. Program będzie drukował kropki, nie czekając na naciśnięcie klawisza, ale jeśli A zostanie naciśnięte, natychmiast posłusznie zareaguje. W podobnie złożony sposób musimy wczytywać kody klawiszy, jeśli chcemy poprawnie odróżnić klawisze zwykłe od specjalnych i rozpoznać interesujące nas klawisze specjalne. Prosty program wykrywający takie klawisze może wyglądać na przykład tak, jak na listingu L1302.PAS. Zanim jednak zostanie przytoczony kompletny listing, omówimy pewną sztuczkę techniczną użytą w tym przykładzie. Repeat Begin Write(‘.’); If KeyPressed Then Znak := ReadKey; If (Znak = ‘A’) Then Break; End; Until (False); Jest to konstrukcja pętli nieskończonej, którą przerywa od wewnątrz instrukcja Break (przerwij). Ten fragment kodu jest równoważny zapisowi: Repeat Begin Write(‘.’); If KeyPressed Then Znak := ReadKey; End; Until (Znak = ‘A’); Zauważmy, że analogiczną, przerywaną od wewnątrz pętlę programową można skonstruować, posługując się słowem kluczowym While (dopóki). Są jednak dwie istotne 12 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 12 2010-04-25 10:44:03 13.1. O klawiszach specjalnych różnice. Po pierwsze, zamiast False mamy True; po drugie, warunek jest sprawdzany na wejściu do pętli, a nie na wyjściu z niej. While (True) Do Begin Write(‘.’); If KeyPressed Then Znak := ReadKey; If (Znak = ‘A’) Then Break; End; W wielu nowoczesnych językach programowania (nie tylko w Pascalu, także w C, C++ czy Javie) oprócz słowa Break istnieje jeszcze jedno słowo o podobnym przeznaczeniu, stanowiące swoiste uzupełnienie słowa Break. Chodzi mianowicie o słowo Continue (kontynuować), ale w tym momencie jeszcze go nie użyjemy. Listing L1302.PAS Uses Crt; Var Znak : Char; BEGIN ClrScr; WriteLn(‘Rozpoznaje klawisze specjalne...’); Repeat Begin If KeyPressed Then Begin Znak := ReadKey; If (Znak = #0) Then WriteLn(‘Klawisz specjalny!’); End; If (Znak = #27) Then Break; End; Until (False); END. Ten kod pozwala wykryć m.in. klawisze: qq funkcyjne F1 – F10 (tabela 13.1), qq kursora (strzałki), qq Insert – PageDown. Sprawdziliśmy, że pierwszy bajt jest zerem, został więc pominięty w tabelach 13.1 i 13.2. Ponieważ jednak klawisze sterujące mogą być naciskane łącznie z klawiszami modyfikującymi (Shift, Alt, Ctrl), kodów jest sporo. 13 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 13 2010-04-25 10:44:03 Rozdział 13 t Obsługa klawiszy specjalnych Tabela 13.1. Kody klawiszy funkcyjnych F1 – F10 Klawisz Bez modyfikatora +Shift +Ctrl +Alt F1 59 84 94 104 F2 60 85 95 105 F3 61 86 96 106 F4 62 87 97 107 F5 63 88 98 108 F6 64 89 99 109 F7 65 90 100 110 F8 66 91 101 111 F9 67 92 102 112 F10 68 93 103 113 Tabela 13.2. Kody klawiszy kursora (strzałek) Klawisz Bez modyfikatora +Ctrl W lewo 75 115 W prawo 77 116 W górę 72 160 W dół 80 164 Niektóre klawisze specjalne powiązane są z kodami sterującymi ASCII (zakres od 0 do 31) i w związku z tym nie generują kodu dwubajtowego, lecz jednobajtowy. Takie znaki sterujące wraz z odpowiadającą im kombinacją (np. Ctrl+C = ^C, Ctrl+Z = ^Z) podano w tabeli 13.3. Tabela 13.3. Jednobajtowe znaki sterujące Oznaczenie Kod Działanie ^@ (NUL) 0 Puste miejsce ^C (Break) 3 Przerwij (ETX — koniec tekstu) ^G (Bell) 7 Beep ^H (BackSpace) 8 Cofnij kursor ^I (Horizontal Tab) 9 Tabulacja 14 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 14 2010-04-25 10:44:03 13.1. O klawiszach specjalnych ^J (Line Feed) 10 O jeden wiersz w dół ^L (Form Feed) 12 O stronę w dół ^M (CR) 13 Kursor na początek wiersza ^P (Del) 16 Usuń znak ^Z (EOF) 26 Koniec pliku tekstowego ^] (Esc) 27 Esc Spacja 32 Odstęp Ctrl+Enter 10 Jak ^J Przechodzenie między kolejnymi oknami Turbo Pascala możesz realizować za pomocą klawisza Alt+numer okna, na przykład Alt+1, Alt+2 (tabela 13.4). Dzieje się tak, ponieważ klawisze cyfr także w połączeniu z klawiszem Alt generują specjalne kody dwubajtowe. Ostatnia, dodatkowa kombinacja zamieszczona w tej tabeli (Shift+Tab) jest często stosowana do przechodzenia między elementami sterującymi okienek dialogowych (rysunek 22.3). Tabela 13.4. Kody dwubajtowe tworzone z wykorzystaniem klawisza Alt Klawisze Kod Klawisze Kod Alt+1 0 120 Alt+7 0 126 Alt+2 0 121 Alt+8 0 127 Alt+3 0 122 Alt+9 0 128 Alt+4 0 123 Alt+0 0 129 Alt+5 0 124 Alt+- 0 130 Alt+6 0 125 Shift+Tab 0 15 W tabeli 13.5 zebrane zostały niektóre specjalne klawisze sterujące i ich kody. Tabela 13.5. Kody specjalnych klawiszy sterujących Klawisz Bez modyfikatora +Shift +Ctrl Home 0 71 55 End 0 79 49 0 117 PageUp 0 73 57 0 132 PageDown 0 81 51 0 118 15 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 15 2010-04-25 10:44:03 Rozdział 13 t Obsługa klawiszy specjalnych Insert 0 82 48 0 165 Del 0 83 46 0 166 Enter 13, 10 10 Działanie innych kombinacji można sprawdzić za pomocą programu testującego. Listing L1303.PAS Uses Crt; Var Znak : Char; BEGIN ClrScr; WriteLn(‘Wprowadz klawisz lub kombinacje klawiszy: ‘); Repeat Znak := ReadKey; If ( Ord(Znak) In [1..31] ) Then WriteLn(‘Znak sterujacy 1..31.’); If ( Ord(Znak) In [32..127] ) Then Write(#13+#10+’Zwykly znak: ‘); If ( Ord(Znak) In [128..170] ) Then WriteLn(‘Rozszerzony kod ASCII’); If ( Ord(Znak) In [171..255] ) Then WriteLn(‘Znak semigraficzny’); If ( Ord(Znak) In [32..255] ) Then Write(Znak); If (Znak = #0) Then Begin Znak := ReadKey; If ( Ord(Znak) In [59..68] ) Then WriteLn(‘Klawisz F’, Ord(Znak) - 58) Else Write(#13+#10+’Inny klawisz specjalny...’); End; Until (Znak = #27); END. Wyjaśnijmy konstrukcję wyrażenia warunkowego: If ( Ord(Znak) In [1..31] ) Then WriteLn(‘Znak sterujacy 1..31.’); Funkcja Ord(Znak), której nazwa pochodzi od ORDering number (numer kolejny, porządkowy), zwraca numer w kodzie ASCII wczytanego znaku przechowywanego w zmiennej Znak. Ten numer jest następnie porównywany z zadanym zakresem: ... In [1..31] 16 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 16 2010-04-25 10:44:03 13.2. Instrukcje While i Case Jeśli liczba należy do przedziału 1 – 31, następuje wykonanie tego, co znajduje się po Then (tu: wydrukowanie stosownego komunikatu). Oto druga nieco bardziej skomplikowana konstrukcja: ... Then WriteLn(‘Klawisz F’, Ord(Znak) - 58) Jest to wywołanie procedury wyprowadzającej na ekran z dwoma parametrami. Pierwszy parametr — łańcuch znaków — jest oczywisty: WriteLn( ‘Klawisz F’, ..... );. Drugi stanowi wyrażenie, którego wartość program musi obliczyć, a następnie wyprowadzić wynik na ekran: WriteLn(‘.............’, Ord(Znak) - 58 );. Skoro klawisze funkcyjne mają kody, na przykład F1 — 0 59, F2 — 0 60, to po odjęciu od kodu liczby 58 uzyskamy dodatkową informację — numer klawisza funkcyjnego: qq 59 - 58 = 1 --> F1, qq 60 - 58 = 2 --> F2 itd. I ostatnia sztuczka techniczna zastosowana w programie. Parametry przekazywane przy wywołaniu funkcji lub procedury mogą być wyrażeniami. Jeśli takie wyrażenie jest wyrażeniem arytmetycznym typu Write( 2+3 ), wszystko jest oczywiste. Wyniki takich wyrażeń obliczane są najpierw, a następnie to te właśnie wyniki przekazywane są jako parametry procedurze. W naszym przypadku wyrażenie ma postać: Write(#13+#10+’Zwykly znak: ‘); Oznacza ono dodawanie łańcuchów znakowych. Zapis #13+#10+’Zwykly znak: ‘ oznacza: „Do napisu ‘Zwykły znak: ‘ dodaj na początku kod specjalny 13, 10, czyli przed wyprowadzeniem napisu przenieś kursor na początek nowego wiersza”. Posługując się programem tego typu, możemy wykrywać naciśnięcie dowolnej kombinacji klawiszy, jeśli tylko ma ona własny unikalny kod. 13.2. Instrukcje While i Case Rozważanie kolejnych możliwych wariantów można przeprowadzić za pomocą tzw. drabinki, posługując się konstrukcjami warunkowymi typu: If ( warunek1 ) Then ... Else If ( warunek2 ) Then ... Else ... Jednak często znacznie wygodniejsze bywa zastosowanie słowa kluczowego Case (wariant). Sposób zastosowania tej instrukcji przedstawiony został na prostym przykładzie. Listing L1304.PAS Uses Crt; Var Znak : Char; BEGIN ClrScr; 17 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 17 2010-04-25 10:44:03 Rozdział 13 t Obsługa klawiszy specjalnych While (Znak <> #27) Do Begin Znak := ReadKey; Case Znak Of ‘A’..’Z’, ‘a’..’z’: WriteLn(‘Litera’); ‘0’..’9’: WriteLn(‘Cyfra’); ‘+’, ‘-’, ‘*’, ‘/’: WriteLn(‘Operator’); Else WriteLn(‘Znak Specjalny’); End; End; END. Zwróćmy uwagę, że konstrukcja: Case ZMIENNA Of wariant1: instrukcje; wariant2: instrukcje; .... Else instrukcje; End musi być zakończona własnym słowem End. Instrukcja pętli While sprawdza warunek na początku (na wejściu) i wykonuje pętlę programową, dopóki warunek jest spełniony. While (warunek) Do Begin .... End; A oto poprzedni przykład zmodyfikowany za pomocą konstrukcji Case: Listing L1305.PAS Uses Crt; Var Znak : Char; BEGIN ClrScr; Write(Ord(Znak)); ReadKey; ClrScr; 18 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 18 2010-04-25 10:44:03 13.2. Instrukcje While i Case WriteLn(‘Wprowadz klawisz lub kombinacje klawiszy: ‘); While (Znak <> #27) Do Begin Znak := ReadKey; Case (Ord(Znak)) Of 1..31: WriteLn(‘Znak sterujacy 1..31.’); 32..127: Write(#13+#10+’Zwykly znak.’); 128..170: WriteLn(‘Rozszerzony kod ASCII’); 171..255: WriteLn(‘Znak semigraficzny’); { 32..255: Write(Znak); <-- Dublowanie! } End; If (Znak = #0) Then Begin Znak := ReadKey; Case (Ord(Znak)) Of 59..68: WriteLn(‘Klawisz F’, Ord(Znak) - 58) Else Write(#13+#10+’Inny klawisz specjalny...’); End; End; End; END. Dodana na początku sekwencja: ClrScr; Write(Ord(Znak)); ReadKey; ma na celu pokazanie, że już w chwili, gdy warunek wykonania pętli jest sprawdzany: While (Znak <> #27) Do Begin Znak := ReadKey; — zmienna Znak ma nadaną automatycznie przez kompilator Pascala wartość początkową = 0. Gdyby tak nie było (nie wszystkie kompilatory wstępnie zerują zmienne), zmienna Znak mogłaby mieć nieokreśloną przypadkową wartość. Jest to efekt o tyle niebezpieczny, że praktyka niezainicjowania zmiennej przed jej użyciem może spowodować wadliwą pracę programu. Prawidłowa praktyka powinna wyglądać tak: 19 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 19 2010-04-25 10:44:04 Rozdział 13 t Obsługa klawiszy specjalnych Znak := #0; While (Znak <> #27) Do Begin Znak := ReadKey; ... Drugi efekt uboczny, na jaki należy tu zwrócić uwagę, to zagadnienie nakładania się lub wyłączności zakresów wartości. Jest to cecha zależna od wersji kompilatora. W Turbo Pascalu 7 i w Delphi przedziały wartości rozpatrywane wewnątrz instrukcji Case nie mogą się nakładać: { 32..127: Write(#13+#10+’Zwykly znak.’); 128..170: WriteLn(‘Rozszerzony kod ASCII’); 171..255: WriteLn(‘Znak semigraficzny’); 32..255: Write(Znak); <-- Dublowanie! } Dlatego właśnie ta część programu musiała być ujęta w nawiasy i stać się komentarzem. Klawisze kursora można w wygodny sposób zastosować także do zmiany prędkości animacji grafiki. Oto fragment kodu, w którym strzałki przyspieszają lub spowalniają przełączanie stron. Zwloka := 100; If (KeyPressed) Then Begin Ch := ReadKey; If (Ch = #0) Then Ch := ReadKey; Case (Ord(Ch)) Of 75 : Inc (Zwloka); 77 : Dec (Zwloka); End; ... SetVisualPage(P1); Delay(Zwloka); SetVisualPage(P2); Delay(Zwloka); ... 20 SINGIEL_Programowanie strukturalne_bez_numerow_spisu_tresci.indd 20 2010-04-25 10:44:04