Das Deutsche Casio-Taschenrechner Forum wurde zum 31.12.2013 geschlossen und kann weiterhin als Nachschlagewerk verwendet werden.
Wer mehr erfahren möchte: Ein sehr guter Beitrag von Elias

HowTo: Tastenabfragen und Menüs

Hilfe beim Programmieren in Casio Basic.

HowTo: Tastenabfragen und Menüs

Beitragvon cfxm » Sa 6. Mär 2010, 17:59

Leider sieht man ja in Programmen immer wieder schlecht programmierte Tastenabfragen. Dann reagieren die Tasten häufig viel zu langsam oder es werden z. B. direkt mehrere Menüs übersprungen, wenn man eine etwas länger gedrückt hält.

Ich würde daher folgendes Konstrukt (zum Abfragen einer beliebigen Taste) empfehlen:
Code: Alles auswählen
While Getkey
WhileEnd
Do
Getkey
LpWhile Not Ans

Das sieht erst einmal komplizierter aus, als es eigentlich ist. ;-)
Betrachten wir zuerst mal die letzte Schleife: Getkey liefert ja bekanntlich den Tastencode der aktuell gedrückten Taste oder null, wenn keine gedrückt wurde. Das Ergebnis wird dann standardmäßig in den Antwortspeicher (Ans) geschrieben oder in eine Variable, sofern diese angegeben wurde. Das folgende Not Ans liefert nun für Ans=0 wahr, also 1, und für Ans=/=0 falsch, also 0, zurück - d. h., wurde keine Taste gedrückt (Ans=0), dann werden mit Getkey erneut die Tasten abgefragt. Will man eine bestimmte Taste abfragen, z. B. [EXE] (Tastencode 31), dann macht man das mit dem Ungleich-Operator (=/=) - also stünde nun statt Not Ans die Anweisung Ans=/=31 an der Stelle. Bei mehreren Tastenabfragen müssen diese mit And verknüpft werden. Wozu aber die erste Schleife? Dazu folgendes Beispiel: Wir werden von einem Menü ins nächste geleitet, was normalerweise per [EXE]-Tastendruck geschieht, oder aber unser Programm wird mit der [EXE]-Taste gestartet. Was passiert nun allerdings, wenn wir die [EXE]-Taste nicht loslassen oder aus Versehen etwas länger gedrückt halten? Dann überspringen wir direkt das nächste oder erste Menü. Besonders fällt das auf neueren GTR wie dem fx-9860G auf, der Casio-Basic-Befehle drei- bis fünfmal schneller ausführt. Die Lösung: Vor der eigentlichen Tastenabfrage mit einer Warteschleife sicherstellen, dass keine Taste gedrückt wird - d. h., solange Getkey hier einen Wert ungleich null zurückgibt, also ein Tastendruck bereits erfolgt ist, passiert gar nichts. Manchmal empfiehlt es sich für ältere GTR aber dennoch, aus Geschwindigkeitsgründen auf diese zusätzliche Schleife zu verzichten.

Somit wären wir auch schon bei der simpelsten Menüstruktur: Vor die Tastenabfrage nur noch etwas Text mit Locate setzen und Not Ans durch die abzufragenden Tasten ersetzen. Da das aber viel zu einfach ist, werde ich das Ganze mit Untermenüs gleich noch etwas komplizierter machen. :|

Ein Menü mit mehreren Untermenüs
Code: Alles auswählen
Do:ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,3,"[F1] Option A1"
Locate 4,5,"[F2] Option B1"
Locate 4,7,"[MENU] Quit"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=79 And Ans=/=69 And Ans=/=48
'
If Ans=79
Then Do:ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,3,"[F1] Option A2"
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=79 And Ans=/=47
If Ans=79
Then ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=47
0:IfEnd
LpWhile Ans=/=47
0:IfEnd
'
If Ans=69
Then Do:ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,5,"[F2] Option B2"
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=69 And Ans=/=47
If Ans=69
Then ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=47
0:IfEnd
LpWhile Ans=/=47
0:IfEnd
'
LpWhile Ans=/=48
ClrText:" ":Stop

Ja, es geht auch ohne Goto's. O.o

Als erstes entwirren wir mal das Hauptmenü.
Dazu gehören die vier Blöcke, die mit Apostroph voneinander getrennt sind:
Block eins ist das Hauptmenü an sich.
Block zwei ruft das Untermenü A1 auf ([F1] gedrückt).
Block drei ruft das Untermenü B1 auf ([F2] gedrückt).
Block vier verlässt das Hauptmenü bzw. beendet das Programm ([MENU] gedrückt).

Zerlegen wir als nächstes das Untermenü A1:
Code: Alles auswählen
...
If Ans=79
Then Do:ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,3,"[F1] Option A2"
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=79 And Ans=/=47
'
If Ans=79
Then ClrText
Locate 1,1,"==== Menu Title ===="
Locate 4,7,"[EXIT] Back"
While Getkey
WhileEnd
Do:Getkey
LpWhile Ans=/=47
0:IfEnd
'
LpWhile Ans=/=47
0:IfEnd
...

Das Untermenü A1 kann man in drei Blöcke unterteilen:
Block eins ist das Untermenü A1 an sich.
Block zwei ruft das Untermenü A2 auf ([F1] gedrückt).
Block drei verlässt das Untermenü A1 ([EXIT] gedrückt).

Was bewirkt aber die Zeile 0:IfEnd in Block drei? Das IfEnd gehört natürlich zu Block zwei des Hauptmenüs und die Anweisung 0 setzt den Antwortspeicher (Ans) auf null. Das ist insofern wichtig, damit das Hauptmenü später nicht durcheinander kommt, da null keiner Taste zugeordnet ist. Beispiel: Das Untermenü A1 wird über [EXIT] (Tastencode 47) verlassen und das Hauptmenü könnte nun ebenfalls mit [EXIT] verlassen werden, dann würde sich das Programm bei Block vier des Hauptmenüs beenden - wir wollten allerdings nur zurück ins Hauptmenü wechseln. Ist Ans also auf null gesetzt, dann verweist Block vier des Hauptmenüs auf sich selbst (Block eins).

Das Untermenü A2 lässt sich nicht mehr sinnvoll zerlegen. Die Zeile 0:IfEnd sorgt auch hier dafür, dass beim Verlassen der Tastencode gelöscht wird und Block drei des Untermenüs A1 an sich selbst (Block eins) weiterleitet. Mit den Untermenüs B1/B2 verhält es sich analog.

Beim folgenden Menütyp (diesmal wird auf Untermenüs verzichtet ;-) ) werden die einzelnen Einträge mit einem kleinen Pfeil ausgewählt. Empfehlen würde ich dafür den Pfeil des Sprungbefehls (=>) - diese beiden ASCII-Zeichen sind also als ein Zeichen zu verstehen.

Ein Menü mit Auswahlpfeil
Code: Alles auswählen
ClrText:3->J
Locate 1,1,"==== Menu Title ===="
Locate 5,3,"[=>] Option A"
Locate 5,4,"[ ] Option B"
Locate 5,5,"[ ] Option C"
Locate 5,6,"[ ] Option D"
Locate 5,7,"[ ] Option E"
'
Do:While Getkey
WhileEnd
Do:Getkey
LpWhile Not Ans
If Ans=28 Or Ans=37
Then Locate 6,J," "
Ans=28=>J-1->J
Ans=37=>J+1->J
J>7=>3->J:J<3=>7->J
Locate 6,J,"=>"
IfEnd
LpWhile Ans=/=31 And Ans=/=47
'
If Ans=47
Then ClrText:" ":Stop
IfEnd
ClrText:J-3->J
J=0=>"Option A"
J=1=>"Option B"
J=2=>"Option C"
J=3=>"Option D"
J=4=>"Option E"

Auch dieses Programm habe ich der Übersicht halber mal in drei Blöcke aufgeteilt:
Block eins ist der Initialisierungsteil - in unserem Fall hauptsächlich Text.
Block zwei beinhaltet die Tastenabfrage und die Steuerung des Auswahlpfeils.
Block drei würde dann entsprechend des gewählten Menüeintrags fortfahren.

Auffällig ist, dass die Tastenabfrage an eine dritte Schleife weitergeleitet wird. Das ist einfach aus dem Grund so, da ohnehin eine weitere Schleife benötigt wird. Schließlich muss der Auswahlpfeil bewegt und danach die Tasten erneut abgefragt werden. Interessant ist vor allen Dingen die If-Anweisung: Hier wird zuerst die alte Position des Auswahlpfeils mit einem Leerzeichen überschrieben, dann die Zeilenvariable J entsprechend der gedrückten Cursortaste um eins verringert (Cursortaste nach oben; Tastencode 28) oder erhöht (Cursortaste nach unten; Tastencode 37), als nächstes geprüft, ob die neue Zeile überhaupt noch zum Menübereich gehört und falls nicht, J entsprechend angepasst und schließlich der Auswahlpfeil in die neue Zeile geschrieben. Mithilfe der [EXE]-Taste wird nun ein bestimmter Menüeintrag ausgewählt und J um drei subtrahiert, liefert uns den entsprechenden Eintrag als Wert von null bis vier.

Der nächste Menütyp besitzt scrollbare Einträge und ist wohl eher etwas speziell.

Ein Menü mit scrollbaren Einträgen
Code: Alles auswählen
ClrText:File 1
{4,5,0,1,2}->List 1
Locate 1,1,"==== Menu Title ===="
Locate 6,5,"["
Locate 15,5,"]"
'
Do:For 1->I To 5
List 1[I]=0=>Locate 7,I+2,"Option A"
List 1[I]=1=>Locate 7,I+2,"Option B"
List 1[I]=2=>Locate 7,I+2,"Option C"
List 1[I]=3=>Locate 7,I+2,"Option D"
List 1[I]=4=>Locate 7,I+2,"Option E"
List 1[I]=5=>Locate 7,I+2,"Option F"
Next
While Getkey
WhileEnd
Do:Getkey
LpWhile Not Ans
Ans=37=>1+List 1-6Int ((1+List 1)/6)->List 1 // OS 2.00: (1+List 1) Rmdr 6->List 1
Ans=28=>5+List 1-6Int ((5+List 1)/6)->List 1 // OS 2.00: (5+List 1) Rmdr 6->List 1
LpWhile Ans=/=31 And Ans=/=47
'
If Ans=47
Then ClrText:" ":Stop
IfEnd
List 1[3]->J
ClrText:ClrList 1 // Ältere GTR: {0}->List 1
J=0=>"Option A"
J=1=>"Option B"
J=2=>"Option C"
J=3=>"Option D"
J=4=>"Option E"
J=5=>"Option F"

Die drei Blöcke sind analog zum vorhergehenden Beispiel. Block zwei scrollt nun allerdings Menüeinträge.

Auch hier wird die Tastenabfrage an eine dritte Schleife weitergeleitet. Neu ist allerdings eine Zählschleife, die für die Anzeige der richtigen Menüeinträge verantwortlich ist. Das funktioniert so: Da wir immer nur fünf von sechs Einträgen auf dem Bildschirm anzeigen lassen, benötigen wir auch nur eine Liste mit fünf Elementen. In jedem davon befindet sich ein Wert, der einem bestimmten Eintrag zugeordnet wird - also 0 für Eintrag A, 1 für Eintrag B und so weiter. Zu Beginn verweist die Liste auf die Einträge E(4), F(5), A(0), B(1) und C(2). Werden nun alle Einträge nach oben verschoben (Cursortaste nach unten; Tastencode 37), wird jeder Wert innerhalb der Liste um eins erhöht. Das hat jedoch zur Folge, dass aus dem Eintrag F(5) nun ?(6) wird - was offensichtlich nicht definiert ist. Deshalb werden alle Werte zusätzlich noch durch sechs, also die Anzahl aller Einträge, dividiert und der Rest davon bestimmt. Das sorgt dafür, dass diese wieder innerhalb des zulässigen Bereiches liegen (Neuere GTR mit installierter Firmwareversion 2.00 sollten hier den Befehl Rmdr verwenden). Ähnlich verhält es sich beim Verschieben aller Einträge nach unten (Cursortaste nach oben; Tastencode 28), bloß dass hier alle Listenwerte vor der Division um eins subtrahiert werden müssten. Da negative Zahlen aber zu Problemen führen, kann man auch einfach die Anzahl aller Einträge minus eins addieren - also fünf. Zu guter Letzt wird noch der entsprechende Menüeintrag durch Drücken der [EXE]-Taste ausgewählt und über das mittlere Listenelement kann dieser als Wert im Bereich von null bis fünf ausgelesen werden.

Für ältere GTR sollte noch angemerkt werden, dass die Geschwindigkeit bei diesem Menütyp doch etwas zu wünschen übrig lässt. Als Lösung könnte man die Anzeige der Einträge auf drei statt fünf reduzieren oder aber auf die zusätzliche Warteschleife bei der Tastenabfrage verzichten. Auch sollten bei sehr vielen Menüeinträgen die Sprungbefehle innerhalb der Zählschleife mit If-Bedingungen zu Vierer- oder Fünfergruppen gepackt werden.

Das könnte dann z. B. wie folgt aussehen:
Code: Alles auswählen
If List 1[I]<4
Then List 1[I]=0=>...
...
Else If List 1[I]<8
Then List 1[I]=4=>...
...
Else If List 1[I]<12
Then List 1[I]=8=>...
...
IfEnd:IfEnd:IfEnd:...

Abschließend werde ich noch auf den Befehl Menu eingehen, für welchen allerdings GTR ab dem fx-9860G oder neuer und mit installierter Firmwareversion 2.00 benötigt werden.

Ein Popup-Menü (ab OS 2.00)
Code: Alles auswählen
ClrText:6
Menu "Menu Title","Option A",0,"Option B",1,"Option C",2,"Option D",3,"Option E",4,"Option F",5
Lbl 0:Ans-1
Lbl 1:Ans-1
Lbl 2:Ans-1
Lbl 3:Ans-1
Lbl 4:Ans-1
Lbl 5:Ans-1
Ans=0=>"Option A"
Ans=1=>"Option B"
Ans=2=>"Option C"
Ans=3=>"Option D"
Ans=4=>"Option E"
Ans=5=>"Option F"

Die Syntax von Menu ist eigentlich recht einfach: Das erste Argument ist der Menüname, das zweite der Name des ersten Menüeintrags, das dritte der Label, mit welchem dieser verknüpft werden soll, das vierte der Name des zweiten Menüeintrags und so weiter und so fort. Die Namen können natürlich auch durch String-Variablen (erfordern ebenfalls OS 2.00) ersetzt werden und es müssen mindestens zwei und dürfen höchstens neun Menüeinträge vorhanden sein. Wählt man nun als nächstes mit der [EXE]-Taste (oder über den Nummernblock) einen bestimmten Eintrag aus, setzt das Programm an jenem Label fort, der diesem zuvor zugewiesen wurde. Da ich aber bekanntermaßen kein Freund von Lbl & Goto bin, habe ich das Ganze mal etwas anders strukturiert: Bevor Menu überhaupt aufgerufen wird, setzt die zweite Anweisung den Antwortspeicher (Ans) auf die Anzahl der Menüeinträge - in diesem Fall also sechs. Zudem folgt jedem Label die Anweisung Ans-1, die je nach Nummer des Eintrags entsprechend oft ausgeführt wird. Das liefert dann z. B. für den dritten und mit Label 2 verknüpften Menüeintrag einen Wert von zwei.

An sich kann einem der Befehl Menu zwar viel Tipparbeit ersparen, aber dafür werden auch einerseits neuere GTR benötigt und andererseits lässt die Anpassbarkeit auch etwas zu wünschen übrig. Es ist beispielsweise nicht möglich eine Abbruchroutine mit der [EXIT]-Taste zu realisieren.

Aber wie dem auch sei, dem einen oder anderen habe ich hoffentlich dennoch ein paar Tipps geben können. :-)
Zuletzt geändert von cfxm am Di 19. Okt 2010, 22:37, insgesamt 13-mal geändert.
cfxm
 
Beiträge: 739
Registriert: Mi 1. Apr 2009, 19:39

Re: HowTo: Tastenabfragen und Menüs

Beitragvon cfxm » So 9. Jan 2011, 20:37

[gelöscht]
Zuletzt geändert von cfxm am Fr 25. Jan 2013, 15:10, insgesamt 1-mal geändert.
cfxm
 
Beiträge: 739
Registriert: Mi 1. Apr 2009, 19:39

Re: HowTo: Tastenabfragen und Menüs

Beitragvon oliwood » Di 15. Nov 2011, 10:19

Danke, für die tolle Anleitung!
oliwood
 
Beiträge: 5
Registriert: Do 10. Nov 2011, 15:16
Taschenrechner: Algebra FX 2.0


Zurück zu Casio Basic (Alle Modelle, die dies unterstützen)

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

cron