Einen wunderschönen guten Abend,
ich möchte gerne durch ein Menü scrollen ( dieses besteht aus einer
Struktur ).
Es funktioniert zwar, ich finde es jedoch nicht elegant gelöst. Hat
jemand eine bessere Lösung wie man solch ein Menü "durch rollen" kann?!
1
/*!<-- TESTING BETA <--*/
2
uint8_tOpenMenueBETA(sLcdMenue_t*psMenue)
3
{
4
uint8_tuiActualMenuePos=0;
5
uint8_tuiSubFncRet=0;
6
7
do
8
{
9
switch(EncoderGetRotation())
10
{
11
caseENC_ROTATION_LEFT:
12
{
13
if(uiActualMenuePos>eLCD_MENUE_START)
14
{
15
uiActualMenuePos--;
16
}
17
}break;
18
19
caseENC_ROTATION_RIGHT:
20
{
21
if(uiActualMenuePos<(__eLCD_MAX_ENTRYS__-1))
22
{
23
uiActualMenuePos++;
24
}
25
}break;
26
}
27
28
/*!<
29
*
30
* psMenue[0..6] // Hier befinden sich die Namen für die Menues
Anja F. schrieb:
1.) Es ist sicher praktisch ein paar Eckdaten zur Hardware anzugeben.
2.) der Code ist (fuer mich) schwer zu lesen.
Warum ...
> uiActualMenuePos
... und nicht einfach "pos" (im Menuecode - was sonst), usw.?
Die ungarische Notation, gross/klein, lange reduntante Namen, usw.
machen keinen Sinn und helfen nicht, den Code schnell zu erfassen - im
Gegenteil.
leo
Anja F. schrieb:> Einen wunderschönen guten Abend,
Beim Versuch dir zu helfen bin ich leider schon an deinen
Englischkentnissen (die offensichtlich nicht sonderlich gut
sind) hängengeblieben.
Dein Verständnis vom englischen "actual" ist offensichtlich
"aktuell".
Die allgemeine englischsprachige Auffassung von "actual"
ist dagegen "tatsächlich".
Wer "aktuell" im Englischen meinen möchte der schreibt "current".
leo schrieb:> Anja F. schrieb:> 1.) Es ist sicher praktisch ein paar Eckdaten zur Hardware anzugeben.>> 2.) der Code ist (fuer mich) schwer zu lesen.>> Warum ...>>> uiActualMenuePos>> ... und nicht einfach "pos" (im Menuecode - was sonst), usw.?> leo
Mit den Namen habe ich es nicht so.. Manchmal schreibe ich da auch etwas
sehr umgeschriebenes hin.. Das muss sich bessern, stimmt.
Sprach Geleerter schrieb:> Beim Versuch dir zu helfen bin ich leider schon an deinen> Englischkentnissen (die offensichtlich nicht sonderlich gut> sind) hängengeblieben.
Konzentriere dich doch erst einmal auf die deutsche Interpunktion, bevor
du Kleinlichkeiten äußerst.
"derzeitig" finde ich auch andere Variationen
https://www.dict.cc/?s=actual
Anja F. schrieb:> char *ui = { "\0\0" };
Die Initialisierung mit "\0\0" halte ich für "unschön". Ich war erstmal
erstaunt, dass hiermit tatsächlich drei Bytes reserviert und
initialisiert werden, weil das erste '\0' ja den Initialisierungsstring
schon terminiert.
Aber das scheint tatsächlich zu funktionieren, wie ich nach einigen
Tests (bzgl. Stack) herausgefunden habe.
Allerdings geht dann das nicht in Ordnung:
> ui[0] = uiActualMenuePos + '0';
Hier schreibst Du in eine Zeichenkonstante.
Unter Linux erzeugt dies reproduzierbar einen Segmentation fault:
1
voidmyfunc(void)
2
{
3
char*ui="\0\0";
4
5
ui[0]='0';// hier knallts
6
7
printf("%02x %02x\n",ui[0],ui[1]);
8
}
Um sicherzustellen, dass Dein String ui tatsächlich terminiert und
beschreibbar ist, schreibe besser:
1
charui[2];
2
ui[0]=uiActualMenuePos+'0';
3
ui[1]='\0';
Beachte, dass dies nur für einstellige Werte funktioniert. Bei
Menüpositionen größer 9 bekommst Du hier ein Problem.
Anja F. schrieb:> if ( uiActualMenuePos+1 < (_eLCD_MAX_ENTRYS_) )> {> ...> }> else> {> LcdFillString( "" , 0 , 1 );> uiActualMenuePos = 5 ;> }
Im switch oberhalb dieses Blockes stellst Du sicher, dass
uiActualMenuePos niemals über die Anzahl der Menüeinträge hinausgehen
kann - also immer kleiner ist.
Was soll dann dieses else? Angst vor eigenem Programmierfehler?
Eigentlich kann das weg. Wenn Du es doch behalten möchtest, dann
schreibe
statt:
1
uiActualMenuePos=5;
besser
1
uiActualMenuePos=__eLCD_MAX_ENTRYS__-1;
Apropos: Diese Konstante mit den beiden führenden Unterstrichen, kommt
diese aus der verwendeten LCD-Bibliothek oder wurde diese von Dir
definiert? Wenn letzteres, lass den Quatsch mit den führenden
Unterstrichen, denn solche Konstanten sind für den Compiler und dessen
Laufzeitumgebung reserviert. Nenne diese Konstante besser
LCD_MAX_ENTRYS.
Noch eine Frage:
> if ( uiActualMenuePos+1 < (_eLCD_MAX_ENTRYS_) )
Warum klammerst Du _eLCD_MAX_ENTRYS_ ?
Muss das so sein, weil sich da eventuell ein komplexer Ausdruck dahinter
verbirgt? Wenn ja: Mach die Klammerung im #define, nicht hier.
Letzte Frage:
> if ( psMenue[uiActualMenuePos+1].pfnc != NULL )
Wieso +1 ? Lässt Du psMenu ab Index 1 loslaufen? Ist denn auch
sichergestellt, dass die Größe des Arrays auch tatsächlich um 1 größer
ist als tatsächlich benötigt?
In C laufen die Indizes über Arrays normalerweise von 0 bis N-1, wobei N
die Größe des Arrays ist.
leo schrieb:> Anja F. schrieb:> 1.) Es ist sicher praktisch ein paar Eckdaten zur Hardware anzugeben.>> 2.) der Code ist (fuer mich) schwer zu lesen.>> Warum ...>>> uiActualMenuePos>> ... und nicht einfach "pos" (im Menuecode - was sonst), usw.?>> Die ungarische Notation, gross/klein, lange reduntante Namen, usw.> machen keinen Sinn und helfen nicht, den Code schnell zu erfassen - im> Gegenteil.>> leo
Sehe ich genau umgekehrt.
Ich finde uiActualMenuePos besser als nur pos.
pos kann z.B. auch positiv bedeutet. Dann hätte man unbeknnten Code und
muss noch um die Abkürzungen rätseln??
Ne Ne: uiActualMenuePos ist voll OK.
Naja, das kann man schon so machen. Blockt halt alles. Kann jenachdem
auch so gewünscht sein.
Oft find ich es auch schöner, keine blockierenden funktionen zu haben.
Man kann statdessen eigentlich immer den Code auf asynchrone Modelle
abändern, aber dann wird das memory management & hier noch das Stacking
komplizierter, weil man dann nicht in ner Funktion mit Speicher auf dem
Stack bleibt. Hier wäre es schön, wenn C co-routinen hätte.
Wie auch immer, um sowas asynchron zu machen, erstmal nur einen
Eventloop erstellen:
1
intmain(){
2
// initialise everything
3
input_init();
4
ui_init();
5
6
while(1){
7
// Different short tasks
8
input_check_for_input();
9
foo_do_stuff();
10
bar_do_stuff();
11
...
12
}
13
}
Dann kann man mit event listenern / callbacks arbeiten. Beispiel:
Gibt sicher auch andere Methoden. Das wichtige ist, so ist der Input
entkoppelt. Nachteil ist, die push/pop aufrufe immer richtig zu machen
ist praktisch unmachbar. Eventuell irgend ne referenz mitgeben, und was
bauen was soviele pops machen kann, bis es bei dem ui element ankommt.
Aber eben, das ist nur wie ich sowas machen würde, andere würden dass
vermutlich anders machen.
Die Position die durch den Encoder verändert wird sollte Gesamtanzahl
Menuepunkte UND darstellbare Zeilen des Displays berücksichtigen.
Beispiel:
10 Menupunkte, 4 Zeilen darstellbar
uiActualMenuePos max = 10-4+1=7 = Zeile 7,8,9,10 werden angezeigt.
Blockiert EncoderGetRotation() bis sich etwas tut oder liefert das Ding
sofort Up/Down/Nix zurück?
Wenn Nix (default) zurück kommt brauchst Du nix am Display machen
1
if( Up || Down )
2
{
3
// LCD updaten
4
5
}
und
1
if ( psMenue[uiActualMenuePos+1].pfnc != NULL )
2
{
3
psMenue[uiActualMenuePos+1].pfnc();
4
}
Abfrage auf != NULL ist überflüssig
Es reicht schlicht sowas.
Anja F. schrieb:> Es funktioniert zwar, ich finde es jedoch nicht elegant gelöst. Hat> jemand eine bessere Lösung wie man solch ein Menü "durch rollen" kann?!> /*!<-- TESTING BETA <--*/> uint8_t OpenMenueBETA( sLcdMenue_t *psMenue )> {....> do> {> switch ( EncoderGetRotation() )
Mich graust so etwas.
Also mal ganz vom Anfang her:
Du hast also nen µC, der irgendwas tun soll. Er hat ein Display, wo du
diverse Dinge sehen kannst und unter diesen ist auch sowas wie ein
"Menü". Aber dort (s.o.) gehst du rein prozedural vor wie der Elefant im
Porzellanladen mit
do { switch (Encoder GET Rotation() ) .... } while...?
Was soll deiner Ansicht nach passieren mit all den anderen Dingen, die
da angezeigt werden? Und was passiert, wenn deine Encoder-Get-Funktion
mal bloß mit "hat keiner dran gedreht" zurück kommt? Du blockierst dich
mit so einem Konstrukt an jeder Stelle selber.
Mach es anders. Zuerst trenne das Ermitteln einer Aktion an deinen
Knöpfen ab vom Rest der Firmware und packe dessen Ergebnisse in Events,
die du in einen Ringpuffer schreibst. Dann schreibe dein Menüsystem
ereignisgestuert und nichtblockierend.
Also etwa so:
in main:
für immer
{ if (GetEvent())
Event = LiesEvent();
if (Event.Typ == MenüEvent) Menue(Event);
if (Event.Typ == Tralala) Tralala(Event);
.. etc.
ErledigeSonstigesZeugs();
}
und
void Menue (TEvent Event)
{ if (Event.Was == Links) MacheLinks();
if (Event.Was == Rechts) MacheRechts();
}
kurzum: nichtblockierend.
W.S.
Sprach Geleerter schrieb:> Die allgemeine englischsprachige Auffassung von "actual"> ist dagegen "tatsächlich".
Außer dir ist das allerdings jedem inkl. dem Compiler vollkommen egal.
Thomas E. schrieb:> Außer dir ist das allerdings jedem inkl. dem Compiler vollkommen egal.
Nö. Und spätestens wenn Du es mal mit Code zu tun bekommst, der nicht in
Denglisch geschrieben ist, sondern im falschen Englisch einer anderen
Sprache, wirst Du auch merken wieso.
Frank M. schrieb:> Nenne diese Konstante besser LCD_MAX_ENTRYS.
Ich empfehle ja "LCD_MAX_ENTRIES", dann wäre das auch korrekt.
Thomas E. schrieb:> Außer dir ist das allerdings jedem inkl. dem Compiler vollkommen egal.
Nein. Ich sehe das Ergebnis täglich auf Arbeit, mit Code von Chinesen
und Japanern. Sowohl Namen als auch Kommentare - sofern vorhanden, weil
noch mehr englische Wörter - sind oft genug nicht klar oder
missverständlich. Bei komplexen Systemen ist das absolut nicht egal.
Wenn es am Englisch scheitert, dann sollte man besser bei Deutsch
bleiben. Mir ist ein fremdsprachlicher Vortrag (übersetzt/untertitelt)
lieber als ein Vortrag, der durch die mangelhafte Sprachkenntnis des
Vortragenden unbrauchbar wird.
GL schrieb:> Daumen hoch für ungarische Notation. Man kommt rein und weiß> anhand des Namens direkt worum es sich handelt.
Daumen runter für ungarische Notation. Man kommt rein und glaubt anhand
des Namens direkt zu wissen, um was es sich handelt. Dann prüft man zwei
Tage später nach und stellt fest, dass das in einer früheren Version des
Codes tatsächlich mal so war.
S. R. schrieb:> Dann prüft man zwei> Tage später nach und stellt fest, dass das in einer früheren Version des> Codes tatsächlich mal so war.
Das hätte dann aber beim Codereview angeprangert werden sollen und
anschliessend korrigiert. ;-)
Die ungarische Notation hilft eigentlich nur für Variablen mit skalaren
Werten; sobald Structs, Arrays, Pointer, usw ins Spiel kommen wird's
lustig:
strName --> ist das ein STRing oder ein STRuct?
psAdresse --> Pointer auf String oder Struct?
acNummer --> Array von Char, also ein String? Aber warum heißt die
Variable dann nicht sNummer oder strNummer?
Eric B. schrieb:> Das hätte dann aber beim Codereview angeprangert werden> sollen und anschliessend korrigiert. ;-)
Wenn ich mir unsere Patches so anschaue, dann sind die in möglichst
minimalinvasiv, um Konflikte zu verhindern. Eine Veränderung, die
möglicherweise sämtliche Dateien im Projekt gleichzeitig anfasst, wäre
ein eigenes Refactoring-Projekt über ein Jahr. Sowas geht nicht spontan
durchs Review. ;-)
Eric B. schrieb:> Die ungarische Notation hilft eigentlich nur für Variablen mit skalaren> Werten;
Nein, sie hilft gar nicht. Bzw. Maximal in so Fällen, wo schmutzige
Tricks mit Überlauf nur bis 256 funktionieren. Aber z.b. hier: ob ich
pos mit ++ oder INCpos() erhöhen sollte, kann ich nicht am Typ ablesen.
Der Typ ist zwar Milliardär geworden und Weltraumtourist, hat uns
Windows beschert und vieles andere, aber das war ein Flop. Sowohl in
seiner Version als auch in der heute üblichen absurden wo der Typ und
nicht der Zweck kodiert wird.
Frank M. schrieb:> Um sicherzustellen, dass Dein String ui tatsächlich terminiert und> beschreibbar ist, schreibe besser: char ui[2];> ui[0] = uiActualMenuePos + '0';> ui[1] = '\0';
Das ist nicht nötig, der Ursprungspost kann leicht korrigiert werden:
Anja F. schrieb:> char *ui = { "\0\0" };
einfach ui[] statt *ui.
Aber statt der Operation am offenen Array einfach sofort auf größeren
Buffer und sprintf gehen. Alles andere ist hacken.