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