Ich versuche eine Menüführung in c zu realisieren:
Es sieht in etwa wie folgt aus:
1
unsignedcharflag=0;
2
...
3
while(1)
4
{
5
if((!BUTBACK_READ&&!BUTNEXT_READ&&flag==0))
6
{
7
DelayMs(500);
8
if((!BUTBACK_READ&&!BUTNEXT_READ))
9
{
10
DelayMs(100);
11
flag++;
12
}
13
}
14
15
if(!BUTNEXT_READ&&flag==1)
16
{
17
DelayMs(100);
18
flag++;
19
20
}
21
22
if(!BUTBACK_READ&&flag==1)
23
{
24
flag--;
25
beep();
26
}
27
28
29
switch(flag)
30
{
31
case=0:
32
menu_anzeigen0();break;
33
case1:
34
menu_anzeigen1();break;
35
case2:
36
menu_anzeigen2();break;
37
}
38
39
}
Er setzt das Flag immer direkt = 2,was mir unverständlich ist.
Passt das in etwa so oder ist das totaler Schrott? Es soll schon eine
relativ verschachtelte Menüführung werden.
Welche anderen Optionen gibt es in C?
Danke
Erst mal Tasterkennung und Tastenentprellung vom Menü trennen. Das sind
zwei Dinge, die so nicht zusammengehören.
Empfehlen kann ich die Danegger Entprellung aus dem Artikel
Entprellung. Mit der ist der Teil Tastenerkennung erledigt und die
weitere Auswertung ein Kinderspiel.
1
currentMenu=0;
2
while(1){
3
4
if(get_key_press(1<<KEY_NEXT)){
5
if(currentMenu<maxNrMenuPoints-1)
6
currentMenu++;
7
}
8
9
if(get_key_press(1<<KEY_BACK)){
10
if(currentMenu>1)
11
currentMenu--;
12
}
13
14
switch(currentMenue)
15
.....
16
}
Was du da mit den Makros und dem flag aufziehst, sieht nicht koscher
aus. Da ich aber nicht weiss, was BUTBACK_READ liefert, enthalte ich
mich der Stimme. Was diese Makros tun, musst du selber wissen. Und auch
ob die 0 oder 1 bei nicht gedrückter Taste liefern.
Ich sehe nicht recht, was du mit deinem Quelltext vorhast.
Aber evtl. wolltest du ab dem zweiten if... ein else if.. haben?
Weil so wie es hier steht kann er ja je nach BUT... ggf. mehrfach das
flag++ machen, und kommt dann leicht auf flag==2.
(Nebenbei: flag ist jetzt kein besonders aussagekräftiger Name, ich sehe
nicht was es markieren soll...)
Wenn du das in der zweiten "Runde" (der Durchlauf deiner Schleife?)
wieder wie zu Anfang haben willst, musst du flag halt nach jedem
Durchlauf zurücksetzen - sonst bleibt es natürlich auf seinem Wert
stehen.
Hallo,
ich habe die Menüführung ähnlich dem Vorschlag von Karl Heinz realisiert
und es klappt auch wie gewünscht, danke dafür.
Jetzt möchte ich aber in den Untermenüs, dieselben Taster mit einer
anderen Funktion abfragen, also in etwa so:
EDIT:
In Menu_1 etc. sollen jetzt wieder dieselben Taster abgefragt werden nur
natürlich was anderes machen. Es handelt sich also um eine Art
"verzweigtes Menü". Möchte natürlich keine fertigen Lösungen, sondern
nur ein paar Rat- bzw. Vorschläge wie man sowas machen kann. Am besten
wäre, wenn man dabei an das obige Beispiel anknüpfen könnte.
Schönen Dank
Trenne das Ganze in drei Teile:
1. Tastenerkennung mit Entprellung
2. Aktionsmenü -> Hier werden Aktionen je nach aktuellen Menüpunkt
ausgeführt (Switch case)
3. Anzeigemenü -> Hier werden Dinge angezeigt je nach aktuellen
Menüpunkt (Switch case)
Bastler schrieb:> Trenne das Ganze in drei Teile:>> 1. Tastenerkennung mit Entprellung> 2. Aktionsmenü -> Hier werden Aktionen je nach aktuellen Menüpunkt> ausgeführt (Switch case)> 3. Anzeigemenü -> Hier werden Dinge angezeigt je nach aktuellen> Menüpunkt (Switch case)
Hmm, also in etwa so??
Schiri schrieb:> if( get_key_press( 1 << KEY_NEXT ) ) {> Switch(currentMenu)> {> case 0:> currentMenu++;> case 1:> if( get_key_press( 1 << KEY_NEXT ) ) ...
Nope. Das wird nicht funktionieren. Du wirst es wohl kaum schaffen, die
Taste genau in dem kurzen Zeitraum zwischen dem ersten Aufruf von
get_key_press und dem zweiten AUfruf zu drücken. Dazu bist du nicht
schnell genug.
Eine einfache Version besteht darin, dass man jedes Menü in eine eigene
Funktion mit ihrer eigenen Schleife verpackt. WIrd ein Submenu
ausgewählt, dann springt man in die betreffende Funktion, die dieses
Submenue behandelt. Der Nachteil ist natürlich, dass der Prozessor dann
in diesem Submenu hängt und nebenbei ausser Interrupts nichts anderes
mehr macht.
1
voidconfigMenu();
2
3
voidmainMenu()
4
{
5
uint8_tcurrentMenu=0;
6
7
while(1)
8
{
9
if(get_key_press(1<<KEY_NEXT))
10
{
11
if(currentMenu<1)
12
currentMenu++;
13
}
14
15
if(get_key_press(1<<KEY_PREV))
16
if(currentMenu>0)
17
currentMenu--;
18
}
19
20
if(get_key_press(1<<KEY_ACTION))
21
if(currentMenu==0)
22
configMenu();
23
lcd_clear();
24
}
25
26
elseif(currentMenu==1)
27
return;
28
}
29
30
lcd_gotoxy(0,0);
31
if(currentMenu==0)
32
lcd_puts("Konfig");
33
elseif(currentMenu==1)
34
lcd_puts("Exit ");
35
}
36
}
37
38
voidconfigMenu()
39
{
40
uint8_tcurrentMenu=0;
41
42
while(1)
43
{
44
if(get_key_press(1<<KEY_NEXT))
45
{
46
if(currentMenu<2)
47
currentMenu++;
48
}
49
50
if(get_key_press(1<<KEY_PREV))
51
if(currentMenu>0)
52
currentMenu--;
53
}
54
55
if(get_key_press(1<<KEY_ACTION))
56
if(currentMenu==0)
57
LightOn=!LightOn;
58
59
elseif(currentMenu==1)
60
SoundOn=!SoundOn;
61
62
elseif(currentMenu==2)
63
return;
64
}
65
66
lcd_gotoxy(0,0);
67
if(currentMenu==0)
68
lcd_puts(LightOn?"Licht ein":"Licht aus");
69
elseif(currentMenu==1)
70
lcd_puts(SoundOn?"Ton ein ":"Ton aus ");
71
elseif(currentMenu==2)
72
lcd_puts("Zurueck ");
73
}
74
}
75
76
intmain()
77
{
78
....
79
80
81
while(1)
82
{
83
84
// wenn der Benutzer auf 'Action' drückt, kommt er in die Menuesteuerung
85
if(get_key_press(1<<KEY_ACTION))
86
mainMenu();
87
88
....
89
}
90
}
Optimal ist das noch lange nicht. Aber es ist mal ein Anfang für ein
simples Menuesystem. Hier kann man sich zb mal überlegen, wie man die
prinzipiell gleichen Funktionen für die Menues zu einer allgemeinen
Funktion zusammenfassen kann, so dass man all das was ein Menu ausmacht
in einer Struktur sammelt und die gleiche Funktion datengetrieben
arbeitet, so dass man nicht für jedes Submenu eine weitere Funktion
schreiben muss.
OK vielen Dank, sieht recht plausibel aus.
Das Problem ist, wenn ich einmal im MainMenu oder im configMenu hänge,
wie komme ich bei Tasten Druck dann wieder in das vorherige Menu?
Diese Funktionalität muss in meinem Beispiel auf jeden Fall
gewährleistet sein.
Ist dies irgendwie auch in dem obigen Beispiel möglich
Hallo Schiri,
ich löse dass bei mir mit zwei Tasten, für die ich zwei Funktionen nutze
- short_keypress und long_keypress - und somit 4 Events erhalte.
Hier mal die Tabelle zu den Events
1
-------------------------
2
Keypress | Key1 | Key2
3
-------------------------
4
short | Links | Rechts
5
-------------------------
6
long | Exit | Ok
7
-------------------------
Wenn man PeDas Entprellroutine verwendet, dann kann man das so
konfigurieren.
Schiri schrieb:> OK vielen Dank, sieht recht plausibel aus.> Das Problem ist, wenn ich einmal im MainMenu oder im configMenu hänge,> wie komme ich bei Tasten Druck dann wieder in das vorherige Menu?
Entweder mit einer speziellen Taste oder aber so wie ich das
vorgeschlagen habe, mit einem Menüpunkt "Zurück".
Aber egal wie, mit der programmteschnischen Umsetzung verschachtelter
Menues hat das ja nichts zu tun. Denn egal wie du das dann konkret
machst, dieses Problem hast du in jedem Fall. Bei verschachtelten Menüs
gibt es nun mal 4 mögliche Aktionen:
nächter Menüpunkt
vorhergehender Menüpunkt
Menüpunkt auswählen bzw. in ein Submenü einsteigen
aus einem Submenü wieder eine Hierarchieebene aussteigen.
Und die musst du unterscheiden können. Egal wie.
> Diese Funktionalität muss in meinem Beispiel auf jeden Fall> gewährleistet sein.> Ist dies irgendwie auch in dem obigen Beispiel möglich
Du hast den Code aber schon studiert. Oder?
OK Danke,
ich versuche meinen Code mal anzupassen und werde dann berichten. Vielen
Dank schon einmal für den Denkanstoss.
Mein grundsätzliches Problem ist, dass ich keine "reinen" Action Tasten
und keine reinen Next oder Back Tasten habe, d.h. je nachdem in welchem
Menuunterpunkt ich mich befinde, muss ich immer abfragen was der
jeweilige Taster überhaupt bezwecken soll/muss.
Wäre es nicht einfacher eine Baumstruktur aufzubauen mit einer
geeigneten struct die auch gleichzeitig noch die Funktionspointer für
die unterschiedlichen Aktionen des jeweiligen Menupunktes beinhalten?
Schiri schrieb:> Hmmm,>> wie (nur in etwa) würde das aussehen?
zb ungefähr so
1
typedefvoid(*FnctPtr)(void);
2
3
structMenuEntry
4
{
5
constchar*text;// Text für den Menüpunkt
6
uint8_tidForNext;// Welcher Menüeintrag ist der nächste wenn Next gedrückt wird
7
uint8_tidForBack;// Welcher Menueeintrag ist der nächste wenn Back gedrückt wird
8
uint8_tifForAction;// welches Submenue soll aufgerufen werden, wenn ... ???
9
FnctPtrfunction;// Welche Funktion soll aufgerufen werden, wenn ... ???
10
};
11
12
voidToggleLight(void)
13
{
14
LightOn!=LightOn;
15
}
16
17
voidToggleSound(void)
18
{
19
SoundOn!=SoundOn;
20
}
21
22
structMenuEntry[]={
23
{"Konfig ",1,0xFF,2,NULL},// 0
24
{"Exit ",0xFF,0,0xFE,NULL},// 1
25
26
{"Light ",3,0xFF,0xFF,ToggleLight},// 2
27
{"Sound ",4,2,0xFF,ToggleSound},// 3
28
{"Zurueck ",0xFF,3,0,NULL}// 4
29
};
für jeden Menüpunkt gibt es eine Beschreibung: welcher Menüpunkt ist der
nächste, wenn auf NEXT gedrückt wird, welcher Menüpunkt ist der nächste
wenn auf PREV gedrückt wird und welcher Menüpunkt wird angesprungen wenn
eine entsprechende Aktion auszuführen ist. Das kann entweder ein anderer
Menüpunkt sein oder eine Funktion die auszuführen ist.
(Bei allen Id der Menüpunkte hab ich eingeführt, das 0xFF als 'kein
Eintrag' angesehen wird, da ich die 0 brauche, weil 0 ja ein gültiger
Index ins Array ist. 0xFE ist der Code an dieser Stelle, dass das
Menüsystem überhaupt verlassen werden soll)
Da kann man dann eine einfache FUnktion schreiben, die ganz einfach beim
ersten Menüpunkt anfängt und die Information, welche Taste gedrückt
wurde mit den Informationen der Beschreibung verknüpft, um die
gewünschte Aktion zu machen
1
voidHandleMenu()
2
{
3
uint8_tcurrentId=0;
4
5
while(currentId!=0xFE)
6
{
7
if(get_key_press(1<<NEXT))
8
{
9
if(MenuEntry[currentId].idForNext!=0xFF)
10
currentId=MenuEntry[currentId].idForNext;
11
}
12
13
if(get_key_press(1<<PREV)){
14
if(MenuEntry[currentId].idForPrev!=0xFF)
15
currentId=MenuEntry[currentId].idForPrev;
16
}
17
18
if(get_key_press(1<<ACTION)){
19
if(MenuEntry[currentId].idForAction!=0xFF)
20
currentId=MenuEntry[currentId].idForAction;
21
22
elseif(MenuEntry[currentId].function!=NULL)
23
MenuEntry[currentId].function();
24
}
25
26
lcd_gotoxy(0,0)
27
lcd_puts(MenuEntry[currentId].text);
28
}
29
}
wie du allerdings mit nur 2 Tasten eine vernünftige Menüführung
hinkriegen willst, ist mir noch schleierhaft. Zumindest 3 Tasten
brauchst du mindestens. Denn 2 Tasten brauchst du ja schon, um durch die
Menüs vorwärts und rückwärts zu navigieren.
Gut, bei kleinen Menüs könnte man auf das rückwärts navigieren
verzichten und die Menüpunkte einer Ebene im Kreis durchlaufen lassen.
Dann würden nur 2 Tasten reichen. Aber du willst ja anscheinend vorwärts
und rückwärts navigieren können. Dann allerdings brauchst du noch eine
3. Taste mit der Bedeutung "Diese Aktion jetzt ausführen". Gedanken
lesen kann ein Computer nun mal nicht.
Schiri schrieb:> Mein grundsätzliches Problem ist, dass ich keine "reinen" Action Tasten> und keine reinen Next oder Back Tasten habe, d.h. je nachdem in welchem> Menuunterpunkt ich mich befinde, muss ich immer abfragen was der> jeweilige Taster überhaupt bezwecken soll/muss.
Ohne jetzt die Details zu kennen, kann ich dir davon nur abraten.
Benutzer haben es gerne, wenn dieselben Tasten immer dieselben Aktionen
machen. Oder zumindest eine thematisch ähnliche Aktion. Die 'Vorwärts'
bzw. 'Rückwärts' Tasten können im Fall des Falles natürlich auch einen
Wert erhöhen bzw. erniedrigen, oder eine Option nach der anderen
durchschalten, aber zumindest die Bestätigungstaste ist immer dieselbe.
Egal ob damit der aktuell angezeigte Menüpunkt bestätigt wird, oder ob
damit eine Zahleneingabe bestätigt wird oder die Auswahl einer Farbe aus
5 Möglichkeiten.
Die Metapher ist dann: Mit den Tasten '<+>' bzw. '<->' verändere ich das
angezeite. Mit der dritten Taste 'OK' übernehme ich diese Veränderung.
Was immer dann auch diese 'Übernahme' konkret bedeutet.
Das kann sich jeder merken und das kommt jedem natürlich vor, weil er
das schon 100-te male bei anderen Geräten genau so gemacht hat.