Форум РадиоКот https://radiokot.ru/forum/ |
|
FlexMenu - решение вопросов меню. Зацените. https://radiokot.ru/forum/viewtopic.php?f=57&t=170472 |
Страница 1 из 4 |
Автор: | ARV [ Сб май 02, 2020 10:33:54 ] |
Заголовок сообщения: | FlexMenu - решение вопросов меню. Зацените. |
Досамоизолировался я до изобретения велосипеда меню... В том или ином виде все это в моих проектах было и ранее, но теперь все объединил в одно целое. Вот как-то так оно работает На видео показаны основные фишки моего варианта: Скроллинг меню, если на дисплее оно целиком не вмещается. Выделение активного пункта в данном случае сделано "прочеркиванием" двумя линиями, но, естественно, можно сделать, как угодно.
- Наличие в меню пунктов со значениями параметров. Выделенный параметр сразу может редактироваться в заданных пределах. - Возможность вложенных меню. Кто работал с MicroMenu, тому подобный способ описания структуры меню будет знаком. Как видите, главное отличие в том, что пункты имеют разный размер, т.е. количество полей разное. Это позволяет экономить память в некоторых случаях. С другой стороны, добавилась возможность добавить новое поле, которое я назвал по-русски свойством, а по-английски property. Пункты меню, имеющие такое поле, позоляют видеть значение свойства и менять его интерактивно - на видео вы это видели. Меню с видео описывается в коде так: Код: // главное меню // id name parent prev next func|subm property MENU_CMD( mm_1, "BEEP", NONE, NONE, mm_2, beep); MENU_PROP(mm_2, "TEST", NONE, mm_1, mm_3, NULL, PROP_U8(u8, NOSTORE, 0, 12)); MENU_PROP(mm_3, "MOTOR", NONE, mm_2, mm_4, motor, PROP_BOOL(motor_state, NOSTORE, sf("STOP"), sf("RUN"))); MENU_SUB( mm_4, "SUBMENU", NONE, mm_3, mm_5, sm_1); MENU_PROP(mm_5, "TEMPERATURE", NONE, mm_4, mm_6, NULL, PROP_I16(vfr, NOSTORE, -20, 20)); MENU_CMD( mm_6, "LOAD", NONE, mm_5, mm_7, load); MENU_CMD( mm_7, "SAVE & EXIT", NONE, mm_6, NONE, save); // субменю MENU_CMD( sm_1, "STOP DEMO 1", mm_4, NONE, sm_2, done); MENU_CMD( sm_2, "STOP DEMO 2", mm_4, sm_1, sm_3, done); MENU_SUB( sm_3, "STOP SUB", mm_4, sm_2, NONE, ssm_1); // субменю 2 MENU_CMD(ssm_1, "SUB STOP DEMO 1", sm_3, NONE, ssm_2, done); MENU_CMD(ssm_2, "SUB STOP DEMO 2", sm_3, ssm_1, ssm_3, done); MENU_CMD(ssm_3, "SUB STOP DEMO 3", sm_3, ssm_2, NONE, done); Реализована поддержка следующих свойств:
- булево значение (т.е. логическое) - оно отображается парой соответствующих строк текста; - выбор одного варианта из нескольких - каждый вариант так же представляется соответствующим текстом. Относительно просто добавить поддержку и 32-битных чисел... Но надо ли? Вводить их значение при помощи кнопок "навигации" - это удовольствие не из приятных... При желании это можно реализовать по-старинке - написав свою функцию и назначив её нужному пункту меню... Таким образом, моя система меню сразу позволяет решить следующие задачи, практически всегда присутствующие в проектах на МК с ЖКИ:
- сохранять автоматически в EEPROM все свойства, используемые в меню, и загружать их оттуда. То есть теперь достаточно описать пункты меню вместе с соответствующими свойствами, выделить место под переменные для этих свойств (эти переменные затем использовать по назначению в программе), выделить место в EEPROM для сохранени значений этих свойств (все это указывается при создании свойства в пункте меню), и в нужный момент вызвать созданное меню на дисплей - все остальные заботы я уже решил! Думаю, многие знают, что не всегда просто решить, напрмер, интерактивную регулировку яркости подсветки дисплея. Часто делают так: в меню выбирают пункт, по активации которого выводится редактор яркости (то ли шкала, то ли число), а потом, когда ввод нового значения яркости завершен, работа с меню завершается, и задается новое значение яркости. Так вот, в моей системе яркость может меняться одновременно с изменением соответствующего свойства! На видео вы могли видеть, как включается и выключается "моторчик" - точно так же можно выполнять какую-то функцию при каждом изменении числа. То есть реализован полностью интерактивный способ изменения всех свойств! И прощайте странные цифры, обозначающие включение или отключение каких-то режимов - здравствуйте понятные слова! |
Автор: | WiseLord [ Сб май 02, 2020 13:47:40 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Да. тема реализации хорошего и универсального меню, похоже, вечная и велосипедистая. Для своего проекта тоже реализовывал что-то похожее (смотреть с момента 2:32). Тут можно глянуть на идеи реализации: menu.h и menu.c По сути меню у меня - массив MenuItem-ов, в каждом из которых есть ссылка на индекс родительского MenuItem-a, его тип и индекс ячейки, где хранится эта настройка). Самое муторное - это "красивый" вывод типа enum текстом, а не просто числами. В функциях ниже видно, как эти вещи "некрасиво" обрабатываются через switch ... case. Можно было бы усложнить структуру MenuItem, и вынести все эти min/max туда, но было уже лениво, да и свои ограничения в этом тоже есть. |
Автор: | ARV [ Сб май 02, 2020 14:18:16 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Ну, в принципе, у меня тот же подход, разве что от массива перешёл к связному списку. Хотя, мог бы и массивом... раньше, кстати, именно массивом делал. Главное, у меня все эти массивы и списки хранятся в памяти программ, что для AVR очень актуально. И более-менее возможным это стало с появлением полноценной поддержки указателей на память программ. |
Автор: | WiseLord [ Сб май 02, 2020 14:36:42 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
А что эта за новость про полноценные указатели на flash в AVR? А то я как-то последние пару лет AVR подзабросил и STM32 осваиваю. Раньше приходилось pgm_read_*() функциями пользоваться, а сейчас что-то изменилось? |
Автор: | ARV [ Сб май 02, 2020 17:26:37 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Вот именно что изменилось! Теперь можно сделать const __flash uint32_t *ptr; и потом спокойно разыменовывать его для чтения. Так что все эти pgm_read_xxxx можно, слава богу, забыть, как страшный сон. |
Автор: | NStorm [ Сб май 02, 2020 18:27:18 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
https://gcc.gnu.org/onlinedocs/gcc/Name ... paces.html Ну только это не пару лет назад изменилось, а лет 5-7 наверное назад. Но мало кто об этом слышал тогда. Один хрень остались нюансы. Функции stdlib'а (тот же printf) надо всё-равно вызывать отдельные, ибо они не из исходников берутся, а скомпилены с жесткой адресацией и соотв. для флэша копии инструкций есть с _P в названии. Но вот без pgmspace.h в основной программе уже можно обойтись, да. |
Автор: | Demiurg [ Вт май 05, 2020 13:07:22 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Я уже сам хотел создать отдельную тему. Итак, я тоже использую концепцию MicroMenu. Но мне не понравилось ни так как это сделано в статье easyelectronics (хотя и взял оттуда наименования, Parent, Child), ни в оригинале на AVRFreaks. Уже не важно, кто и как реализовал саму структуру. У каждого свой подход, свои потребности. Я остановился на такой реализации для символьного дисплея: menu.h СпойлерКод: //================== #ifndef MENU_H #define MENU_H #include "menu.h" #include "main_def_func.h" //================== //================== // Typedefs: typedef void (*FuncPtr)(void); //================== //================== typedef struct menu_item { void *Parent; void *Child; void *Next; void *Prev; FuncPtr EnterFunc; FuncPtr PlusFunc; FuncPtr MinusFunc; FuncPtr MenuInitFunc; char __flash *Rus_Text; char __flash *Angl_Text; } menu_item; //================== // Externs: //================== extern menu_item __flash *CurrMenuItem; // Текущий пункт меню. extern menu_item __flash Null_Menu; //================== // Defines and Macros: //================== #define NULL_ENTRY Null_Menu #define NULL_FUNC (void*)0 #define NULL_TEXT "" #define PAGE_MENU 3 //================== //================== #define MAKE_MENU(Name, Parent, Child, Next, Prev, EnterFunc, PlusFunc, MinusFunc, MenuInitFunc, Rus_Text, Angl_Text) \ extern menu_item __flash Parent; \ extern menu_item __flash Child; \ extern menu_item __flash Next; \ extern menu_item __flash Prev; \ menu_item __flash Name = \ { \ (menu_item*) &Parent, \ (menu_item*) &Child, \ (menu_item*) &Next, \ (menu_item*) &Prev, \ EnterFunc, \ PlusFunc, \ MinusFunc, \ MenuInitFunc, \ {Rus_Text}, \ {Angl_Text} \ } //================== //================== #define PARENT *((menu_item __flash*) (CurrMenuItem->Parent)) #define CHILD *((menu_item __flash*) (CurrMenuItem->Child)) #define NEXT *((menu_item __flash*) (CurrMenuItem->Next)) #define PREV *((menu_item __flash*) (CurrMenuItem->Prev)) #define ENTER_FUNC *((FuncPtr) (CurrMenuItem->EnterFunc)) #define MENU_PLUS_FUNC *((FuncPtr) (CurrMenuItem->PlusFunc)) #define MENU_MINUS_FUNC *((FuncPtr) (CurrMenuItem->MinusFunc)) #define MENU_INIT_FUNC *((FuncPtr) (CurrMenuItem->MenuInitFunc)) //================== //================== #define SET_MENU_LEVEL(x) \ Set_Menu_Level(&x) #define SET_MENU_ITEM(x) \ Set_Menu_Item(&x) #define GO_MENU_FUNC(x) \ MenuFunc((FuncPtr*)&x) #define EXTERN_MENU(Name) \ extern menu_item __flash Name; #define CHK_CHANGE_MENU(x) \ chk_change_menu(&x) //================== //================== enum { SET_LEVEL = 0, SET_NEXT, SET_PREV, }; //================== // Prototypes: //================== bool Set_Menu_Level (menu_item __flash *NewMenu); bool MenuFunc(FuncPtr* Function); bool chk_change_menu (menu_item __flash *NewMenu); //================== //================== bool proc_menu_keys (void); //================== //================== void Out_Menu_Items_Init (void); void Out_Menu_Items (void); //================== //================== void out_name_level (void); u08 count_chars (char __flash *data); void make_page_menu (void); void inc_pos_y_curs (void); void dec_pos_y_curs (void); void set_pos_curs (void); //================== //================== char __flash * Get_Addr_Lang_Text (void); //================== #endif menu.c СпойлерКод: //================== #include "menu.h" //================== //================== static u08 quant_items; static u08 pos_y_curs; //================== //================ menu_item __flash *CurrMenuItem; // Текущий пункт меню. menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню. menu_item __flash *temp_menu; menu_item __flash Null_Menu = {(void*)0, (void*)0, (void*)0, (void*)0, NULL_FUNC, NULL_FUNC, NULL_FUNC, NULL_FUNC, NULL_TEXT, NULL_TEXT}; //================ //================== bool Set_Menu_Level (menu_item __flash *NewMenu) { if ((void*)NewMenu == (void*)&NULL_ENTRY) return false; else { CurrMenuItem = NewMenu; Out_Menu_Items_Init (); // Так как новый уровень, инициализация переменных. Out_Menu_Items (); // Вывод названия уровня меню и пунктов меню, курсора. GO_MENU_FUNC (MENU_INIT_FUNC); return true; } } //================== //================== bool MenuFunc (FuncPtr* Function) { if ((void*) Function == (void*) NULL_FUNC) return false; else { ((FuncPtr) Function)(); return true; } } //================== /* Уровни, пункты, текст - все выводится автоматом. Так как все переходы по меню расписаны в структуре, то отпадает надобность в запоминании перемещений по меню. */ //================== void Out_Menu_Items_Init (void) { quant_items = 1; pos_y_curs = 1; // Получение адреса начала массива уровня меню. BeginCurrMenuLevel = CurrMenuItem; temp_menu = (menu_item __flash *)(CurrMenuItem->Prev); while (1) { if ((void*)temp_menu == (void*)&NULL_ENTRY) { break; } else { BeginCurrMenuLevel = temp_menu; temp_menu = (menu_item __flash *)(temp_menu->Prev); } } // Получение количества пунктов меню. temp_menu = (menu_item __flash *)(BeginCurrMenuLevel->Next); while (1) { if ((void*)temp_menu == (void*)&NULL_ENTRY) { break; } temp_menu = (menu_item __flash *)(temp_menu->Next); quant_items++; } // Позиция курсора. if (quant_items > 1) { temp_menu = BeginCurrMenuLevel; while (1) { if ((void*)temp_menu == (void*)&NULL_ENTRY) return; if (temp_menu == CurrMenuItem) return; else pos_y_curs++; temp_menu = (menu_item __flash *)(temp_menu->Next); } } } void Out_Menu_Items (void) { clr_dsp_buf (); out_name_level (); // Вывод названия уровня меню. make_page_menu (); // Вывод пунктов меню. set_pos_curs (); // Установка позиции и вывод курсора. } //================== //================== // Вывод названия уровня меню. void out_name_level (void) { temp_menu = (menu_item __flash *)(CurrMenuItem->Parent); // Считывание названия уровня меню из пункта меню в верхнем уровне. if ((void*)temp_menu != (void*)&NULL_ENTRY) { u08 i = count_chars (Get_Addr_Lang_Text ()); // Подсчет кол-ва символов в строке. // Выравнивание текста посередине строки дисплея. u08 a = i; i = (20 - i); // Дисплей 20x4. Отнимаем от 20 число символов. i >>= 1; // Делим остаток на 2. if (a & (1<<0)) i += 2; // Если число нечетное. else i++; // Если число четное. Print_Buf (1, i, Get_Addr_Lang_Text ()); } } //================== //================== // Подсчет кол-ва символов в строке. u08 count_chars (char __flash *data) { u08 i = 0; while (data [i]) { i++; } return i; } //================== //================== void make_page_menu (void) { signed char tmp_pos_y_curs; u08 i; // Счетчик страниц. u08 j; // Страница меню. if (quant_items > 1) // Если пунктов меню больше 1, значит есть что выводить. { temp_menu = BeginCurrMenuLevel; if (pos_y_curs > PAGE_MENU) { tmp_pos_y_curs = pos_y_curs; i = 0; // Счетчик страниц. while (tmp_pos_y_curs > 0) { tmp_pos_y_curs -= PAGE_MENU; i++; } tmp_pos_y_curs += PAGE_MENU; j = PAGE_MENU; // Страница меню. while (i-- > 1) { while (j--) { temp_menu = (menu_item __flash *)(temp_menu->Next); // Следующий пункт меню. } j = PAGE_MENU; // Страница меню. } } u08 pos_y_text_item = 2; // j = PAGE_MENU; // Страница меню. while (j--) { Print_Buf (pos_y_text_item, 2, Get_Addr_Lang_Text ()); // вывод названия пункта меню. temp_menu = (menu_item __flash *)(temp_menu->Next); // Следующий пункт меню. if ((void*)temp_menu == (void*)&NULL_ENTRY) // Если элемент Next return; // пустой, то выход. else pos_y_text_item++; } } } //================== //================== void inc_pos_y_curs (void) { if (quant_items > 1) { if (pos_y_curs < quant_items) pos_y_curs++; } } void dec_pos_y_curs (void) { if (quant_items > 1) { if (pos_y_curs > 1) pos_y_curs--; } } //================== //================== void set_pos_curs (void) { if (quant_items > 1) { signed char tmp = pos_y_curs; while (tmp > 0) { tmp -= PAGE_MENU; } if (tmp <= 0) tmp += PAGE_MENU; Print_Char (tmp + 1, 1, ARROW_RIGHT); } } //================== //================== bool chk_change_menu (menu_item __flash *NewMenu) { if ((void*)NewMenu == (void*)&NULL_ENTRY) return false; else return true; } //================== //================== char __flash * Get_Addr_Lang_Text (void) { switch (get_lang ()) { case RUS: return temp_menu -> Rus_Text; case ANGL: return temp_menu -> Angl_Text; default: return NULL_TEXT; } } //================== Это взято из проекта с семисегментниками. menu.h СпойлерКод: //================== #ifndef MENU_H #define MENU_H #include "menu.h" #include "main_def_func.h" //================== //================== // Typedefs: typedef void (*FuncPtr)(void); //================== //================== typedef struct menu_item { void *Parent; void *Child; void *Next; void *Prev; FuncPtr PlusFunc; FuncPtr MinusFunc; FuncPtr EnterFunc; FuncPtr MenuInitFunc; char __flash *Text; } menu_item; //================== // Externs: //================== extern menu_item __flash *CurrMenuItem; // Текущий пункт меню. extern menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню. extern __flash menu_item Null_Menu; extern char Menu_Str_Buf []; // Буфер для вывода текста. extern void (*MenuFuncPtr)(void); //================== // Defines and Macros: //================== #define NULL_ENTRY Null_Menu #define NULL_FUNC (void*)0 #define NULL_TEXT "" #define PAGE_MENU 3 //================== //================== #define MAKE_MENU(Name, Parent, Child, Next, Prev, PlusFunc, MinusFunc, EnterFunc, MenuInitFunc, Text) \ extern menu_item __flash Parent; \ extern menu_item __flash Child; \ extern menu_item __flash Next; \ extern menu_item __flash Prev; \ menu_item __flash Name = \ { \ (menu_item*) &Parent, \ (menu_item*) &Child, \ (menu_item*) &Next, \ (menu_item*) &Prev, \ PlusFunc, \ MinusFunc, \ EnterFunc, \ MenuInitFunc, \ {Text} \ } //================== //================== #define PARENT *((menu_item __flash*) (CurrMenuItem->Parent)) #define CHILD *((menu_item __flash*) (CurrMenuItem->Child)) #define NEXT *((menu_item __flash*) (CurrMenuItem->Next)) #define PREV *((menu_item __flash*) (CurrMenuItem->Prev)) #define PLUS_FUNC *((FuncPtr) (CurrMenuItem->PlusFunc)) #define MINUS_FUNC *((FuncPtr) (CurrMenuItem->MinusFunc)) #define ENTER_FUNC *((FuncPtr) (CurrMenuItem->EnterFunc)) #define MENU_INIT_FUNC *((FuncPtr) (CurrMenuItem->MenuInitFunc)) //================== //================== #define SET_MENU_LEVEL(x) \ Set_Menu_Level(&x) #define GO_MENU_FUNC(x) \ MenuFunc((FuncPtr*)&x) #define EXTERN_MENU(Name) \ extern menu_item __flash Name; //================== //================== enum { SET_LEVEL = 0, SET_NEXT, SET_PREV, }; //================== // Prototypes: //================== bool Set_Menu_Level (menu_item __flash *NewMenu); bool MenuFunc(FuncPtr* Function); //================== //================== void Out_Menu_Items_Init (void); void Out_Menu_Items (void); //================== //================== bool proc_menu_keys (void); //================== #endif menu.c СпойлерКод: //================== #include "menu.h" //================== //================ menu_item __flash *CurrMenuItem; // Текущий пункт меню. menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню. menu_item __flash *temp_menu; menu_item __flash Null_Menu = {(void*)0, (void*)0, (void*)0, (void*)0, NULL_FUNC, NULL_FUNC, NULL_FUNC, NULL_FUNC, {NULL_TEXT}}; void (*MenuFuncPtr)(void); //================ //====================== // Уровни, пункты, текст - все выводится автоматом. // Так как все переходы по меню расписаны в структуре, то отпадает надобность в запоминании перемещений по меню. //====================== //================== bool Set_Menu_Level (menu_item __flash *NewMenu) { if ((void*)NewMenu == (void*)&NULL_ENTRY) return false; else { CurrMenuItem = NewMenu; clr_dsp_buf (); Print_Buf (1, CurrMenuItem->Text); // вывод названия пункта меню. GO_MENU_FUNC (MENU_INIT_FUNC); return true; } } //================== //================== bool Set_Menu_Item (menu_item __flash *NewMenu) { if ((void*)NewMenu == (void*)&NULL_ENTRY) return false; else { CurrMenuItem = NewMenu; clr_dsp_buf (); Print_Buf (1, CurrMenuItem->Text); // вывод названия пункта меню. GO_MENU_FUNC (MENU_INIT_FUNC); return true; } } //================== //================== bool MenuFunc (FuncPtr* Function) { if ((void*) Function == (void*) NULL_FUNC) return false; else { ((FuncPtr) Function)(); return true; } } //================== //================== //---------- Навигация по меню. ---------- // Кнопки: // Esc. Выход из меню. Переход на родительский уровень. Отмена. // Enter. Вход в меню. Переход на пункт меню. Вход в режим редактирования параметра. Сохранение параметра. // Plus. Следующий пункт меню. Изменение параметра. Увеличить значение параметра. // Minus. Предыдущий пункт меню. Изменение параметра. Уменьшить значение параметра. //---------- // Esc. Parent Level. // Enter. Child Level. Func. // Plus. Next Item. Func. // Minus. Prev Item. Func. //---------- bool proc_menu_keys (void) { if (Get_Event (EV_ID_KEY_PRESSED)) { switch (Get_Key_Code ()) { case KEY_ESC_COD: if (SET_MENU_LEVEL (PARENT)) return true; else return false; case KEY_ENTER_COD: if (CurrMenuItem -> EnterFunc != NULL_FUNC) GO_MENU_FUNC (ENTER_FUNC); if (SET_MENU_LEVEL (CHILD)) return true; else return false; case KEY_PLUS_COD: if (CurrMenuItem -> PlusFunc != NULL_FUNC) { GO_MENU_FUNC (PLUS_FUNC); return false; } if (SET_MENU_LEVEL (NEXT)) return true; else return false; case KEY_MINUS_COD: if (CurrMenuItem -> MinusFunc != NULL_FUNC) { GO_MENU_FUNC (MINUS_FUNC); return false; } if (SET_MENU_LEVEL (PREV)) return true; else return false; default: return false; } } else return false; } //================== В последнем примере проект. Дисплей из семисегментных индикаторов. Навигация 3 кнопки. Пришлось как следует помучаться, задать кнопками двойные назначения. Next_Plus, Prev_Minus. Получилось достаточно гибко. Игра не только алгоритмом но и тем, что находится в полях структуры. С самого начала была сложность с параметрами. Они не для каждого пункта нужны. И потому лишняя информация. И здесь выход - хоть какая-то типизация, если параметров нет, указатель в NULL, если есть, указатель на параметры. Минус - лишний расход памяти. Если в этом топике кто-нибудь поможет выработать решение с полем параметры, топик можно будет считать успешным. Задача, написать макрос, который позволит сделать поле Parameters не обязательным. Как пример. |
Автор: | ARV [ Вт май 05, 2020 15:19:10 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Demiurg писал(а): Если в этом топике кто-нибудь поможет выработать решение с полем параметры, топик можно будет считать успешным. именно это я и реализовал, помимо всего прочего, что вы могли бы заметить самии по примеру кода, что я приводил в первом сообщении, если бы не были так самоуверены.Задача, написать макрос, который позволит сделать поле Parameters не обязательным. Как пример. прежде всего, у меня сделаны различные по длине (и, соответственно, по количеству полей) структуры для разных типов пунктов меню - экономим память. например, для пункта-подменю не требуются функции "плюс, минус, энтер" (если в вашей концепции) - зачем тогда хранить поля под них?! далее, я реализовал полную (почти) независимость отображающей части и структурной. то есть изменений в коде всех фукнций меню не потребуются ни для символьного ЖКИ, ни для графического ЖКИ, ни для внешнего терминала. конечно, на самом деле небольшие правки нужны, в частности, макросы, определяющие количество символов в пункте - это явно зависит от средства отображения. так же, как я и писал в самом начале, я реализовал концепцию "свойств", т.е. отображаемых и редактируемых при помощи меню параметров программы. сейчас реализована поддержка свойств типа uint8_t, int8_t, uin16_t, int16_t, bool и "enum". поддержка означает, что любое из этих свойств можно:
- изменить на ±1 или ±N, где N<128 (N фиксируется для всей системы меню, но для изменения на N и на 1 применяются разные команды, т.е. можно реализовать обычное и ускоренное изменение свойства); - считать из EEPROM автоматически при инициализации меню или в любой момент отдельной функцией; - сохранить в EEPROM при помощи отдельной функции. считывание/сохранение делается имнно для всех свойств, связанных с меню, т.е. автоматически, без необходимости делать это отдельно для каждого. при отображении свойств типа bool вместо 1 или 0 (или вместо фиксированных строк "true"/"false") выводятся задаваемые пользователем строки, т.е. делается осмысленный вывод значения параметра типа "МОТОР ВКЛ.", "ПОДСВЕТКА ОТКЛ.". для свойства "enum" подобных строк должно быть определено пользователем столько, сколько элементов перечисления. ну и последнее: я не сал париться с самописными функциями для работы со строками, и использовал стандартные библиотечные. возможно, в чем-то я проиграл, но явно выиграл по трудоемкости. как-то так вот. на двух приведенных ранее видео все эти возможности показаны. сейчас думаю: стоит ли делать пункт меню, вид которого целиком зависит от желаний пользователя? т.е. и текст пункта, и реакция на команды редактирования обеспечиваются отдельными функциями, вызываемыми системой меню в нужные моменты? |
Автор: | Demiurg [ Вт май 05, 2020 15:52:41 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
1 - Вспомним наши споры. И вспомним, что мы остановились на том, что остались при своих. Где-то я с вами согласен, но в основном мы разошлись. 2 - Нет никакой самоуверенности. А вот у вас я вижу самовосхваление вашего труда. Без эмоций. Напоминаю. 3 - Вам уже написали, значит другие тоже работали над этой темы (и я в том числе), и вам уже написали, что универсальности не добиться. 4 - Я на примере двух своих проектов показал, что универсальности нет. Разные типы дисплеев. И я не про символьный-графический-семисгментный. Разные количества строк, то сеть пунктов и выводимой на дисплей информации. Разная навигация, разное количество строк. Скроллинг, и все остальное прочее. Это тоже дело вкуса, и у каждого разный подход и предпочтения. Поэтому, задвигаем в сторону все красивости. И программную реализацию. Смотрим шире. Главную причину я уже указал. Плюс вы еще на одну указали, что есть лишние поля. И я сам знаю о о них. Нам не нужна на самом деле регулировка яркости дисплея на месте, это я сделал еще в 2013 году. И на самом деле это к меню не относится. Что мы хотим от проекта? Хороший и удобный инструмент создания и редактирования меню. Итак, вспоминаем правило. Чем больше данных, структур - тем меньше программа. И наоборот. Что мы можем упаковать в структуры? Начинаем классифицировать. Вы сказали, что поля "Enter", "Plus", "Minus" лишние. Согласен и не согласен. Особенно, если у кнопок альтернативные функции. Next-Plus, Prev_Minus. Для меня это был компромисс. Параметры тоже не в каждом пункте. Вы написали, что решили эту проблему. Поделитесь. Это мы обсудим. Все остальное красивости. И мне неинтересны. И я уверен, что мою позицию поддержат. Если кто-то из новичков попросит готовое решение, почему бы и нет. Дадим ему тестовый проект для примера. Как это, вообще-то, в статьях по микроконтроллерам и делается. Ведь мы с вами уже знаем, как создавать меню. Не так ли? |
Автор: | ARV [ Вт май 05, 2020 16:06:59 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Demiurg писал(а): универсальности нет универсальность есть. например, стандартная библиотека Си - разве она не универсальна? универсальность достигается предоставлением основных решений наиболее характерных задач. а остальное - да, требует усилий.так вот. MicroMenu решает только одну проблему - составление структуры меню, как вы ранее справедливо заметили - просто фантик для обертки данных. и частично решает проблему навигации по этой структуре. вопрос отображения меню, как и вопрос получения "команд навигации" MicroMenu не решает. моя версия, которую я назвал FlexMenu, решает еще несколько проблем - редактирование свойств. вопрос отображения структуры так же остается за рамками, как и вопрос получения команд управления. именно отказом от 100% готовых решений достигается универсальность остальной части: структура меню неизменно создается одним и тем же кодом как для ЖКИ, так и для 7-сегментника, так и для терминала по USART. эта часть универсальна. так же универсальным остается часть, отвечающая за редактирование и сохранение/загрузку свойств - она не зависит от того, как меню отображается и чем управляется. количество кнопок к системе отображения меню и системе редактирования свойств не имеет отношения, и это концептуально правильно. даже если у вас останется всего 1 кнопка, система меню должна отрабатывать все свои функции, а вот то, как вы при помощи одной кнопки будете формировать нужные системе меню команды - это ваши проблемы. код FlexMenu я пока вылизываю, чтобы он был красивым, читаемым и гибким в настройке, а так же самодокументируемым. К сожалению, это противоречивые требования, и идеала (по моему мнению) я пока не достиг. как доделаю - обязательно выложу на всеобщее обозрение. единственное, что я пока не стал делать (и более того: не очень понимаю, надо ли это делать, а так же как оптимальнее всего это сделать) - это возврат из субменю ровно к тому виду родительского меню, из которого был вход в подменю. на видео вы видели это: вход в субменю осуществлялся из третьей или пятой строки меню, а после возврата из субменю выделенной оказывалась первая строка. если это не проблема (как по мне - так не проблема) - то и ладно, если это надо устранить - придется кумекать... |
Автор: | Demiurg [ Вт май 05, 2020 16:34:09 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
единственное, что я пока не стал делать (и более того: не очень понимаю, надо ли это делать, а так же как оптимальнее всего это сделать) - это возврат из субменю ровно к тому виду родительского меню, из которого был вход в подменю. на видео вы видели это: вход в субменю осуществлялся из третьей или пятой строки меню, а после возврата из субменю выделенной оказывалась первая строка. если это не проблема (как по мне - так не проблема) - то и ладно, если это надо устранить - придется кумекать... ![]() Как я вам уже писал, если ваш проект решит многие известные проблемы с меню, то он будет иметь неплохой успех. Не хуже, чем в статье на easyelectronics. Если же он удобен только для вас и только под ваш компилятор, он не будет иметь никакой практической цены. Кроме вас. |
Автор: | ARV [ Вт май 05, 2020 16:43:25 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Demiurg писал(а): Я вам писал, что вы не поняли сути MicroMenu я вам писал, что вы слишком самоуверены.Demiurg писал(а): Знаете, как это дико выбешивает, если нужно настроить аппарат, а у него меню на сотни пунктов. не знаю, но представить могу ![]() Demiurg писал(а): если ваш проект решит многие известные проблемы с меню так вы сформулируйте эти проблемы, потому как я не понимаю, о чем вы говорите, после того, как я расписал и продемонстрировал на видео уже реализованные возможности. что еще не решенным осталось-то?Demiurg писал(а): он будет иметь неплохой успех я за славой не гонюсь ![]() |
Автор: | Reflector [ Вт май 05, 2020 18:27:24 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
С самого начала была сложность с параметрами. Они не для каждого пункта нужны. И потому лишняя информация. И здесь выход - хоть какая-то типизация, если параметров нет, указатель в NULL, если есть, указатель на параметры. Минус - лишний расход памяти. Если в этом топике кто-нибудь поможет выработать решение с полем параметры, топик можно будет считать успешным. На C++ меню можно описать в одном виде, а потом на этапе компиляции взять массив и превратить его в нечто совершенно другое. Например, меню из первого поста может выглядеть так: СпойлерКод: constexpr MenuItem menuItems[] = { { 0, "BEEP", beep }, { 0, "TEST", nullptr, PROP_U8(u8, NOSTORE, 0, 12) }, { 0, "MOTOR", motor, PROP_BOOL(motor_state, NOSTORE, sf("STOP"), sf("RUN")) }, { 0, "SUBMENU" }, { 1, "STOP DEMO 1", done); { 1, "STOP DEMO 2", done); { 1, "STOP SUB"); { 2, "SUB STOP DEMO 1", done }, { 2, "SUB STOP DEMO 2", done }, { 2, "SUB STOP DEMO 3", done }, { 0, "TEMPERATURE", nullptr, PROP_I16(vfr, NOSTORE, -20, 20) }, { 0, "LOAD", load }, { 0, "SAVE & EXIT", save } }; А после трансформации уровня вложенности не будет, зато появится, например, связный список. Или можно для каждого пункта меню добавить флаг указывающий на то используются ли указатели на функции(передвинутые в конец структуры), если нет, то можно их выкинуть и перелинковать список с учетом этого. И главное этот новый массив будет лежать во флеше, а старый массив компилятор удалит за ненадобностью, кода естественно тоже никакого сгенерировано не будет. |
Автор: | Demiurg [ Вт май 05, 2020 20:05:53 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Нужен костяк, на котором можно уже реализовать все красивости, про которые вы так расписали. Пусть это даже не будет соответствовать вашим запросам, но полностью моим. Анализируем. Навигация. Есть точка входа в меню. Этих точек может быть несколько. Дальше работает "матрица". Текущий пункт, подпункт, следующий, предыдущий. Закладываем "родительский" пункт. То есть, основа MicroMenu. Parent, Child, Next, Prev. Это ядро "матрицы". В ней у нас прописана вся навигация по меню. По сути отдельный конечный автомат. И текст пунктов меню. Это тоже обязательный элемент структуры. Здесь уже дело вкуса, у кого-то это указатель на текст, но здесь добавляется трудоемкость, придумывание название тексту. Лично мне такой способ не годится, потому прямой текст, и сразу визуально видно пункт меню в проекте и уменьшается количество мест для создания меню. Что остается у нас в итоге: Код: // NAME PARENT CHILD NEXT PREV TEXT // Можно так. // NAME TEXT PARENT CHILD NEXT PREV // Можно так. Кому как нравится. //=========== // NAME PARENT CHILD NEXT PREV TEXT //---------- //---------- MAKE_MENU (L_DEFAULT, NULL_ENTRY, L_L1_I1, NULL_ENTRY, NULL_ENTRY, "ПАРАМЕТРЫ"); MAKE_MENU (L_L1_I1, L_DEFAULT, NULL_ENTRY, L_L1_I2, NULL_ENTRY, "Пункт 1"); Это минимальная структура, которая необходима для всех пунктов. Что делать дальше? Можно сделать поле-указатель (void*). Этим мы получаем свободу в плане, лепим уже где-то там необходимые нам структуры с необходимыми нам вещами, параметры, указатели на функции. Вариант так себе, это еще два байта на каждый пункт меню. А вот если бы написать такой-ие макрос-ы, что позволило бы в случае необходимости добавлять нужные поля. Вот как у Reflector. Но он говорит о плюсах. А я их не знаю. И кстати, ARV, я бы все-таки хотел в вашем проекте глянуть только один момент: как вы реализовали добавление поля параметров в пункты меню, и при этом это необязательное поле в структуре. Также я хотел бы глянуть в шестнадцатеричке, как компилятор это компилирует. Есть подозрение, что это и есть те два байта о которых я писал чуток выше. То есть спрятано в программе, но не во Flash |
Автор: | ARV [ Вт май 05, 2020 21:48:02 ] | ||
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. | ||
Demiurg писал(а): Есть подозрение, что это и есть те два байта о которых я писал чуток выше. То есть спрятано в программе, но не во Flash не те два байта, а разное количество байт, в зависимости от того, какие именно поля в структуре присутствуют, а какие отсутствуют. вся структура меню размещается во flash. только что закончил "тестирование" размещения всего этого безобразия и в ОЗУ - для так любимой вами "кроссплатформенности". теперь можно или в ОЗУ все структуры строить, или во flash. Добавлено after 1 hour 10 minutes 18 seconds: подготовил DOXYGEN-документацию, прилагаю. там есть ссылки почти на все исходники для ознакомления с их содержимым, все исходники "интерактивные", т.е. при наведении на все документированные штучки всплывает подсказка, а по клику происходит переход к описанию этой штуки.
|
Автор: | ARV [ Ср май 06, 2020 13:48:12 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
полный набор файлов FlexMenu вместе с демо-проектами и проектом протеуса доступны по ссылке: https://cloud.mail.ru/public/7eFh%2Fo9BnSJE3g |
Автор: | WiseLord [ Ср май 06, 2020 14:08:07 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
В целом, красиво сделано. Что мне не очень нравится, так это то, что у пункта меню есть поля .prev и .next. Я когда свою имплементацию делал, пытался от этого избавиться. Ведь технически, эти поля - лишние. Плюс они требуют ручных изменений в структуре меню, если какой-то пункт хочется перенести в другое место - на том же уровне или подменю. Ну.. как бы схлопнуть разорванный двусвязный список и вставить пункт в другой. Ведь по идее, у пункта меню есть родительский пункт, и этого вполне достаточно. Зная родителя, можно пробежаться по всем пунктам меню и составить список всех пунктов с этим же родителем. Единственное, возникает проблема с тем, что как теперь выстроить порядок пунктов меню в этом субменю. В данной реализации последовательность объявления пунктов меню роли не играет. Она такая чисто для удобства чтения. Но вот в моём случае всё же приходится пользоваться общим массивом пунктов мееню (констатнтым, во flash), и тогда порядок описания пунктов меню уже сам по себе решает эту задачу. Но как я писал раньше, свою реализацию я так до конца и не вылизал ещё. Поэтому сюда это в виде идеи подаю. |
Автор: | Demiurg [ Ср май 06, 2020 14:28:43 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
... Будь другом, покажи здесь одну вещь, как ты реализовал необязательный параметр. Либо в личку. По сути, со времен MicroMenu, это у меня единственная заморочка. Помню пробовал, не получилось, плюнул, пошел дальше. Добавлено after 15 minutes 24 seconds: Я уже писал, чтобы была полноценная реализация навигации по меню, а тем более, чтобы возвращаться как в родительский так и в подпункты, нужно 4 поля. Parent, Child, Next, Prev. Нам нужно запомнить начало уровня и номер текущего пункта меню. Все. Выше я выкладывал примеры меню. Также у меня реализован вывод какого угодно числа пунктов. При символьном дисплее 20х4. |
Автор: | ARV [ Ср май 06, 2020 15:11:19 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
WiseLord писал(а): Ведь технически, эти поля - лишние. проблема лично для меня в том, что я пока не смог написать макрос, который бы позволял создавать массив указателей на структуры переменной длины прямо в инициализации самого массива. для списка взял идею из MicroMenu, а вот для массива не выходит - ругается компилятор на неопределенный размер структуры... или я чего-то не понимаю, или это невозможно в принципе.поэтому пришлось делать список. раньше тоже делал меню массивами. но все структуры были фиксированного размера - снова лишние поля... Demiurg писал(а): нужно 4 поля. Parent, Child, Next, Prev. я сделал на трех. проблему с возвратом из субменю к первому пункту решил - она решаема для любого количества пунктов любых реализаций, и в примерах проектов, которые я выложил вместе с исходниками, это демонстрируется: возврат из субменю происходит к выделению "входного" пункта.Demiurg писал(а): как ты реализовал необязательный параметр. анонимный union: для пункта "команда" не нужно поле "субменю", но нужно поле "функция", для пункта "субменю" не нужно поле "функция", но нужно поле "субменю" - если сделать оба поля в union-е, то вместо двух полей будет фактически одно... так можно и для трех, и для четырех сделать - было бы желание.Добавлено after 3 minutes 29 seconds: WiseLord, я старался сделать так, чтобы можно было максимально абстрагироваться от патформы... можете собрать демо-проект под свой ARM - получилось у меня или нет? в fmenu_config.h надо заремарить PLACE_CONST_IN_FLASH - у ARM памяти много... и должно собраться. с портами и USART, конечно, я не знаю, насколько просто будет выглядеть адаптация демо-проекта... |
Автор: | Demiurg [ Ср май 06, 2020 17:40:28 ] |
Заголовок сообщения: | Re: FlexMenu - решение вопросов меню. Зацените. |
Пара моментов. Скажу сразу, взглянул на ваш код и у меня "бигуди зашевелились"... Первый. В статье про MicroMenu честно написано, сколько байтов на пункт меню тратится. В своей переделке я тоже знаю. Вы можете сказать, сколько у вас тратится? В разных вариациях. Второй момент. Проект MicroMenu хорош, если параметры идут списком. Мне вот интересно, а что если экран примерно такого вида? У меня это визуально реализовано так. Значение нужного параметра мигает. Это означает, что его можно редактировать. Вложение: Я когда начинал возиться с меню, смотрел в сторону DOS (autoexec.bat, config.sys (у меня где-то до сих пор сохранились книги, распечатки, файлы про MS-Dos, команды (в свое время делал мультизагрузку (один мой рабочий загрузочный диск мой знакомый даже купил)))), Volcov Commander с его vc.mnu. И остановился я в итоге вот на какой концепции. Универсальности нет и не будет. ПЛК, точнее, панель оператора. Это абсолютно самостоятельный модуль, пусть и подчиненный. Работа панели оператора не влияет на работу ПЛК, работа ПЛК не влияет на работу панели. Так вот, меню - это самостоятельный модуль. И состояние другого модуля. Я его назвал info_service. Несколько лет наткнулся на TUI, но этот проект не соответствовал моим запросам. В итоге родилась концепция info_service. Мухи отдельно, котлеты отдельно. Ха, кстати, в одном проекте остался этот рудимент. Название, но не суть. Где-то 2013 год. Вложение:
|
Страница 1 из 4 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |