Tach zusammen!
Hab da ein Problem bei der Realisierung meiner Menüstruktur in C.
Ich habe vier Taster zur Bedienung meiner Menüstruktur. Ich habe ein
Grundmenü was sich wiederrum in mehrere Untermenüs aufsplittet. Kann ich
diese Menüführung nur über eine if Anweisung mit einer while Schleife
aufbauen oder weiß jemand von Euch einen besseren Rat?
Bin dankbar für jeden Ratschlag!
Natürlich kannst du das,
du speicherst in einer Variable den Tastencode (0-3) und wertest dann
mit Hilfe von "switch" - "case" das ganze aus.
if geht auch; ich persönlich finde switch case schöner.
Zumindest wenn mehr als ein Befehl ausgeführt werden soll.
Ich wäre für switch!
für jede Auswahlmöglichkeit(zb. im Hauptmenü sind 6 untermenüs
aufrufbar)
eine switch-Anweisung nutzen. Im Untermenü dann wieder ein switch
switch(hautpmenü)
switch(untermenü1)
.
.
.
ich weiß, das mit den üs geht so net, also nicht auf mir rumhacken... ;)
UImenuExec zeigt das menu an und gibt eine dem ausgewählten Eintrag
entspr. ID zurück (Eintrag anwählen und irgend'n festgelegten button
drücken).
UIchgIntPtr ist eine Subroutine in der man Werte eingeben kann (mit 4
Tasten).
In 64k IDs ist auch genug Platz die Menu IDs unterzubringen, so
funktionieren auch submenus ohne das UImenuExec was zurückgibt.
Ich habe mich grade auch mit einem Menü 'rumgeschlagen.
Ich rufe zunächst ein Hauptmenü (gesonderte Endlosschleife) auf, in dem
kann über swich/case ein Untermenü von 16 gewählt werden (einzelne
Funktionen mit enthaltener Endlosschleife).
Durch die Displayausgaben, EEPROM lesen/Beschreiben, Werte verändern und
begrenzen usw. frisst das Ding ordentlich Speicher. In meinem Regler
(mit Sonderfunktionen) frisst das Menü beim mega644 schon 15% des Flash.
OK, da kann ich mit leben, aber Optimierung tut da auch Not, werde mich
nach Abschluss des Projektes mal dranmachen und in die Codesammlung
setzen.
Bei meiner Variante ist die Auswertung recht einfach, ich weis ehrlich
gesagt nicht wie man da ~10kB Flash verheizen kann.
UImenuExec muss nur folgendes machen:
- menu ID von aktuell ausgewähltem menu und Eintragsnummer (pos. in
array) in statischer variable speichern
- menuList nach passender menu ID für aktuelles Menu durchsuchen (1
Schleife)
- Funktion aufrufen die dieses Menu mit aktuell gewähltem Eintrag
malt/bei oben/unten Taste die Menueinträge durchgeht, bei ok Nummer des
gewählten Eintrags zurückgibt
- Gucken ob Menu mit der ID des gewählten Eintrags vorhanden - wenn ja,
als neue menu ID benutzen und von vorn anfangen, ansonsten Eintrags-ID
zurückgeben
Und das war's eigentlich schon.
>ich weis ehrlich gesagt nicht wie man da ~10kB Flash verheizen kann.
Mit der Menüstruktur selbst nicht. Das braucht auch fast nix.
Ich will aber in den einzelnen Untermenüs auch was tun. Bei vielen
Untermenüs (bei mir 16) kommt da schon Code zusammen.
Z.B. einen Bildschirm anzeigen (immer Anders), eine Variable ausgeben,
diese durch Tastendruck hoch, bzw: 'runterzählen, die Variable entweder
im EEPROM abspeichern oder direktes Verlassen des Untermenüs. Die
Tastenabfragen und Absicherungen gegen Überlauf usw. sind in vielen
Untermenüs verschieden, darum kann hier nix zusammengefasst werden.
Das ist wie beim Windoof auch, sobald Benutzereingaben gemacht werden
können müssen Gürtel und Hosenträger her. :)
Das kostet schon Platz. ;)
// if there's a menu with this id as mid, goto submenu
205
if(UIsearchMid(id)!=-1)
206
{
207
UImid=id;
208
}
209
// else return id
210
else
211
{
212
UImidLast=UImid;
213
UIidLast=ret;
214
215
returnid;
216
}
217
218
// event tick
219
EventTick();
220
}
221
222
return0;
223
}
Nicht besonders sauber programmiert, aber es funktioniert fehlerfrei
(solange die menustruktur in ordnung ist).
Das EventTick() kann weg (gehört zu einem einfachen Multimthreading wo
Tasks einstellbar alle xyz ms aufgerufen werden, daher in jeder
while-schleife), irgendwo in dem Code ist auch noch die Möglichkeit das
Menu 1- oder 2-zeilig anzeigen zu lassen (UImode).
Die Ausgabe ist in etwa so:
1
>== Menu Name ==< -- selektierter Eintrag, daher die > <
2
Item 1
3
Item 2
4
.
5
.
6
.
Verweist die ID von Item xyz auf ein Submenu wird noch "->" angehängt
(ist ein Zeichen, das 'z'+4).
INPgetReset gehört zum Key-Handler, gibt für die gegebene Key-Mask
ungleich 0 zurück falls die Taste gedrückt wurde, und setzt den Status
der Taste zurück falls dem so ist.
Meinst du weil die Zeilen vorher immer geleert werden? Wie gesagt, als
ich den Code geschrieben habe wollte ich eigentlich was ganz anderes
machen, brauchte dazu aber ein menu.
Außerdem spar ich so die lib-funktion ;)
Richtig Sinn macht das Menu übrigens erst mit dem Event-gedödel. Das
besteht aus einem Array mit einem Eintrag/Task, da kann man einen
Funktionszeiger reinschreiben und das Ausführungsintervall angeben (zB.
alle 100msec).
Über einen Timer wird dann ermittelt welche Funktionen ausgeführt werden
müssen, und das wird dann bei EventTick gemacht.
So kann man nebenher zB. ein paar Werte ausgeben, und muss sich dann
garnimmer drum kümmern.
I_ H. wrote:
> Ach ja, wär sicher net falsch das mal so umzuändern, dass die> Strukturdaten im Flash liegen.
Jep, besonders deine strings. Ansonsten: Die Idee sieht sehr sauber aus
muss ich sagen.
In den prinzipiellen Aufbau ist auch verhältnismäßig die meiste Zeit
geflossen... den Rest kann man ja bei Bedarf auch neu implementieren ;).
Frei nach dem Motto Klassen sind dafür da dem ganzen Müll ein sauberes
Interface zu verpassen (in dem Fall structs und funktionen).
So schaut dann übrigens ein etwas komplexeres Menu aus:
1
// menu structure
2
structUImenumenuAkku=
3
{
4
"Akku",
5
200,
6
4,
7
{
8
{"Info",210},
9
{"Diag",230},
10
{"Prog",220},
11
{"leave",2000}
12
}
13
};
14
15
structUImenumenuAkkuInfo=
16
{
17
"Info",
18
210,
19
5,
20
{
21
{"sh. state",2100},
22
{"sh. energy",2101},
23
{"sh. all",2102},
24
{"hide",2103},
25
{"back",200}
26
}
27
};
28
29
structUImenumenuAkkuDiag=
30
{
31
"Diag",
32
230,
33
4,
34
{
35
{"set charge",2300},
36
{"set disch.",2301},
37
{"reset en.",2302},
38
{"back",200}
39
}
40
};
41
42
structUImenumenuAkkuProg=
43
{
44
"Prog",
45
220,
46
6,
47
{
48
{"Setup",240},
49
{"do disch.",2200},
50
{"do deep d.",2201},
51
{"do charge",2202},
52
{"stop",2203},
53
{"back",200}
54
}
55
};
56
57
structUImenumenuAkkuProgSet=
58
{
59
"Setup",
60
240,
61
8,
62
{
63
{"charge",2400},
64
{"d.charge",2406},
65
{"cur. cur.",2401},
66
{"volt thres.",2402},
67
{"ch. time",2403},
68
{"d.ch. time",2404},
69
{"void time",2405},
70
{"back",220}
71
}
72
};
Es gibt noch ein 2. Menu im Zahlenraum 100, den Zeiger auf das Struct
mit den Menus (UImenuList) kann man ja umladen. Man könnte jetzt auch
noch für jeden Eintrag ein char mit Attributen dazunehmen, mir ist nur
bisher noch nix sinnvolles eingefallen.
Sorry, wenn ich den 15 Jahre alten Thread nochmals bemühe, aber
offensichtlich hat sich in der zugelassenen C-Syntax etwas geändert:
Ich versuche das Beispiel aus diesem ForumsArtikel nachzubauen, aber
folgender Code lässt sich einfach nicht (mehr) compilieren:
1
2
// structure for one single menu entry
3
structUImenuEntry
4
{
5
constchar*name;// entry name
6
intid;// entry ID
7
};
8
9
// structure for a simple menu
10
structUImenu
11
{
12
constchar*menuName;// menu name
13
intmid;// menu ID will be used to search for this entry
14
15
charentries;// how many entries will be in the following list
16
structUImenuEntrye[];// for list of menu-entries (see struct above)
17
};
18
19
/* .......... */
20
21
// definition of main menu
22
structUImenumenuMain={
23
"Main Menu",// menu name
24
100,// menu id of this menu
25
5,// number of entries in this menu
26
{
27
{"Main_01",1000},// entry with returncode when selected
28
{"Main_02",1001},// entry with returncode when selected
29
{"Main_03",1002},// entry with returncode when selected
30
{"Main_04",1003},// entry with returncode when selected
31
{"submenu",101},// entry with ID-Code of the submenu
32
}
33
};
Fehlermeldung:
1
[Error] too many initializers for 'UImenuEntry [0]'
Hab natürlich mal nachgegoogelt und es hängt wahrscheinlich damit
zusammen, dass man keine Inline-Initialization für listen-members mit
unbekannter Listengröße mehr machen darf.
So ganz hab ich es nicht verstanden, aber wenn man auf Stackoverflow
nach "too many initializers for 'int [0]' c++" sucht, findet man einen
entsprechenden Hinweis...
Leider werde ich daraus nicht schlau, wie ich nun die Menüs definieren
kann, damit sie der Compiler vernünftig schluckt....
Kann mir ein C-Guru von Euch hier bitte den richtigen Weg weisen...
Vielen Dank im Voraus
Johann
Hi
C geht mir an Allerwertesten vorbei. Aber ich würde ein Menü als
verkette (oder doppelt verkette) Liste aufbauen. Das Prinzip gibt es
eigentlich in allen Programmiersprachen. Setzt aber die entsprechenden
Sprachkenntnisse voraus-
MfG Spess
Hallo!
Vielen Dank für die Hinweise auf die linked List bzw. State Machine.
Keine Sorge, von meinen Programmier-Skills her kann ich sowohl linked
lists als auch state machines in Programmen umsetzen. Die Links auf
geeksforgeeks.org finde ich trotzdem interessant.
Mir hat jedoch der Lösungsansatz in diesem Forums-Artikel gefallen, dass
ich lediglich durch Inline-Definition der Menü-Einträge sowohl Menüs,
als auch sub-Menüs einhängen kann und die gesamte Menü-Abarbeitung
extrem schlank aufgebaut ist.
Wenn ich es als linked list löse, müsste ich meiner Meinung nach
entweder pro SubMenü eine neue linked list definieren, oder mittels
Menue-ID's und Filterung die Submenüs in einer Gesamt-linked-list
abarbeiten.
Werde noch versuchen, das Inline-Initialisierung-Problem zu lösen und
wenn ich das nicht schaffe, werde mal mit einem Lösungsansatz
Gesamt-Linked-List beginnen.
Vielen Dank!
Es lässt sich aber auch mit dem Komma kompilieren, jedenfalls mit gcc.
per cut&paste in https://www.onlinegdb.com/ kopiert, läuft.
Anstatt des langen switch/case blocks zur Auswertung würde ich function
pointer in die UImenuEntry Strukturen packen.
Würde das mittels RTOS Thread in einer Klasse mit Thread-member (mind.
in einem C Thread, wenn ohne Klassen) abbilden. Diese versendet Messages
für Aktionen, und merkt sich den aktuellen Stand (bzw. geht einfach in
ein osThreadFlagsWait(), vom Eingabethread aus getriggert).
In C++ lassen sich komplexere Menues gut als abgeleitete Klassen eines
Basisobjektes erzeugen, von denen jede Klasse weiß, was sie kann.
Verwaltung per Polymorphie.
Vielleicht ein bischen überzogen, dafür lesbar und einfach erweiterbar.
Und das RTOS bewahrt vor polling.
Johann K. schrieb:> Werde noch versuchen, das Inline-Initialisierung-Problem zu lösen und> wenn ich das nicht schaffe,
Prinzipiell geht das wie in Deinem Beispiel, ist aber fehleranfällig
weil n manuell bachgepflegt werden muss.
Nimm einfach Array und struzktur getrennt:
Ein Array unbekannter Größe mit Einträgen, und die Verwaltungsstruktur
mit Name, ID, diesem Array und n als countof(dieses Array).
Einmal so angelegt, kannst Du x Einträge hinzufügen oder per #define
rausnehmen ohne n nachzuführen
J. S. schrieb:> Es lässt sich aber auch mit dem Komma kompilieren, jedenfalls mit gcc.>> per cut&paste in https://www.onlinegdb.com/ kopiert, läuft.>
Ja, diese Inline-Initialisierung eines Pointer-Arrays unbestimmter Größe
ist bei manchen Compilern noch akzeptiert, aber dürfte nicht exakter
Standard sein, weshalb manche Compiler dies nicht akzeptieren...
> Anstatt des langen switch/case blocks zur Auswertung würde ich function> pointer in die UImenuEntry Strukturen packen.
Ja, es gibt noch einiges Verbesserungspotential wie function-pointer und
Strings in den Progmem und non-blocking für den Microprozessor-loop().
Zuerst wollte ich aber einmal den Beispielcode zum laufen bringen und
dann entsprechend umbauen...
Mit C komme ich ganz gut zu recht inklusive einfachen Pointern. Aber bei
Pointer-Listen bzw. Arrays von pointern hab ich noch ein bissl an der
Syntax zu knabbern.
Was das ist und wie es intern arbeiten soll, verstehe ich, weil ich
schon vor 30 Jahren in Assembler auf Großrechnern programmiert habe.
Aber in C die richtige Syntax zusammenzubringen ist für mich in manchen
Fällen wie diesem eine Herausforderung...
Danke für Eure Ratschläge und Unterstützung!
es läuft in C und C++, auch mit den moderneren C++ Varianten. Nur das
alte Turbo C/C++ liefert den Fehler (im OnlineGDB ausgeführt).
Die Funktionszeiger kann man mit typedef entschärfen, hier auch ohne
Anzahl und mit Endekennung:
1
#include<stdio.h>
2
3
// structure for one single menu entry
4
5
typedefvoid(*UIMenuFn)();
6
7
typedefstruct
8
{
9
constchar*name;// entry name
10
UIMenuFnfn;// entry function
11
}UImenuEntry;
12
13
// structure for a simple menu
14
15
typedefstruct
16
{
17
constchar*menuName;// menu name
18
UImenuEntrye[];// for list of menu-entries (see struct above)
19
}UImenu;
20
21
/* .......... */
22
23
// definition of main menu
24
25
voidmenuFn1(){
26
printf("function 1\n");
27
}
28
29
voidmenuFn2(){
30
printf("function 2\n");
31
}
32
33
UImenumenuMain={
34
"Main Menu",// menu name
35
{
36
{"Main_01",menuFn1},// entry with returncode when selected
37
{"Main_02",menuFn1},// entry with returncode when selected
38
{"Main_03",menuFn2},// entry with returncode when selected
39
{"Main_04",menuFn2},// entry with returncode when selected
40
{"submenu",menuFn2},// entry with ID-Code of the submenu
Stefan S. schrieb:> nur über eine if Anweisung
Schau dir mal die "Kontrollstrukturen" in C an.
Stefan S. schrieb:> weiß jemand von Euch einen besseren Rat?
Da es sehr viele Möglichkeiten gibt und nichts von deinem Umfeld bekannt
ist, wäre es besser etwas konkreter zu werden.
-Wie liegen die Tastinformetionen vor?
-Wofür sollen die Tasten im Menü benutzt werden
-Wie sieht die Menüstruktur genau aus
-Geht es darum Texte zu wechseln oder unterschiedliche Werte zu
"stellen" oder zu setzen.
-Wie sind die Datenstrukturen der Werte oder Texte die "bedient" werden
sollen
-Wie ist die Programmstruktur?...
Also was genau ist der Umfang und worauf liegt das Hauptaugenmerk usw...