Программерские приёмчики: организация меню.

Dimanoss
Offline
Зарегистрирован: 29.05.2016
Опять вопрос по приёмам.  Например: нужна древовидная многоуровневая менюшка.  Каждый пункт меню может (но не обязан) иметь один или несколько подпунктов.  Вид примерно такой:
 
 
Пункт 1
     Пункт 1.1
     Пункт 1.2
Пункт 2
     Пункт 2.1
         Пункт 2.1.1
         Пункт 2.1.2
             Пункт 2.1.2.1
         Пункт 2.1.3
     Пункт 2.2
     Пункт 2.3
         Пункт 2.3.1
     Пункт 2.4
Пункт 3
Пункт 4
     Пункт 4.1
     Пункт 4.2
     Пункт 4.3
 
 
Как наиболее грамотно организовать подобное в коде?  Многомерный массив?  Одна переменная, как понимаю, должна описывать текущую позицию меню, нужна ли ещё одна, описывающая родительский пункт?  Что менее затратно - выделять память под такую переменную (теряем память), или же динамически вычислять родителя (теряем время)?
 
sadman41
Offline
Зарегистрирован: 19.10.2016

Связные списки, например.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

Связные списки, например.

дополню - связный список обьектов. Для описания пунктов меню создается структура или класс, поля которого хранят текст пункта, указатели на предыдущий, последующий. вышестоящий и нижестоящий пункт и прочую необходимую информацию.

Примеров в сети довольно много. но должен предупредить, что организация меню - один из довольно сложных разделов ООП программирования. Мне кажется. что для вас пока будет сложно его освоить...для человека. не понимающего даже. как посчитать число элементов обычного одномерного массива.

Dimanoss
Offline
Зарегистрирован: 29.05.2016

b707 пишет:

Мне кажется. что для вас пока будет сложно его освоить...для человека. не понимающего даже. как посчитать число элементов обычного одномерного массива.

 

если опустить выпад насчёт массива - спасибо. :-)  То, что я не знаком с конкретной реализацией С++, не означает, что не работаю с ООП в целом. ;-)   

 

 

rkit
Offline
Зарегистрирован: 23.11.2016

sadman41 пишет:

Связные списки, например.

Единственное  причина использовать эту структуру - быстрая вставка. Которая тут совершенно не нужна.

rkit
Offline
Зарегистрирован: 23.11.2016

Dimanoss пишет:

То, что я не знаком с конкретной реализацией С++

Ты ни с какой не знаком.

b707
Offline
Зарегистрирован: 26.05.2017

вот вам ссылочка на обсуждение организации многомерного меню на этом форуме. там и примеры есть. Только не советую брать первые два - там как раз пример того, как это делать НЕ НАДО

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogourovnevogo-graficheskogo-menyu

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

Единственное  причина использовать эту структуру - быстрая вставка. Которая тут совершенно не нужна.

я бы сказал не "быстрая", а скорее простая и легкая. Меню на связанном списке позволяет играючи добавлять и убирать элементы при разработке программы. Считаю это этого более чем достаточно. чтобы выбрать именно этот вариант

rkit
Offline
Зарегистрирован: 23.11.2016

b707 пишет:

Меню на связанном списке позволяет играючи добавлять и убирать элементы при разработке программы. Считаю это этого более чем достаточно. чтобы выбрать именно этот вариант

ИНтересно, как это так выбор структуры изменит скорость редактирования кода.

sadman41
Offline
Зарегистрирован: 19.10.2016

rkit пишет:

sadman41 пишет:

Связные списки, например.

Единственное  причина использовать эту структуру - быстрая вставка. Которая тут совершенно не нужна.

С чего ты решил, что знаешь какая цель преследуется и как будет использоваться структура меню?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dimanoss пишет:

Опять вопрос по приёмам. 

В Вашем прошлом вопросе, Вам ответили сразу два человека, а вместо благодарности огребли наезд с топаньем ногами и истерическим капсом. Так что ЛЕСОМ!

Dimanoss
Offline
Зарегистрирован: 29.05.2016

ЕвгенийП пишет:

Так что ЛЕСОМ!

 

да, если нечего высказать по вопросу - лесом, пожалуйста.  С капсом и без.

 

 

 

rkit
Offline
Зарегистрирован: 23.11.2016

sadman41 пишет:

rkit пишет:

sadman41 пишет:

Связные списки, например.

Единственное  причина использовать эту структуру - быстрая вставка. Которая тут совершенно не нужна.

С чего ты решил, что знаешь какая цель преследуется и как будет использоваться структура меню?


Понятия не имею, какая цель преследуется. Я рассуждаю с точки зрения рациональности решения. А какая нмркоманская логика привела к рекомендации связанных списков в такой задаче - это для меня темный лес.

b707
Offline
Зарегистрирован: 26.05.2017

Dimanoss пишет:

да, если нечего высказать по вопросу - лесом, пожалуйста.  С капсом и без

Уважаемый, вы прямым ходом движетесь к указателю и последующему бану. Или начинайте вести себя как взрослый человек, или никто не станет тратить на вас время - нет смысла, если вас не сегодня-завтра забанят...

Во-всяком случае, я пока от ответа на ваши вопросы воздержусь.

sadman41
Offline
Зарегистрирован: 19.10.2016

rkit пишет:

Понятия не имею, какая цель преследуется. Я рассуждаю с точки зрения рациональности решения.

Ты просто хочешь всегда казаться истиной в последней инстанции. Вот и всё. Это в каждом твоем выступлении (по теме и без) сквозит.

rkit
Offline
Зарегистрирован: 23.11.2016

Я просто молчу, когда чего-то не знаю, а не выдумываю чушь, как любят делать многие тут.

Logik
Offline
Зарегистрирован: 05.08.2014

Согласен, списки для меню не в тему. Я обычно использую массив структур. В структуре есть поля для глубины вложения,  указателей на текст данного пункта и функцию отвечающую за динамику - видимость пункта, дополнительный текст и пр. Чисто статические меню  почти что не встречаются. Они не удобны.Например в меню настройки параметров  куда приятней видеть название параметра и его текущее значение, чем только название.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Logik пишет:

Согласен, списки для меню не в тему. Я обычно использую массив структур. В структуре есть поля для глубины вложения,  указателей на текст данного пункта и функцию отвечающую за динамику - видимость пункта, дополнительный текст и пр. Чисто статические меню  почти что не встречаются. Они не удобны.Например в меню настройки параметров  куда приятней видеть название параметра и его текущее значение, чем только название.

Эту прелесть еще бы и в PROGMEM запхать какнить. :) 

sadman41
Offline
Зарегистрирован: 19.10.2016

В микроменю вполне себе уживаются и связные списки и PROGMEM без загонов насчёт быстрых вставок.

https://github.com/abcminiuser/micromenu-v2/blob/master/MicroMenu.h

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

В микроменю вполне себе уживаются и связные списки и PROGMEM без загонов насчёт быстрых вставок.

https://github.com/abcminiuser/micromenu-v2/blob/master/MicroMenu.h

Спасибо.  Я то себе сделал, сначала строки в прогмем загоняю, потом список структур с указателями на них, но дюже неудобно и нечитабельно, а в макросы я играть не умею. 

b707
Offline
Зарегистрирован: 26.05.2017

Logik пишет:

Согласен, списки для меню не в тему. Я обычно использую массив структур.

массив структур списку не противоречит. Вот мой простенький вариант - с точки зрения синтаксиса - массив структур в ПРОГМЕМ, с точки зрения логики - связанный список.

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogourovnevogo-graficheskogo-menyu#comment-530028

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

сначала строки в прогмем загоняю, потом список структур с указателями на них, но дюже неудобно и нечитабельно

мне тоже не понравилось микроменю тем что громоздко и нечитабельно, я его переделал нагляднее и проще :)

Logik
Offline
Зарегистрирован: 05.08.2014

DetSimen пишет:

Logik пишет:

Согласен, списки для меню не в тему. Я обычно использую массив структур. В структуре есть поля для глубины вложения,  указателей на текст данного пункта и функцию отвечающую за динамику - видимость пункта, дополнительный текст и пр. Чисто статические меню  почти что не встречаются. Они не удобны.Например в меню настройки параметров  куда приятней видеть название параметра и его текущее значение, чем только название.

Эту прелесть еще бы и в PROGMEM запхать какнить. :) 

Запихуется. Но не всегда запихиваю. Там жеж самих строк нет, занимают не так и много. А вот строки - те да, те PROGMEM

b707
Offline
Зарегистрирован: 26.05.2017

Logik пишет:

Запихуется. Но не всегда запихиваю. Там жеж самих строк нет,

я в структуру разом положил все - и строчки, и указатели, и номера пунктов. И все в ПРОГМЕМ

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

мне тоже не понравилось микроменю тем что громоздко и нечитабельно

Вот-вот.  Поэтому я тоже лисапед изобрёл, но выкладывать никуда не стал, ибо за код мучительно стыдно, хоть и менее громоздко, но тоже нечитабельно.   :)  А я люблю "академический" код, который говорит сам за себя даже с минимумом комментариев :)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

я в структуру разом положил все - и строчки, и указатели, и номера пунктов. И все в ПРОГМЕМ

Огорчу тебя, в ПРОГМЕМ у тебя указатели на строчки, а сами строчки -> в ОЗУ. 

Если код остался этот http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogourovnevogo-graficheskogo-menyu#comment-530028

 

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

Огорчу тебя, в ПРОГМЕМ у тебя указатели на строчки, а сами строчки -> в ОЗУ. 

Если код остался этот

код, этот, да.

А чего строчки не в ПРОГМЕМ - посмотри внимательно. там выше сама структура описана - она вся в ПРОГМЕМ, разве нет? строчки не ссылками, а константы с заранее описанным размером. Или надо на строку отдельно еще один квалификатор ПРОГМЕМ ставить?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

НЕТ

const char[] сам по себе строки в прогмем не пхает.  Структура да, она у тебя вся в прогмем, со всеми указателями, только указатели эти указывают на ОЗУ, там где сопсно строки и лежат.  

Для другого надо сначала строки в прогмем запхать

const char txt_MenuItem1[] PROGMEM = "Menu Item1 Text";

а потом уже в структуре с ними указатель связывать.

struct TMenuItem {

__FlashStringHelper__ ItemText;

.

.

}

const TMenuItem Item1 PROGMEM = {txt_MenuItem1,...};

 

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

Для другого надо сначала строки в прогмем запхать

const char txt_MenuItem1[] PROGMEM = "Menu Item1 Text";

а потом уже в структуре с ними указатель связывать.

это мерзко и разрушает всю идею. Мне это не подходит. Надо придумать что-то другое :)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

это мерзко и разрушает всю идею. Мне это не подходит. Надо придумать что-то другое :)

Почему я и говорю, что получается некрасиво. :)  Я - за красивый ко(д/т). 

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

это мерзко и разрушает всю идею. Мне это не подходит. Надо придумать что-то другое :)

Мои опыты вроде как показывали, что поведение массива, описанного в PGM-структуре ничем не отличается от поведения массива описанного отдельно с квалификатором PROGMEM.

Дед просто думает, что в структуре указатель, но это не так. Я тоже постоянно забываю, как правильно надо объявлять, когда месяца три этот прогмем не трогаю.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

Дед просто думает, что в структуре указатель, но это не так. 

если в структуре стоит строка, то это и есть указатель.  Возможно, я крепко ашыбаюсь, пусь взрослые подскажут. :) 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А можно мне на код глянуть?

sadman41
Offline
Зарегистрирован: 19.10.2016

А давайте, как настоящие анжинеры, просто палкой по прогмему хрястнем и посмотрим, что выйдет:

#pragma GCC optimize ("O0")

//#define ASCIIZ_AS_ARRAY

typedef struct {
  const uint16_t id;
#ifdef ASCIIZ_AS_ARRAY
  const char name[12];
#else
  const char* name;
#endif
} listUsr_t;

listUsr_t const users[] PROGMEM = {
  { 1234, "Eji"},
  { 4321, "Petruccio"}
};
    
void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, users[random(2)].name[0] == 'E');
}
void loop() {}

//#define ASCIIZ_AS_ARRAY

Sketch uses 1518 bytes (4%) of program storage space. Maximum is 30720 bytes.
Global variables use 27 bytes (1%) of dynamic memory, leaving 2021 bytes for local variables. Maximum is 2048 bytes.
 

#define ASCIIZ_AS_ARRAY

Sketch uses 1538 bytes (5%) of program storage space. Maximum is 30720 bytes.
Global variables use 13 bytes (0%) of dynamic memory, leaving 2035 bytes for local variables. Maximum is 2048 bytes.
 
...
digitalWrite() тупо воткнуто чтобы компилятор не усердствовал.

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ЕвгенийП пишет:

А можно мне на код глянуть?

код по ссылке из #25

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Гри, дело в том, что const char[12] и const char[]  это разные типы данных, первый - известной длины и относится к типам-значениям, он может храниться в PROGMEM вместе со структурой, а второй тип данных, для компилятора - неизвестной длины, а неоднозначности он не терпит, поэтому второй тип ни что иное как const char * известной длины, что ничему не противоречит.  Поэтому в первом случае структура хранит в себе весь массив как набор байт, а во втором - только указатель.  Можешь распечатать sizeof(listUsr_t)  в первом и во втором случае. 

Но, согласись, для меню фиксированный массив это оверхэд. 

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

Гри, дело в том, что const char[12] и const char[]  это разные типы данных, первый - известной длины и относится к типам-значениям, он может храниться в PROGMEM вместе со структурой, а второй тип данных, для компилятора - неизвестной длины, а неоднозначности он не терпит, поэтому второй тип ни что иное как const char * известной длины, что ничему не противоречит. 

Деда, если мы обсуждаем код по моей ссылке - там-то как раз массив известной длины:

typedef struct PROGMEM {
  const char  Text[MENU_NAME_SIZE];
  const uint8_t Next;
  const uint8_t Type;
} menuItem;

И я не случайно его так описал - а именно с целью того, чтобы он весь упихался в прогмем.

Цитата:
согласись, для меню фиксированный массив это оверхэд

Если он в ПРОГМЕМ и флеша хватает - какая разница? - зато код проще и нагляднее

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

А давайте, как настоящие анжинеры, просто палкой по прогмему хрястнем и посмотрим, что выйдет:

и это правильно :) сам собирался проверить, но вечером. на работе ардуины нет.

Спасибо

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

b707 пишет:

Деда, если мы обсуждаем код по моей ссылке - там-то как раз массив известной длины:

И я не случайно его так описал - а именно с целью того, чтобы он весь упихался в прогмем.

Ну так я ж слепой, прошу пардону. Паду, полью лысину воткой. 

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

Но, согласись, для меню фиксированный массив это оверхэд. 

А я не спорю с этим. Мое замечание касалось реализации с интегрированным в структуру массивом. А рационально это или нет - решать тому, кто пользуется. К примеру для менюшек на LSD - это очень даже и плюс. Сразу определил 17 (или 21) символ в массиве и лепишь туда строки с пробелами. При выводе они ещё и почистят весь мусор на экранчике.

 

Logik
Offline
Зарегистрирован: 05.08.2014

..а если этот текст в массив засунуть, и  вынести в отдельный файл .h , то можно в кирилице от гребаного юникода избавится. Но редактировать в ИДЕ не получится. Вот где экономия :-)  Ещё в вывод текста на экран дописал функцию вывода из progmem из массива по заданному индексу... Ляпота.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

А я не спорю с этим. Мое замечание касалось реализации с интегрированным в структуру массивом. А рационально это или нет - решать тому, кто пользуется. К примеру для менюшек на LSD - это очень даже и плюс. Сразу определил 17 (или 21) символ в массиве и лепишь туда строки с пробелами. При выводе они ещё и почистят весь мусор на экранчике.

Ну всё, извините, парни, это я идиот, ляпнул не разобравшись. Буду внимательней впредь. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:

Огорчу тебя, в ПРОГМЕМ у тебя указатели на строчки, а сами строчки -> в ОЗУ. 

Если код остался этот http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogourovnevogo-graficheskogo-menyu#comment-530028

Да, нет, всё там в прогмем. Проверить легко. Компилируем вот так:

#define printVar(x) do { Serial.print(#x "="); Serial.println(x); } while (false)

#define MENU_NAME_SIZE	32

enum { MENU_LINE, COM_TUNE, COM_EXIT, COM_SEL_SHUNT, COM_SAVE, SUB_MENU, COM_CLEAR, MENU_END };

typedef struct PROGMEM {
  const char  Text[MENU_NAME_SIZE];
  const uint8_t Next;
  const uint8_t Type;
} menuItem;

const menuItem menustack[] PROGMEM  = {
	{"Settings menu:", 0, SUB_MENU},
	{"Select shunt",   1, MENU_LINE},
	{"Tune shunt",   COM_TUNE,MENU_LINE},
	{"Save settings", 2 ,MENU_LINE},
	{"Clear settings", 3 ,MENU_LINE},
	{"Exit", COM_EXIT,MENU_LINE},
	
	{"Shunt list:",1, SUB_MENU},
	{"1 Ohm",COM_SEL_SHUNT, MENU_LINE},
	{"0.1 Ohm",COM_SEL_SHUNT, MENU_LINE},
	{"0.05 Ohm",COM_SEL_SHUNT, MENU_LINE},
	{"0.01 Ohm",COM_SEL_SHUNT, MENU_LINE},
	{"0.005 Ohm",COM_SEL_SHUNT, MENU_LINE},
	{"<< back", 0 ,MENU_LINE},
	
	{"Save current shunt & tune?", 2, SUB_MENU},
	{"Save", COM_SAVE, MENU_LINE},
	{"Cancel", 0 ,MENU_LINE},
	
	{"Clear current shunt & tune?", 3, SUB_MENU},
	{"Clear", COM_CLEAR, MENU_LINE},
	{"Cancel", 0 ,MENU_LINE},
	{"",0,MENU_END}
};

constexpr size_t totalIems = sizeof(menustack) / sizeof(menustack[0]);

void setup(void) {
  Serial.begin(115200);
  digitalWrite(5, menustack[random(0, totalIems)].Next); // хрен выбросит!
  printVar(sizeof(menustack));
  printVar(totalIems);
}

void loop(void) {}

/*********
Скетч использует 3160 байт (10%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 222 байт (10%) динамической памяти, оставляя 1826 байт для локальных переменных. Максимум: 2048 байт.
*********/

Теперь компилируем вот так:

#define printVar(x) do { Serial.print(#x "="); Serial.println(x); } while (false)

#define MENU_NAME_SIZE	32

enum { MENU_LINE, COM_TUNE, COM_EXIT, COM_SEL_SHUNT, COM_SAVE, SUB_MENU, COM_CLEAR, MENU_END };

typedef struct PROGMEM {
  const char  Text[MENU_NAME_SIZE];
  const uint8_t Next;
  const uint8_t Type;
} menuItem;

const menuItem menustack[] PROGMEM  = {
	{"Settings menu:", 0, SUB_MENU},
	{"",0,MENU_END}
};

constexpr size_t totalIems = sizeof(menustack) / sizeof(menustack[0]);

void setup(void) {
  Serial.begin(115200);
  digitalWrite(5, menustack[random(0, totalIems)].Next); // хрен выбросит!
  printVar(sizeof(menustack));
  printVar(totalIems);
}

void loop(void) {}

/********
Скетч использует 2548 байт (8%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 222 байт (10%) динамической памяти, оставляя 1826 байт для локальных переменных. Максимум: 2048 байт.
********/

Как видишь, расход ОЗУ не изменился, а расход прогмем изменился на 3160 - 2548 = 612 байтов, т.е. в точности на 18 * sizeof(menuItem)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ох, сорри, пока писал, тут уже всё и без меня разрулилось. Сорри.