Меню, вопросы.

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

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

Вот код

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

char* mainMenu[] = {"1. SETUP", "2. TEST"};
char* setupMenu[] = {"1. TIME SETUP", "2. GSM SETUP"};
char* testMenu[] = {"1. GSM TEST"};


void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop() {
  for (int i = 0; i < 3; i++)
  {
    lcd.clear();
    showMenu(i);
    delay(3000);
  }
}

void showMenu(int numM)
{
  switch (numM)
  {
    case 0:
      {
        int n = sizeof(mainMenu) / sizeof(mainMenu[0]);
        for (int i = 0; i < n; i++)
        {
          lcd.setCursor(0, i);
          lcd.print(mainMenu[i]);
        }
      }
      break;
    case 1:
      {
        int n = sizeof(setupMenu) / sizeof(setupMenu[0]);
        for (int i = 0; i < n; i++)
        {
          lcd.setCursor(0, i);
          lcd.print(setupMenu[i]);
        }
      }
      break;
    case 2:
      {
        int n = sizeof(testMenu) / sizeof(testMenu[0]);
        for (int i = 0; i < n; i++)
        {
          lcd.setCursor(0, i);
          lcd.print(testMenu[i]);
        }
      }
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
      break;
  }
}

Код написал для тестирования работы меню.

ТУТ симуляция работы кода, если интересно.

1. Как лучше объявлять переменные в которых хранятся названия меню? Глобально или локально в фцнкции showMenu? Я понимаю что если глобально то у меня будет меньше памяти, но если локально получается при вызове функции постоянну переменным прописываются значения. Вот не понимаю какое из двух зол выбрать.

2 И второй вопрос, меню имеют разное количество пунктов. Количество элементов в массиве можно получить так int n = sizeof(Array) / sizeof(Array[0]); Но для двумерного массива как определить количество элементов я не нашел. Есть ли такая возможноать? Двуменрый массив нужен чтобы сложить все меню в одну переменную, и выводить в зависимости от уровня меню нужный массив.

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

Например массив объявляем так

char* mMenu[][2] = {{"1. SETUP", "2. TEST"},{"1. TIME SETUP", "2. GSM SETUP"},{"1. GSM TEST"}};

Но тогда я не могу получить количество элементов в массиве программно. 

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

int elemMenu[] = {2,2,1};

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015
Andrey12 пишет:
1. Как лучше объявлять переменные в которых хранятся названия меню? Глобально или локально в фцнкции showMenu?
 
Если переменная используется только внутри функции и нигде больше - её всегда нужно объявлять внутри функции. Всегда! И никогда иначе! Если при этом не нужно, чтобы она каждый раз заново инициализировалась, а, наоборот, нужно чтобы сохраняла значение между вызовами, то надо перед описанием типа добавить слово static. А если она ещё и никогда не изменяется в процессе работы программы, то добавить const static. Но описывать только внутри функции - никаких исключений! Любой, кто описывает переменную используемую только в одной функции, как глобальную - безграмотный чайник.
 
Т.е. Вам надо описать их внутри функции примерно так.
static const char* mainMenu[] = {"1. SETUP", "2. TEST"};

Andrey12 пишет:
Количество элементов в массиве можно получить так int n = sizeof(Array) / sizeof(Array[0]); Но для двумерного массива как определить количество элементов я не нашел. Есть ли такая возможноать?

А чем Вас не устраивает старый, добрый приём sizeof(Array)/sizeof(Array[0]) ? В чем проблема?
Я вот пишу:
	char* mMenu1[][2] = {
		{"1. SETUP", "2. TEST"},
		{"1. TIME SETUP", "2. GSM SETUP"},
		{"1. GSM TEST"}
	};
	Serial.println(sizeof(mMenu1)/sizeof(mMenu1[0]));

и всё получается. А у Вас что, не выходит? Или просто не пробовали?

Andrey12 пишет:
Например массив объявляем так
char* mMenu[][2] = {{"1. SETUP", "2. TEST"},{"1. TIME SETUP", "2. GSM SETUP"},{"1. GSM TEST"}};
Но тогда я не могу получить количество элементов в массиве программно.
 
Почему не можете? Не понимаю. Заратустра не позволяет?
 
Andrey12 пишет:
Может быть мне не заморачиваться и сделать еще массив с указанием количества элементов меню?
int elemMenu[] = {2,2,1};
 
Очень неудачное решение. Тогда при люом изменении меню надо будет не забывать менять и этот массив. Оно Вам надо?
Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

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

Т.е. Вам надо описать их внутри функции примерно так.
static const char* mainMenu[] = {"1. SETUP", "2. TEST"};

Спасибо от чайника, вы развеяли мои сомнения :-)

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

А чем Вас не устраивает старый, добрый приём sizeof(Array)/sizeof(Array[0]) ? В чем проблема?
 
Я вот пишу:
char* mMenu1[][2] = {
{"1. SETUP", "2. TEST"},
{"1. TIME SETUP", "2. GSM SETUP"},
{"1. GSM TEST"}
};
Serial.println(sizeof(mMenu1)/sizeof(mMenu1[0]));

и всё получается. А у Вас что, не выходит? Или просто не пробовали?

Обижаете уважаемый.. пробовал, но что он вам в сериал выводит с таким примером? 3! То есть у меня 3 элемента на первом уровне. 
А мне интересно получить количество элементов в массиве второго уровня, то есть как узнать что в первом массиве второго уровня 2 элемента во втором тоже 2 и в третьем 1?
Но думаю никак, потому что IDE на такую конструкцию char* mMenu1[][] = {.... выдает ошибку и говорит что второй параеметр многомерного массива должен быть заполнен, а значит сколько я ни укажу пунктов меню то в случае char* mMenu1[][2] = {.... всегда будет выводить 2. Ну теоретически потому как я пробовал так:
 
char* mMenu1[5][2] = {
{"1. SETUP", "2. TEST"},
{"1. TIME SETUP", "2. GSM SETUP"},
{"1. GSM TEST"}
};
Serial.println(sizeof(mMenu1)/sizeof(mMenu1[0]));
выдает 5!

Так что вопрос - как получить количество элементов в массиве второго уровня остался открытым. Может всетаки есть какая хитрость?

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

Почему не можете? Не понимаю. Заратустра не позволяет?

ну по мойе вере ни Перун ни Велес ничего против не имеют :-) 

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

Andrey12 пишет:
Может быть мне не заморачиваться и сделать еще массив с указанием количества элементов меню?
int elemMenu[] = {2,2,1};
 
Очень неудачное решение. Тогда при люом изменении меню надо будет не забывать менять и этот массив. Оно Вам надо?
 
Тоже была такая мысль. Понял, буду искать другие решения.

 

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

Andrey12 пишет:

Обижаете уважаемый.. пробовал, но что он вам в сериал выводит с таким примером? 3! То есть у меня 3 элемента на первом уровне. 

А мне интересно получить количество элементов в массиве второго уровня, то есть как узнать что в первом массиве второго уровня 2 элемента во втором тоже 2 и в третьем 1?
 
 
А, вон, что Вам надо.
 
Там Вы просто запутались в понятиях. На втором уровне у Вас всегда два элемента (Вы же явно указали [2]). Всегда. То, что Вы (в последней строке) проинициализировали только один из них, а второй оставили неинициализированным - это Ваша проблема, там их всё равно два. Понимаете? И память выделена под два указателя.
 
Если хотите по-простому, то можно делать так - условиться, что последний всегда пустая строка. Тогда Вы легко узнаете, где последний и сколько их всего. Но так менюшки не делают. Почему? Потому, что это ненужный расход памяти. Вот смотрите:
char* mMenu1[][5] = {	// здесь три элемента по 5 указателй в каждом!
	{"1. SETUP", "2. TEST", "3. TEST", "4. TEST", ""},	// Все пять указателей при деле
	{"1. TIME SETUP", "2. GSM SETUP", ""},	// Три указателя испоользуются, а два - не при делах
	{"1. GSM TEST", ""}	// Два указателя испоользуются, а три - не при делах
};

Понятно, что Вы попусту растратили память на пять указателей? Она у Вас что, лишняя? В реальных больших меню будет гораздо больше.

По уму, если меню имеет структуру "дерева", то и делать её надо в виде дерева, а не в виде массива. Собственно, так всегда и делается.

 
Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

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

А, вон, что Вам надо.

 
Там Вы просто запутались в понятиях. На втором уровне у Вас всегда два элемента (Вы же явно указали [2]). Всегда. То, что Вы (в последней строке) проинициализировали только один из них, а второй оставили неинициализированным - это Ваша проблема, там их всё равно два. Понимаете? И память выделена под два указателя.
 
Если хотите по-простому, то можно делать так - условиться, что последний всегда пустая строка. Тогда Вы легко узнаете, где последний и сколько их всего. Но так менюшки не делают. Почему? Потому, что это ненужный расход памяти. Вот смотрите:
char* mMenu1[][5] = {	// здесь три элемента по 5 указателй в каждом!
	{"1. SETUP", "2. TEST", "3. TEST", "4. TEST", ""},	// Все пять указателей при деле
	{"1. TIME SETUP", "2. GSM SETUP", ""},	// Три указателя испоользуются, а два - не при делах
	{"1. GSM TEST", ""}	// Два указателя испоользуются, а три - не при делах
};

Понятно, что Вы попусту растратили память на пять указателей? Она у Вас что, лишняя? В реальных больших меню будет гораздо больше.

По уму, если меню имеет структуру "дерева", то и делать её надо в виде дерева, а не в виде массива. Собственно, так всегда и делается.

 
Спасибо за рекомендации и разъяснения.
 
Да, теперь все правильно. И то что я указываю mMenu1[][5] это не моя идея :-)  не хочет по другому ардуина. Ругается на такой код  - mMenu1[][]
 
sketch_jan29a:5: error: declaration of 'mMenu1' as multidimensional array must have bounds for all dimensions except the first
declaration of 'mMenu1' as multidimensional array must have bounds for all dimensions except the first
 
У меня всего 2 уровня меню. Понял, попробую посмотреть в сторону деревьев. Если будет по ресурсам лучше и по коду не сильно сложно то хорошо. Если нет остановлюсь всетаки на массивах.
 
Памяти много не бывает, делаю под MEGA, ее (памяти) пока хватает.
Но есть желание потом портировать на UNO, так как UNO дешевле, да и ATMega328 распаять на плате можно самому, то есть будет еще дешевле.
А вот ATmega2560 я распаять не смогу :-( придется всегда брать плату у братьев китайцев.