Hallo ich schreibe gerade an einem Menü und würde gerne das Ende der
Liste heraus finden.
1
typedefvoid(*MenuFnct)(int);
2
3
structMenuEntry
4
{
5
charText[20];
6
MenuFnctFunction;
7
intMenuIndex;
8
};
9
10
structMenuEntryMainMenu[]=
11
{
12
// Der Funktion HandleEdit soll beim Aufruf 23 mitgegeben werden
13
{"Auto Search",HandleEdit,101},
14
{"Band Scanner",HandleCopy,102},
15
{"Manual Mode",HandlePaste,103},
16
{"Settings",NULL,104},
17
18
{NULL,NULL,NULL},
19
};
20
21
voidDoMenu(structMenuEntryMenu[])
22
{
23
24
// until the end of pointer list
25
inti=0;
26
while(Menu[i].Function!=NULL){
27
28
osd.print(Menu[i].Text,7,7+i);
29
i++;
30
}
31
}
Diese Variante funktioniert schon mal, ich will aber noch in unter Menü
springen dh steht bei Settings auch NULL da es hier keine
Funktionspointer gibt und ich das mit NULL symbolisiere . Ich wollte
jetzt den String am Ende auf NULL abfragen ->
while (Menu[i].Text != NULL) {
Leider geht das aber nicht, ich komme leider nicht darauf wieso und
weshalb das hier nicht funktioniert.
Gruß
D. C. schrieb:> Leider geht das aber nicht, ich komme leider nicht darauf wieso und> weshalb das hier nicht funktioniert.
Also erstens ist Dein Dreifach-Null am Ende schonmal schlecht, weil NULL
ein Pointer ist, Du das dritte NULL aber in einen INT setzt. Das geht
nur, falls zufälligerweise auf Deiner Plattform int genauso breit wie
ein Pointer ist. Das dritte NULL sollte stattdessen 0 sein.
Zweitens geht das nicht, weil Dein Funktionszeiger eben ein Zeiger ist,
da kann also NULL drinstehen, während Dein Text kein Pointer ist,
sondern ein Array. Da kann nicht NULL drinstehen. Arrays und Pointer
sind nicht ganz dasselbe.
D. C. schrieb:> Diese Variante funktioniert schon mal...
...aber ausgesprochen schlecht.
Fang doch erstmal damit an, dir ein Konzept zurechtzulegen:
- soll es ein reines Text-Menü sein? Wenn nein, dann mußt du mit
Quasi-Objekten arbeiten, wen ja, dann kannst du bei Text bleiben.
- bedenke, was du für Bedienelemente hast. Tasten? und wie viele und mit
welcher Bedeutung? oder Drehknopf mit Pushbutton? Letzlich, ob du nur 3
Dinge hast (vor, zurück, action) oder Koordinaten (auf, ab, rechts,
links, action) oder eine Reihe Knöpfe, denen grafisch ne Überschrift
zugeordnet wird.
Obendrein mußt du dich entscheiden, ob du ein ganzes Menü mit mehreren
Einträgen anzeigen willst oder nur 1 Menüpunkt oder einen Ausschnitt aus
dem Menü. Alle Varianten bedürfen einer etwas anderen Menügestaltung.
Aber in jedem Menü-Element brauchst du folgendes:
- Flags, die dir Eigenschaften anzeigen, also z.B. ob es das erste
Element im Menü ist oder das letzte oder keines von beiden (also
mittendrin), ob das Element ein Start ist für eine Aktion oder ein Edit
für einen Parameter oder ob das Element neu gezeichnet werden muß bei
einer bedienerunabhängigen Angelegenheit (z.B. Uhrzeit-Änderung), sodann
Indizes, die dir anzeigen, wohin die Reise gehen soll, wenn jemand auf
'action' drückt, sodann ein oder mehrere Texte (aber als char* und nicht
als char array).
Eines brauchst du im Menü-Element aber garnicht: einen Menü-Index. Die
Stelle, wo du dich im Menü befindest, wird vollständig beschrieben durch
die Nummer des aktuellen Elementes.
Ich würde das etwa so machen:
#define erster (1<<0)
#define letzter (1<<1)
#define intedit (1<<2)
#define floatedit (1<<3)
.. usw.
struct MenuEntry
{ dword Flags;
void* EditPointer; // wenn Eintrag ein Edit ist
int IndexOnAction; // für Submenü-Sprünge
char* Text1;
// char* Text2; für zweizeiliges LCD
};
W.S.
D. C. schrieb:> Ich wollte jetzt den String am Ende auf NULL abfragen ->> while (Menu[i].Text != NULL) {> Leider geht das aber nicht, ich komme leider nicht darauf wieso und> weshalb das hier nicht funktioniert.
Es liegt daran, dass du den Unterschied zwischen Zeiger und Array noch
nicht verstanden hast. Text ist ein Array. Das kann nicht NULL sein.
Nop schrieb:> Also erstens ist Dein Dreifach-Null am Ende schonmal schlecht, weil NULL> ein Pointer ist, Du das dritte NULL aber in einen INT setzt. Das geht> nur, falls zufälligerweise auf Deiner Plattform int genauso breit wie> ein Pointer ist. Das dritte NULL sollte stattdessen 0 sein.
Stimmt zwar, aber die Begründung ist falsch.
Das geht auch, wenn sizeof(int) != sizeof(NULL) ist.
Je nach Definition von NULL sollte aber eine Warnunung vom Compiler
kommen.
http://www.cplusplus.com/reference/cstdlib/NULL/
Ich habe nie verstanden, wieso man NULL überhaupt verwenden sollte, und
nicht einfach 0 nimmt. Ich meine 0 ist 0, wiso sollte ich da extra eine
Makro Konstante nehmen wenn ich einem Pointer 0 zuweisen will, wenn ich
bei jedem anderen arithmetischen Datentyp einfach 0 für 0 hinschreibe?
Daniel A. schrieb:> Ich habe nie verstanden, wieso man NULL überhaupt verwenden sollte,
Man kann beim lesen schon erkennen, das es um Pointer geht.
Man schreibt auch, wenn es um C-Strings geht, '\0' und nicht 0 für das
Stringende.
Zudem ist NULL nicht immer als 0 definiert. Z.B ist (void*)0 auch
möglich.
Daniel A. schrieb:> Ich habe nie verstanden, wieso man NULL überhaupt verwenden sollte, und> nicht einfach 0 nimmt. Ich meine 0 ist 0, wiso sollte ich da extra eine> Makro Konstante nehmen wenn ich einem Pointer 0 zuweisen will, wenn ich> bei jedem anderen arithmetischen Datentyp einfach 0 für 0 hinschreibe?
Bei GCC wird NULL durch das compilerspezifische __null ersetzt, das
warnt, wenn man es fälschlicherweise in einem nicht-Integer-Kontext
benutzt.
In C++ hat Stroustrup bis C++98 tatsälich auch dazu geraten, 0 statt
NULL für Nullzeiger zu verwenden, unter anderem weil das z.B. dort bei
überladenen Funktionen sonst zu Verwirrung führen kann. Seit C++11
gibt's aber das spezielle Schlüsselwort nullptr, das man nun verwenden
soll.
Dirk B. schrieb:> Zudem ist NULL nicht immer als 0 definiert. Z.B ist (void*)0 auch> möglich.
Spielt aber keine große Rolle. Ob 0 funktionert oder nicht, hängt nicht
davon ab, wie NULL defniert ist.
Naja, aus gutem Grunde ist in Pascal sowas wie 'nil' definiert, ebeb
darum daß man damit ne saubere Unterscheidung hat zwischen Zeiger-Zeugs
und arithmetischem Zeugs. Aber in C kann man ja hinpinseln, was man
will.
ähemm.. und der TO hat sich inzwischen ohne Abschied verdrückt. War ihm
wohl doch nicht so wichtig mit seinem Menü.
W.S.
Daniel A. schrieb:> Ich habe nie verstanden, wieso man NULL überhaupt> verwenden sollte, und nicht einfach 0 nimmt. Ich> meine 0 ist 0, wiso sollte ich da extra eine Makro> Konstante nehmen wenn ich einem Pointer 0 zuweisen> will, wenn ich bei jedem anderen arithmetischen> Datentyp einfach 0 für 0 hinschreibe?
Ich habe auch nie verstanden, was Tristate-Logik für
ein Quatsch sein soll. Ich meine, wenn an einem Ausgang
keine Spannung anliegen soll - warum legt man da nicht
einfach 0V an? Das ist doch "keine Spannung"?
SCNR
W.S. schrieb:> D. C. schrieb:>> Diese Variante funktioniert schon mal...>> ...aber ausgesprochen schlecht.> Fang doch erstmal damit an, dir ein Konzept zurechtzulegen:
Der Code ist aus meinem Konzept entstanden
> - soll es ein reines Text-Menü sein? Wenn nein, dann mußt du mit> Quasi-Objekten arbeiten, wen ja, dann kannst du bei Text bleiben.
Ja es wird ein Text Menü, mit maximal 3 Submenüs am Ende befinden sich
meist als ich sag mal aktion eine Funktion die über einen
Funktionspointer aufgerufen werden soll was auch schon mal funktioniert.
> - bedenke, was du für Bedienelemente hast. Tasten? und wie viele und mit> welcher Bedeutung? oder Drehknopf mit Pushbutton? Letzlich, ob du nur 3> Dinge hast (vor, zurück, action) oder Koordinaten (auf, ab, rechts,> links, action) oder eine Reihe Knöpfe, denen grafisch ne Überschrift> zugeordnet wird.
Ich habe einen Button, der hat die Auswertung Klick(Menü down)
Doubleklick( Menü tiefer) longklick (Einstellung ändern aktion ausführen
-> funktionspointer)
> Obendrein mußt du dich entscheiden, ob du ein ganzes Menü mit mehreren> Einträgen anzeigen willst oder nur 1 Menüpunkt oder einen Ausschnitt aus> dem Menü. Alle Varianten bedürfen einer etwas anderen Menügestaltung.
Vom Menü ist immer das ganze Menülevel sichtbar bzw 4 Einträge wenn es
mehr sein sollen
> - Flags, die dir Eigenschaften anzeigen, also z.B. ob es das erste> Element im Menü ist oder das letzte oder keines von beiden (also> mittendrin), ob das Element ein Start ist für eine Aktion oder ein Edit> für einen Parameter oder ob das Element neu gezeichnet werden muß bei> einer bedienerunabhängigen Angelegenheit (z.B. Uhrzeit-Änderung), sodann> Indizes, die dir anzeigen, wohin die Reise gehen soll, wenn jemand auf> 'action' drückt, sodann ein oder mehrere Texte (aber als char* und nicht> als char array).
Wo liegt das Problem mit char Array ?
> Eines brauchst du im Menü-Element aber garnicht: einen Menü-Index. Die> Stelle, wo du dich im Menü befindest, wird vollständig beschrieben durch> die Nummer des aktuellen Elementes.
Ja das war mein erster Gedanke so einfach wie möglich aber ich bekomme
eben wenig information daraus es hätte aber gereicht, deine Idee mit dem
Flags ist da wohl die bessere Lösung.
Schon mal vielen Danke !!!
Ich hätte mir das mit dem verschachteln so gedacht das ich meiner
Funktion DoMenu einfach bei Settings(nächstes Submenü) nach der Aktion
ein neues MenuEntry übergebe mit dem Aufbau des Submenüs von Settings.
Gruß
Possetitjel (Gast)
>Ich habe auch nie verstanden, was Tristate-Logik für>ein Quatsch sein soll.
Ist zwar eine "off topic" Anmerkung, aber wenn's Deinem Verständnis
hilft:
https://de.wikipedia.org/wiki/Tri-State:
Durch Tri-States ist es möglich, die Ausgänge mehrerer Bauelemente
zusammenzuschalten, ohne dass es zu Kurzschlüssen, einer Überlagerung
oder einer Wired-AND- oder Wired-OR-Verknüpfung kommt, z. B. bei
Datenbussen.
Aber was soll der Schmarren mit den 0b1000000 ?
Dazu hast du doch extra die Flags definiert.
Allerdings passt 0b1000000 zu keinem deiner #defines
Macros werden in C i.A. vollständig groß geschrieben.
(Jetzt kannst du deine Endekennung mit den NULL-Pointern machen.)
Dirk B. schrieb:> Aber was soll der Schmarren mit den 0b1000000 ?> Dazu hast du doch extra die Flags definiert.> Allerdings passt 0b1000000 zu keinem deiner #defines
Außerdem: Warum nicht einfach einen Enum benutzen statt #defines und
int?
Pros:
* Man kann eine neue Funktionalität und deren Listeneintrag
dazunehmen/entfernen, indem man die Datei Dazulinkt oder auch nicht.
Cons:
* Übler hack
* GCC-Spezifisch, andere noch nicht getestet
Viele dank für die Antworten,
der Vorschlag von Daniel ist sicher ganz nett aber für mich jetzt
übertrieben da ich mich erst tiefer einarbeiten muss. Und ich froh bin
das mein Makefile jetzt endlich mal so funktioniert. Und das Menü ist
auch nicht sehr groß.
1
structMenuEntryMainMenu[]=
2
{
3
{"Auto Search",AutoSearch,{0,1},NULL,0},
4
{"Band Scanner",BandScanner,{0,0},NULL,0},
5
{"Manual Mode",ManualMode,{0,0},NULL,0},
6
{"Settings",drawMenu,{0,0},NULL,0},
7
{"Exit",exit,{1,0},NULL,0},
8
9
};
10
11
structMenuEntrySettings[]=
12
{
13
{"Batterie BEEPS",AutoSearch,{0,0},NULL,0},
14
{"Calibrate RSSI",BandScanner,{0,0},NULL,0},
15
{"Video Input",ManualMode,{0,0},NULL,0},
16
{"Back",drawMenu,{1,0},NULL,0},
17
};
18
19
voidMenuRefresh(structMenuEntry*Menu)
20
{
21
osd.clearScreen();
22
// find activ menu item, if not set the first item...
Aktuell ist dass der Stand der Dinge, es gibt jetzt noch ein Struct in
dem ich flags setzen und lesen kann.
Das Menü lässt sich mit einem Button bedienen sprich einmal drücken ->
MenuNext, lang drücken bedeutet Aktion ausführen MenuAction. Bin ich am
Ende springe ich wieder hoch.
Eins steht noch an und zwar wie übergebe ich MenuRefresh ein neues
SubMenu bzw woher hole ich mir am besten die infos welches subMenu
aufgerufen werden soll.
Für Anregungen bin ich natürlich offen . Gruß
D. C. schrieb:> Ich hätte mir das mit dem verschachteln so gedacht das ich meiner> Funktion DoMenu einfach bei Settings(nächstes Submenü) nach der Aktion> ein neues MenuEntry übergebe mit dem Aufbau des Submenüs von Settings.
Nun ja, ist heut schon spät.. trotzdem:
Du denkst immer noch zu wirr.
Also: ein char* kostet im Menü nur einen Zeiger und dafür können die
Texte beliebig lang sein, denn sie werden ja außerhalb des Menüarrays
gespeichert. Das entspannt die Speichersituation.
Dann: Packe alle Menüs und Untermenüs und Unter..Unter.. in ein und
dasselbe Array. Ein jedes hat ein erstes und ein letztes Element und
diese beiden begrenzen all das, was du darstellen willst. Für das
Aufrufen eines Submenüs brauchst du bloß den aktuellen Index neu zu
setzen, ggf. unter Einkellern eines Rückkehr-Index. Alternative: eine
Stelle im Submenü, die dediziert heraus in die nächsthöhere Ebene führt
oder implizit nach Abarbeiten der aktuellen Aktion.
W.S.
Ja diese Lösung hatte ich schon, aber es muss doch auch geordneter gehen
. Ich hab jetzt an soetwas gedacht ->
1
structMenuEntrySettings[]=
2
{
3
{"Batterie BEEPS",AutoSearch,{0,0},NULL,0},
4
{"Calibrate RSSI",BandScanner,{0,0},NULL,0},
5
{"Video Input",ManualMode,{0,0},NULL,0},
6
{"Back",drawMenu,{1,0},MainMenu,0},
7
};
8
9
structMenuEntryMainMenu[]=
10
{
11
{"Auto Search",AutoSearch,{0,1},NULL,0},
12
{"Band Scanner",BandScanner,{0,0},NULL,0},
13
{"Manual Mode",ManualMode,{0,0},NULL,0},
14
{"Settings",drawMenu,{0,0},Settings,0},
15
{"Exit",exit,{1,0},NULL,0},
16
17
};
drawMenu wäre eine Funktion die den Pointer auf Settings übergeben
bekommt und einer Variable die alle Menüs halten kann übergeben wird
also vom typ MenuEntry. Nach der Übergebe wird einfach MenuRefresh
wieder aufgerufen.
Das war jetzt nur ein Gedanke....
Gruß