Skocz do zawartości

Uniwersalna biblioteka obsługi menu wielopoziomowego - jak to ugryźć?


_LM_

Pomocna odpowiedź

1 godzinę temu, _LM_ napisał:

zastanawiam się czy nie lepiej byłoby zamiast tego indeksować pozycję menu przez zwykłego uin16_t

Rozumiem, że ograniczasz się do AVR-ów, bo przecież na całym świecie wskaźniki na tekst mają szesnaście bitów?

Jeśli już to:

#ifdef _AVR_
  typedef int16_t menu_displayItem;
#else
  // reszta świata
  typedef void *menu_display_tem;
#endif

chociaż ja specjalnie zostawiłem menu_displayItem jako typ określany przez użytkownika.

W ten sposób np. możesz zrobić coś w stylu:
 

typedef char * menu_displayItem;
...
// w inicjalizacji:
.display = PSTR("cośtam")

(dobra, ja właśnie z pociągu wysiadłem, 150 minut opóźnienia na starcie, kurde jeszcze chyba jadę, jak mi się przestanie wydawać że monitor to okno w wagonie to może coś dopiszę).

 

 

Link do komentarza
Share on other sites

Przed chwilą, ethanak napisał:

Rozumiem, że ograniczasz się do AVR-ów, bo przecież na całym świecie wskaźniki na tekst mają szesnaście bitów?

Skąd taki pomysł? od początku było mówione że libs ma działać na każdym mikrokontrolerze dla którego się skompilować kod w c. Kombinuję aby pozbyć się tego wskaźnika ze struktury, a zamiast niego indeksować kolejne pola i na podstawie zwróconego indeksu wykonać akcję. Niech sobie użytkownik określi co chce zrobić z daną pozycją

Link do komentarza
Share on other sites

7 minut temu, ethanak napisał:

BTW dlaczego chcesz się pozbyć wskaźnika? Możesz to jakoś uzasadnić, bo nie rozumiem jaki jest cel tej operacji.

Na etapie budowania struktury, po to że nie zawsze będziesz chciał działać na wyświetlaczu. Poza tym dodanie wskaźnika na tekst, czy nawet typ nieokreślony(pusty) z góry wymusza jego podstawienie na etapie budowania szkieletu menu

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

11 minut temu, _LM_ napisał:

nie zawsze będziesz chciał działać na wyświetlaczu.

Oczywiście, gdzieś tam czasami uzywam TTS 😉

12 minut temu, _LM_ napisał:

Poza tym dodanie wskaźnika na tekst,

Ale dlaczego wskaźnika na tekst... uczepiłeś się tego tekstu.

Chodzi o to, żeby stworzyć uniwersalną strukturę i uniwersalne API. Jeśli w strukturze masz menu_displayItem, to możesz sobie zadeklarować że to int16_t albo zgoła uint8_t, w innym przypadki będzie to wskaźnik na tekst, a w jeszcze innym wskaźnik na strukturę (lub nawet bezpośrednio struktura) zawierająca bitmapę od ikonki, markup text, kolorki i parametry syntezy mowy.

Jeśli masz int16 (czemu nie uint, czyżby indeksy miałyby być ujemne/) to masz drugą tablicę, która musi być synchroniczna z aktualnym menu. Jeśli tworzy to automat to jeszcze ujdzie, ale jeśli będziesz chciał to robić ręcznie - gwarantuję, że po siódmej poprawce ktoś kto by Ciebie przypadkowo podsłuchał mógłby nauczyć się kilku nowych nieprzyzwoitych słów 🙂

 

 

 

Link do komentarza
Share on other sites

2 minuty temu, ethanak napisał:

Jeśli masz int16 (czemu nie uint, czyżby indeksy miałyby być ujemne/) to masz drugą tablicę, która musi być synchroniczna z aktualnym menu.

Bo już zmęczony jesteś 😉 tam jest uint16_t, a owszem zdaję sobie sprawę że będzie trzeba drugiej tablicy wykonawczej. 

4 minuty temu, ethanak napisał:

le jeśli będziesz chciał to robić ręcznie - gwarantuję, że po siódmej poprawce ktoś kto by Ciebie przypadkowo podsłuchał mógłby nauczyć się kilku nowych nieprzyzwoitych słów 🙂

hah no czasem miałby czego 😉 Oki jeszcze to przemyślę 

Link do komentarza
Share on other sites

@_LM_ Napisałeś na początku, że ma to być uniwersalna biblioteka do menu, a potem jest jakiś fragment o wyświetlaczach. Jeśli to ma być uniwersalne, to dlaczego nie może to być "gadacz" odczytujący mp3, holograficzny wyświetlacz laserowy albo cokolwiek innego?

Moim zdaniem, jeśli to ma być uniwersalne, to całą prezentację pozostawić należy użytkownikowi i pozwolić mu zdefiniować funkcję w rodzaju "wyświetl_pozycję_menu(id_pozycji)", którą biblioteka będzie wywoływać, a już użytkownik będzie wiedział na czym i jak ma to być wyświetlone.

Kolejną niezbędną funkcją będzie funkcja typu "przesuń_o_n_pozycji(kierunek, liczba_pozycji)", gdzie kierunek będzie jednym z wyborów góra, dół, lewo, prawo... . Jest tutaj już ograniczenie co do struktury menu, jeśli miała by być bardziej rozbudowana, to oczywiście liczba "kierunków" zwiększy się. Ta funkcja będzie oczywiście wywoływana przez użytkownika biblioteki.

Jeśli chcesz dodać "pozycje specjalne" wybierane jednym klawiszem, (bo nie zakładasz, czym obsługujemy wybór z menu - pełna klawiatura też jest dopuszczalna) to trzeba udostępnić użytkownikowi funkcję w rodzaju "przesuń_do_pozycji_specjalnej(id_pozycji_specjalnej)"

Oczywiście, aby to wszystko było do zaakceptowania dla użytkownika to musisz dołożyć przykładową realizację na jakimś konkretnym medium (albo kilku) - pewnie tam będą znaczne ograniczenia, np "mamy tylko dwa klawisze sterujące menu i jeden klawisz akceptacji", lub "mamy pięć klawiszy".

Moim zdaniem, jeśli to menu ma być uniwersalne, to trzeba zrobić jedną bibliotekę zajmującą się przemieszczaniem się po menu, serię przykładowych bibliotek wejścia i serię przykładowych bibliotek prezentacji (na początku po jednej). W przeciwnym przypadku rozrośnie się to tak, że nigdzie się nie zmieści.

Przydał by się jeszcze moduł do weryfikacji poprawności definicji menu podanej przez użytkownika - wykrywający na przykład nieosiągalne pozycje, ale to dopiero po utworzeniu działającego modelu.

Wydaje mi się, podkreślam, to jest moja opinia, że dopiero kiedy będziesz miał zdefiniowane zachowanie tej biblioteki i przynajmniej jej podstawowy interface, warto zająć się takimi szczegółami  jak język w którym piszesz, struktury danych które stosujesz, warianty zależne od wyboru sprzętu itd itp.

Być może kiedyś w przyszłości - bo zakładam że tak przydatne narzędzie będzie rozwijane - dojdziesz do wniosku, że trzeba będzie to trochę inaczej zrobić dla różnych kontrolerów czy komputerów, ale to już może być sprawa np kompilacji warunkowej i nie ma się teraz co tym przejmować. Optymalizacją zajmiemy się jak już będzie działało.

Zastanów się też nad nad możliwymi wariantami współdziałania menu z efektorami (tak się to chyba nazywa): klawisze, potencjometry... i jak to przekazać do procedury głównej aby działało.

Mnie osobiście Twój pomysł napisania uniwersalnej biblioteki do menu bardzo się podoba i chyba dlatego się tak rozpisałem.

Pozdrawiam i życzę wesołych świąt i szczęśliwego nowego roku.

                                       Bogusław

PS Tak, wiem, że wszystko to prowadzi to do automatu skończenie stanowego, ale jestem przekonany, że robiąc swoją bibliotekę uważnie i  w sposób przemyślany sam dojdziesz do rozwiązania, które okaże się właśnie takim automatem, a jeśli się mylę i wyjdzie Ci coś lepszego to tym lepiej!

                                       B

PS2 Jeśli to ma być uniwersalne, to dlaczego ograniczać się tylko do mikrokontrolerów, to może to być przydatne np dla Raspberry Pi.

                                       B

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

1 godzinę temu, bjrk napisał:

Moim zdaniem, jeśli to ma być uniwersalne, to całą prezentację pozostawić należy użytkownikowi i pozwolić mu zdefiniować funkcję w rodzaju "wyświetl_pozycję_menu(id_pozycji)", którą biblioteka będzie wywoływać, a już użytkownik będzie wiedział na czym i jak ma to być wyświetlone.

private:
    static inline std::vector<menu_t*> path{}; // Represents current menu path
    inline static int current_option_index = 0; // Current option
    inline static int current_render_index = 0; // Current option to render as first
    inline static int rendered_options_count = 2; // How many options to render
public:
    static void open_menu(menu_t* menu); // Open specified menu
    static void go_back(int amount = 1); // Go back to previous menu
    static void next(); // Go to next option
    static void previous(); // Go to previous option
    static void refresh(); // Refresh current menu
    static void execute(action_type_t type, i_action_metadata* metadata); // Execute option's action

};

😉 Moja wersja jest jeszcze trochę WIP, ale mam całkiem przyjemną opcję:

class menu_option_t {
    private:
        std::vector<action_t*> listeners = {};
        i_renderer* renderer;
    public:
        menu_option_t* add_listener(action_t* listener);
        void execute(action_type_t actionType, i_action_metadata* metadata);
        explicit menu_option_t(i_renderer* rendererPtr);
        explicit menu_option_t(const char* text);
        void render(bool selected);
};

Która potrafi zrobić magiczne rzeczy:

class i_renderer {
public:
    virtual void render(bool selected) = 0; // Render this display item
};

I dzięki temu zyskuje magiczną możliwość zrobienia dziedziczności:

void printf_text_renderer_t::render(bool selected) {

    // Draw selection mark
    if(selected) printf("> ");
    else printf("  ");

    // Draw option name
    printf("%s", text);
}

printf_text_renderer_t::printf_text_renderer_t(const char *text) {
    this->text = (char*) text;
}

Tylko ja niestety pracuję w C++, bo C jest zbyt drewniane... No i bardzo mocno oszczędzam RAM -.- (sarkazm). Mam 264kB RAM'u na coś trzeba go zużyć, zwłaszcza, że większość projektów i tak jest banalna typu potencjometr, enkoder, sterowanie PWM czy silnikami krokowymi do czego RAM jest zbędny... 😄Niech więc posłuży do wyświetlania... Po coś mam te dwa rdzenie 😉 

GITHUB: https://github.com/H1M4W4R1/MCU_Menu_Library/tree/master

Link do komentarza
Share on other sites

8 godzin temu, H1M4W4R1 napisał:

pozwolić mu zdefiniować funkcję w rodzaju "wyświetl_pozycję_menu(id_pozycji)"

Poszedłbym jeszcze dalej: funkcja w rodzaju "wyświetl aktualne menu(menu)".

Jako że w zmiennej "menu" powinno siedzieć zarówno całe menu, jak i wskaźnik (lub id) bieżącej pozycji, ew. jakieś dodatkowe dane, funkcja jest bardziej uniwersalna.

Weźmy przykład, kiedy dane menu ma 6 pozycji, a na wyświetlaczu mamy cztery linie. Po to, żeby menu nie jeździło bez potrzeby na wyświetlaczu, trzeba zapamiętać poprzednio wyświetlaną pozycję, jak i pozycję pierwszego elementu na wyświetlaczu. Jeśli przekażesz do funkcji wyłącznie id - będzie sporo kombinowania. Jeśli całą strukturę menu - możesz zrobić wszystko co chcesz.

A co do zawartości tego co się wyświetla (czyli zawartość displayItem)...

Wyobraźmy sobie maszynę wyposażoną w główny panel sterowania z jakimś fikuśnym wyświetlaczem (typu 480x640 kolorowy). Do tego mamy mały panelik przy samej maszynie (używany w pracach serwisowych, jakiś LCD 4x20). Oba wyświetlacze w przypadku pracy z menu muszą wyświetlać tę samą informację, ale w różny sposób (mały LCD wyświetla tylko tekst i jakąś strzałeczkę pokazującą gdzie jesteś); duży może pokazać owo menu używając ikony i tekstu, jeśli jest trochę większy można wyświetlić nad menu (albo pod, albo obok) większy obrazek pokazujący w sposób więcej artystyczny na jakiej to pozycji menu jesteśmy, a jeśli wyświetlacz główny jest jeszcze większy to można wyświetlić nawet rozwinięte wielopoziomowe menu (jak w aplikacjach pecetowych).

Ogólnie zaczynam dostrzegać tu pewne niezrozumienie idei biblioteki.

Biblioteka ma za zadanie unifikację poruszania się po grafie menu, przyjmując polecenia z jakiejś abstrakcyjnej biblioteki obsługi wejść (oczywiście najpopularniejsze - klawiatura, enkoder - powinny być dołączone do biblioteki, ale nie być jej nieodzowną częścią). Takoż musi wydać dyspozycje co do wyświetlenia aktualnego stanu przez równie abstrakcyjną funkcję (i jak poprzednio, najpopularniejsze - np. LCD - powinny być dołączone do biblioteki na zasadzie umożliwienia, a nie nakazu stosowania).

Mając (przynajmniej w zarysie) taką bibliotekę można skupić się nad możliwymi funkcjami wejścia/wyjścia czy sposobem łatwego deklarowania struktury menu (nie, tablica funkcji nie jest łatwym sposobem bo wymaga operowania jakimiś indeksami które same w sobie nic nie znaczą - tzn. pisząc coś trzeba wiedzieć że 11 to "wyłącz ekstruder", a jak się będzie chciało dodać jakąś dodatkową funkcję między pozycją 3 i 4 to trzeba całe menu przejechać). Ale do działania samej biblioteki wystarczy jakiś getchar/getch na wejściu i printf na wyjściu.

Inaczej: chodzi mi o maksymalne odizolowanie warstwy logicznej (poruszanie się po menu, wywołanie funkcji egzekutora, zmiana stanu checkbox/radio itp) od fizycznej (klawisze, wyświetlacze i takie tam elektryczne zabawki).

No - chyba że uważacie że takie podejście jest błędne, to mówcie od razu, mam co robić i nie będę się wtrącać 🙂

 

 

  • Lubię! 1
Link do komentarza
Share on other sites

@H1M4W4R1 Zacytowałeś mnie, ale niestety nie rozumiem, czy się zgadzasz z moją tezą, czy nie.

@ethanak Użytkownik musi podać bibliotece swoje menu więc już je zna. Ponadto wewnątrz biblioteki nie powinno się umieszczać ani przetwarzać tekstu, dźwięku błysków itd. Jeśli menu ma być prezentowane jednocześnie na kilku istotnie różnych wyświetlaczach, a na dodatek odczytywane przez megafon i nagrywane na magnetofonie, to potrzebne jest jedynie zidentyfikowanie wybranej do prezentacji pozycji. Jak napisałeś, "chodzi o maksymalne odizolowanie klasy abstrakcji". Nie jestem pewien, czy udostępnienie użytkownikowi wewnętrznej struktury jest tutaj konieczne, tym bardziej, że może ona być w jakiś dziwny sposób optymalizowana i przez to niezrozumiała dla "prostego użytkownika". Nawet nie jestem przekonany czy uruchomienie wybranej pozycji powinno się znaleźć wewnątrz podstawowej warstwy abstrakcji. Chodzi mi o sytuację gdy siedzę nad menu w restauracji i wybieram potrawę, a wezwanie kelnera to osobna czynność. Może to być na przykład sytuacja, gdy menu jest sterowane czterema klawiszami (cofnij, góra, dół, rozwiń) a wybranie przypisanej do pozycji operacji odbywa się osobnym klawiszem.

Przy okazji przyszło mi do głowy, że można zrobić menu wielokrotnego wyboru.

Dlatego w osobnej warstwie widział bym pomocnicze biblioteczki pomagające użytkownikowi załadowanie wewnętrznej struktury. W najprostszym przypadku może to być wczytanie tekstowego menu, na przykład wielopoziomowe menu z wcięciami, które było by przetłumaczone na wewnętrzną strukturę. Te biblioteczki drugiego poziomu winny mieć bardzo czytelny i prosty - może nawet prymitywny - interface.

Do wprowadzenia wielopoziomowego menu tekstowego sterowanego pięcioma klawiszami wystarczy wielokrotne wywołanie funkcji w rodzaju pozycja_menu(lp, poziom, nr_na_poziomie, tekst) , albo co lepiej tabela, ale to już dla kogoś kto o tablicach słyszał.

Link do komentarza
Share on other sites

1 minutę temu, bjrk napisał:

siedzę nad menu w restauracji i wybieram potrawę, a wezwanie kelnera to osobna czynność

O właśnie. Siedzisz nad menu w restauracji, naciskasz guzik "zawołaj kelnera' i nie interesuje Cię to, czy tam dzwoni dzwoneczek, zapala się żaróweczka czy może krasnoludek odbiera SMS-a i kopie kelnera w d... 🙂

 

  • Lubię! 1
Link do komentarza
Share on other sites

1 minutę temu, bjrk napisał:

I cały czas właśnie to  mówię

Nie. Traktujesz wezwanie kelnera jako osobną czynność, a tymczasem jest to jedna ze standardowych funkcji biblioteki (takie wciśnięcie "enter" na menu).

Osobną funkcją jest reakcja na owego entera (czyli ta nieszczęsna funkcja "menu_Display") i ja właśnie o tym mówię 🙂

Kucharza układającego menu nie interesuje techniczna realizacja wzywania kelnera.

Link do komentarza
Share on other sites

@ethanak @bjrk @H1M4W4R1 Dziękuję za uwagi. Dwa słowa wyjaśnienia dlaczego "uparłem" się na tekst, a to dlatego że na tym etapie łatwiej mi testować swoje pomysły, dlatego też pytałem czy nie lepiej jest mieć funkcyjkę pomocniczą która będzie zwracała aktualną pozycję widocznego menu czyli jego indeks, następnie musi być jakaś tablica która umożliwi translację danej pozycji na to czego użytkownik oczekuje.

W kwestii wejścia, po wysłuchaniu opinii doszedłem do wniosku że niema co mnożyć bytów, niech user ma jedną funkcję która jako argument przyjmuje wartość z enuma, na podstawie tej wartości nastąpiłaby nawigacja po menu. Tu właśnie jest ten styk pomiędzy wartswą abstrakcyjną a sprzętową, gdyż nie sposób określić co będzie wpływało na wybór pozycji.

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.