Создание многоуровневого графического меню

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Картинки - иконки все одинаковые по размеру. Выводятся tft.pushImage(x1, y1, sizeX, sizeY, file); Где х1, y1 начальная точка, sizeX, sizeY - размер картинки. file - сам файл с изображением.

меню1 в примере Евгения выглядит так:

SMenuItem m1("Menu1", nullptr, & m0);

Для меня должно быть так:

SMenuItem m1(x1,y1, sizeX, sizeY, menu1, nullptr, & m0);

Вопрос x1,y1, sizeX, sizeY - integer. file - ???

И далее как править конструктор

SMenuItem(const char * const _itemText, SMenuItem * _prev = nullptr, SMenuItem * _parent = nullptr) {
    itemText = _itemText;
    prev = _prev;
    parent = _parent;
    children = nullptr;
    next = nullptr;

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

Туцик пишет:

Для меня должно быть так:

SMenuItem m1(x1,y1, sizeX, sizeY, menu1, nullptr, & m0);

неверно.

Во-первых, если все картинки одного размера,  зачем его(размер) указывать в каждой строке меню? Задайте  его один раз в глобальной переменной и все.

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

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

С размером картинки мне понятно. А со всем остальным не понятно. Где это все и как указывать?

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

b707 пишет:
зачем его(размер) указывать в каждой строке меню? Задайте  его один раз в глобальной переменной и все.
Зачем в глобальной? В статическом члене структуры menuItem - так техничнее.

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

Туцик пишет:

С размером картинки мне понятно. А со всем остальным не понятно. Где это все и как указывать?

мне кажется, вы путаете функцию рисования картинки на экране и описание структуры меню в программе. Это абсолютно несвязанные вещи.

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

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

 

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Я понимаю, что в строке SMenuItem m1("Menu1", nullptr, & m0); 

nullptr, & m0 это и есть описание структуры пункта меню и его нахождение в структуре древа. Но  const char * itemText - "Menu1" это привязка непосредственно к первому пункту меню.

Где мне и как поменять "Menu1" на иконку1 ???

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

Туцик пишет:

Где мне и как поменять "Menu1" на иконку1 ???

Вы сами не понимаете, что за ерунду вы спрашиваете :(

блин, если я вам напишу так:

SMenuItem m1(ikonka1, nullptr, & m0);

вам это поможет? - если да, пользуйтесь. это правильный ответ.

Но вообще я сильно сомневаюсь. что вы его поймете.

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

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Тогда придется менять строку const char * itemText; на

int * itemText ?? так?

далее

SMenuItem m1(ikonka, nullptr, & m0);

а далее

switch (ikonka) {

 case 1:

tft.pushImage(1, 8, 50, 52, menu1);

 case2: 

tft.pushImage(56 ,8, 50, 52, menu2);

} так???

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

Туцик

самому Вам этого не сделать. Не сегодня. Прокачаться надо. Начните с чего-нибудь попроще.

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

Туцик пишет:

Тогда придется менять строку

const char * itemText;

на int * itemText ?? так?

не угадали.

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

Сейчас вы их не понимаете и пишете ерунду.

 

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

А почему нельзя все сделать в виде?

SMenuItem m1(x1,y1, 50, 52, menu1, nullptr, & m0);

SMenuItem m2(x2, y2,50, 52, menu2, & m1, &m0);

 

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Мне не понятен знак * или умножение

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

Туцик пишет:

А почему нельзя все сделать в виде?

ну сделайте. Только потом не спрашивайте : "А как мне из этого сделать меню...?"

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Я не вые..ться сюда пришел. А прошу помощи у знающих людей. Если бы знал, то не делал бы вам мозги.

Мне не понятен знак умножения или звездочка в данном случае: const char * itemText;

И судя по всему я действительно искал не там, идентичность каждого пункта меню перед скобками. А именно

SMenuItem m1("Menu1", nullptr, & m0);

m1, m2, m3, m4, m411 и тд...

 

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

Если это поможет, то звёздочка - указатель.

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

Туцик пишет:

Мне не понятен знак умножения или звездочка в данном случае: const char * itemText;

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

Поэтому я вас просил - разберитесь хотя бы с этой строчкой. "Непонятна звездочка" - и что вы ждете? Берите учебник и ищите, что она значит. И со всем остальным так же!

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

Туцик пишет:
прошу помощи у знающих людей. Если бы знал, то не делал бы вам мозги.

Вы неправильный путь выбрали. Я тоже раньше не знал, а потом прочитал книжку и знаю. И никому мозги в форумах "не делал".

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Не понятно, что за указатель. Я думал указатель это эта строка:

Serial.print(iAmActive ? '*' : ' ');

 

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Я не спорю, только подскажите про что прочитать? Что в поисковике то набрать "что значит зведочка или умножение  в построении меню на ардуино?"

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

Туцик пишет:

Не понятно, что за указатель.

непонятно - возьми книжку и прочитай.

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

Туцик пишет:

Я не спорю, только подскажите про что прочитать?

язык программирования Си

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Спасибо за помощь! Вся помощь свелась к, иди читай учебник по Си. Вообще то это раздел песочница, помощь новичкам. Я не понимаю зачем Вы уважаемый строчите? Не хотите/ не можете объяснить, зачем что то писать вообще? Евгений как смог так и объяснил, но от вас сударь помощи 0.

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

закономерный результат... и зачем я строчил, действительно...

Удачи вам.

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Без обид. У каждого свой уровень познания языка. Я никого не просил написать готовый код. А Ваши посты похожи на викторину угадай ка мелодию. Можно ведь было сразу направить на изучение "указателей в языке си". И от них уже идти к построению меню. Привести пример какой нибудь простой, с использованием тех же указателей. Но мне кажется в моем конкретном случае можно сделать все проще и без костылей. Я говорю о коде на первой странице. Проверю напишу. Мир Вам.

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

Туцик пишет:

Можно ведь было сразу направить на изучение "указателей в языке си".

поверьте, у меня и в мыслях не было отправлять вас учить указатели.

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

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

Так и у нас с вами вышло.

Поверьте, я над вами не глумился и обьяснял вам все всерьез. Просто я не думал. что вам надо с алфавита начинать...

mixail844
Offline
Зарегистрирован: 30.04.2012

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

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

 век живи , век учись ,правда как я понял ,такое есть только в C++ .

mixail844
Offline
Зарегистрирован: 30.04.2012

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

Да и вообще, если класс работает с внешними указателями - это признак неграмотного проектирования программы.

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

в меню есть возможности изменить переодичность полива (поле int),количество полива (поле float) ,оба поля могут быть инкрементированы и декрементированы кнопками экранчика..соотвественно "поливалка" ,когда поливает , использует эти поля что бы поливать . так же эти поля(количества полива и переодичнсть) могут быть изменены из приложения на смартфоне посредством команды через модуль bluetooth в устройстве ,так же модуль bluetooth может опрашивать и передавать в приложение на смартфоне текущее состояние всех пераметров меню

так же , есть еще одно поле float ,индикатор оставшиейся жидкости в канистре ,который уменьшается с каждым поливом на величину указываемую в поле "количество полива" .

а так же есть еще одно action поле при использовании которого ,значение поля канистры остатков жидкости сбросит на некое дефолтное число .

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

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

class menu
{
  record *firstChild;
  record *selectedChild;
  int childCount;
  class menu *next;
  class menu *prev

  static outputBuff[ROW_NUM][COL_NUM];
  
 };

class record
{
  int id;
  class record *next,
  class record *prev
  menu *parent;
  bool dirty;
  virtual void increase()=0; 
  virtual void decrease()=0;
  virtual void serialize(char* outBuf) =0;


};

class recordFloat: record
{
  float *value;
  float  max_limit ;
  float min_limit;
  float  step;
  void increase();
  void decrease();
  void serialize(char* outBuf);

class recordInt : record
{
  int *value;
  int  max_limit ;
  int min_limit;
  int  step;
  void increase();
  void decrease();
  void serialize(char* outBuf);
};

смысл был в том, что для того что бы другие модули(полива , bluetooth и.т.д) видели изменения сделанные через меню , в консруктор класса record передавался указатель на переменную соответствующего типа (а так же другие требуемые параметры ).

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

А что такое "видел"? Модуль БТ самовольно лезет в список и получает доступ к чужим переменным или Вы его в прошивке сами пускаете к необходимым?

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

Туцик пишет:

Вся помощь свелась к, иди читай учебник по Си. Вообще то это раздел песочница, помощь новичкам. 

Поймите, на форуме можно подсказать, если человек в принципе знает, но какого-то нюанса не понял. Если же человек не знает вообще ничего (Ваш случай), подсказать ему невозможно - он не поймёт подсказки, т.к. базы нет. Поэтому в Вашем случае - помощь может быть только такой - дать учебник. По-другому - никак.

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

mixail844 пишет:

век живи , век учись ,правда как я понял ,такое есть только в C++ .

Почему? Нет. Практически во всех языках есть.

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

ЕвгенийП

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

Проблему решил, все оказалось проще как и предполагал. Без костылей и без флажков. Все делается в одном switch. Хотя Ваш пример считаю более грамотным.

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

Туцик пишет:

Проблему решил, все оказалось проще как и предполагал.

а знаете, почему решили? - потому что сами.

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

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

Тогда в принципе логичней поставить автомат на форуме, который бы печатал после каждого поста мседж "Иди читай учебник по Си"

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

Тем, кто пришол сюда вообще без знаний, с криками "Памагити!!!" такой автомат точно не помешал бы

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

Туцик пишет:

Тогда в принципе логичней поставить автомат на форуме, который бы печатал после каждого поста мседж "Иди читай учебник по Си"

Хорошая идея!

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

DetSimen пишет:

Тем, кто пришол сюда вообще без знаний, с криками "Памагити!!!" такой автомат точно не помешал бы

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

Туцик
Туцик аватар
Offline
Зарегистрирован: 31.03.2020

При регистрации еще парт билет просите и справку с налоговой.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Не знаю, как у ТС, а мне тема зашла.
Практически весь используемый в менюшках функционал, обработку энкодера и вывод на дисплей, реализовал в 110 строках.
Свой вариант с динамическим объявлением уронить так и не смог.
Но , послушав старших товарищей, на всякий случай , сделал и второй вариант, полностью статический.

Единственный вопрос так и остался нерешенным, ну и фиг с ним. Костыль работает :-)

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

Kakmyc пишет:
Свой вариант с динамическим объявлением уронить так и не смог.
Да нет там у Вас никакого обновления. А уронить - как да пальца. Держите:

#include <Printing.h>

typedef struct  menuItem {
	char *name; //первые 4 пункта понятны должны быть
	struct menuItem *prev;
	struct menuItem *next;
	struct menuItem *ok;
	byte func_num; //функциональное назначение: 1 проходное меню, 2 меню из которого меняются переменные, 3 вызываем обработчик
	void (*Handler)(); //название обработчика
	int value; //в моем случае номер элемента массива с переменными
} menuItem;


void menu() {
	menuItem m1;
	m1 = {"Setup menu", NULL, NULL, NULL, 1};
	static menuItem *curItem = &m1;
	
	// Для проверки создания меню, 
	// печатаем имя первого элемента
	Serial.print("Item name: ");
	Serial.println(curItem->name);
}

int  getValue(const int n) {
	menu();
	String s = "Example #";
	s += n;
	return n ? getValue(n-1) : 0;
}

void setup(void) {
	Serial.begin(57600);
	getValue(1);
}

void loop(void) {}

С какого перепою при втором проходе имя не печатается?

Только если начнёте спорить, например, "ситуация искусственная! у меня такого не будет!" - просто пошлю на, так и знайте. Жизнь Вам и не таких ситуаций накидает. Сказано "нельзя так делать", значит нельзя - деды не глупее нас были).

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

Kakmyc пишет:
вывод на дисплей

Вывод не дисплей в смысле только пункты одного уровня или все меню скопом?

Если один уровень, то можете подсказать куда копать?

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

Евгений, можете помочь разобраться с кодом?

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

        Serial.print(iAmActive ? '*' : ' ');

Я так понимаю, что в Serial.print(iAmActive ? '*' : ' '); производится сравнение. Если iAmActive  возвращает true, то печатаем "*", иначе пробел. А вот с

        for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
            if (ptr->parent) Serial.print("   ");

вообще вообще запутался...

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

vdm пишет:
А вот с

        for (SMenuItem * ptr = parent; ptr; ptr = ptr->parent) {
            if (ptr->parent) Serial.print("   ");

вообще вообще запутался...

В чём именно Вы запутались? Это самый обыкновенный цикл for. В скобках три выражения, разделённые точкой с запятой, а в фигурных скобках "тело цикла".

Работает точно, как описано в руководстве:

  1. выполняется первое выражение (SMenuItem * ptr = parent)
  2. Вычисляется второе выражение (ptr). Если оно равно нулю, то переход к п. 6
  3. Выполняется тело цикла (то, что в фигурных скобках)
  4. Выполняется третье выражение (ptr = ptr->parent)
  5. Переход к п. 2.
  6. Цикл закончен, продолжаем выполнять программу

Точно так работает абсолютно любой цикл for хоть в С, хоть в С++.

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

Запутался я вот с этим выражением: ptr = ptr->parent

В арифметических, условных и операторах сравнения, доступных на сайте, такого знака нет ->. Это не значит, что его вообще нет. Это значит, что я не нашел. 

И как мне остановить вывод пунктов меню только одного уровня одного родителя?

Kakmyc
Offline
Зарегистрирован: 15.01.2018

vdm пишет:

Запутался я вот с этим выражением: ptr = ptr->parent

В арифметических, условных и операторах сравнения, доступных на сайте, такого знака нет ->. Это не значит, что его вообще нет. Это значит, что я не нашел. 

И как мне остановить вывод пунктов меню только одного уровня одного родителя?

А что Гугл отвечает на запрос "-> C++" ?

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

vdm пишет:

Запутался я вот с этим выражением: ptr = ptr->parent

Это обращение к полю (методу) класса, если имеется указатель на объект. Вас не смущает запись через точку типа Serial.begin ? Ну, а если у Вас есть не сам Serail, а указатель на него, то вместо точки ставится ->

Serial.begin(9600);
auto pSerial = & Serial; // берём адрес Serial в указатель
pSerial->begin(9600); // это РОВНО ТОЖЕ САМОЕ, что было в строке №1

vdm пишет:

И как мне остановить вывод пунктов меню только одного уровня одного родителя?

Берёте этого родителя и вызываете его метод showChildren с параметром 1 (типа roditel.showChildren(1); )

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

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

Берёте этого родителя

 

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

    Serial.print(parent);

Выдало ошибку. 

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

Ну, так не делается. Вы же ID то не печатаете вообще, так с чего бы ему печататься? Тут надо что-то вроде Serial.print(parent->id);

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

Спасибо.

Почему 

    Serial.print(itemText);

Возвращает незвание пункта меню, а

    Serial.print(parent);

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

m0.showChildren(1);

 

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

vdm пишет:

Почему 

    Serial.print(itemText);

Возвращает незвание пункта меню, а

    Serial.print(parent);

выдает ошибку. 

Ну, Вы кода не приводите. Если это внутри метода, то потому, что itemText - это поле, а parent - указатель на объект

vdm
vdm аватар
Offline
Зарегистрирован: 29.08.2016

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

Ну, Вы кода не приводите.

Да код то Ваш, целиком и полностью. Пытаюсь разобраться, как сменить m0 в

m0.showChildren(1); // Печатаем меню

На другой ID для вывода только детей активного уровня.

Хотел через switch и ID записать в itemText, но мракобесие получается. В моем случае будет 19 экранов. 

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

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

Я не понимаю, что такое "дети активного уровня". Вы хотите, чтобы среди детей были разные по функциональности? Ну тогда надо либо расширить дерево (дать им разных родителей), либо просто скрывать их (у меня там был для этого метод). Давайте Вы таки нарисуете совё дерево и приведёте код.