Официальный сайт компании Arduino по адресу arduino.cc
ООПные изыскания на тему многоуровневого меню
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Традиционно всем бобра! :)
С кнопками с помощью Евгения (спасибо еще раз! ) разобрались . Настал черед меню. Можно взять готовое? Наверняка можно, и даже нужно, но в моем случае надо еще и учиться писать, тренироваться на кошках так сказать. Так что не гневайтесь! :)
Написал в Visual Studio т.к. дебаг нужен был. В общих чертах - работает. Даже внял некоторым советам из ветки про кнопки.
Итак, количество уровней - не ограничено, разве что памятью, количество пунктов в уровне - в принципе тоже не ограничено, но в написанном макс = sizeof(uint8_t). Создание пукта меню - простым созданием экземпляра класса, с указанием родительского пункта. Сделал типа на двунаправленых связаных списках, но экзепляр содержит указатели на дочение пункты в контейнере. Как сделать по-другому - не допер.
собсно код:
#include <iostream> #include <conio.h> using namespace std; class MyMenu { protected: string name; MyMenu* parent;//указатель на родительское меню MyMenu** ptr;//контейнер ссылок на дочение меню unsigned char children;//количество дочерних меню uint8_t index;//индекс данного экземпляра в контейнере у родителя const static uint8_t _size = sizeof(MyMenu*); public: MyMenu(string n = "base", MyMenu* p = nullptr) : name(n), parent(p), ptr(nullptr), children(0), index(0) { if (p != nullptr) { if(parent->children < sizeof(unsigned char)-2)parent->children++;// у родителя увеличили кол-во детей else { delete this; return; }// не уверен что так можно :) void* some = realloc(parent->ptr, parent->children * _size);//перевыделили память под контейнер if (some != nullptr)parent->ptr = (MyMenu**)some; parent->ptr[parent->children - 1] = this;//указатель на экземпляр занесли в контейнер родителю index = parent->children - 1;// и запомнили интекс данного экземпляра в контейнере у родителя } } void moveUp(MyMenu*& p) const{// ф-ия перемещения курсора вверх if (p->parent == nullptr)return;// если нет родителя - уходим if (p->index > 0) {//если этот экз не первый на уровне p = p->parent->ptr[p->index - 1];//то курсор переходит на позицию выше p->drawMenu(p);// прорисовка меню и курсора return; } else {// если курсор смотрит на первую строчку меню на уровне if (p->parent->parent == nullptr)return; else p = p->parent->parent->ptr[0];//то курсор переходит на первую строчку на уровень выше p->drawMenu(p);// прорисовка } } void moveDown(MyMenu*& p) const {// ф-ия перемещения курсора вниз if (p->index < p->parent->children - 1) {//если курсор смотрит не на последнюю строку p = p->parent->ptr[p->index + 1];// то курсор переходит на строку вниз p->drawMenu(p);// прорисовка return; } } void moveIn(MyMenu*& p) const {// ф-ия выбора пункта меню if (p->children == 0) {// если у данного экз нет детей cout << "jumped to " << p->name << " handler\r\n";// то прыгает в обработчик данного пункта return; } p = p->ptr[0];// либо же курсор перемещается на первую строчке уровнем вниз p->drawMenu(p); return; } void drawMenu(MyMenu* p)const {// ф-ия прорисовки меню if (p == nullptr)return; cout << endl; for (int i = 0; i < p->parent->children; i++) {// цикл по кол-ву детей у родителя данного экземпляра cout << i << ". " << parent->ptr[i]->name;// печатаем имя if (p == parent->ptr[i])cout << "<-";// отмечаем пункт на который указывает курсор cout << endl; } } MyMenu* operator=(MyMenu* p) { return this; }// перегружает = в нужном нам контексте }; MyMenu base;// создаем самый базовый экземпляр, он не имеет родителя MyMenu line1("line1", &base);// создаем пункты меню , указывая родителя MyMenu line2("line2", &base); MyMenu line3("line3", &base); MyMenu line4("line4", &base); MyMenu line5("line5", &base); MyMenu line6("line6", &base); MyMenu line7("line7", &base); MyMenu line8("line8", &base); MyMenu line9("line9", &base); MyMenu line11("line11", &line1); MyMenu line12("line12", &line1); MyMenu line13("line13", &line1); MyMenu line21("line21", &line2); MyMenu line22("line22", &line2); MyMenu line23("line23", &line2); MyMenu line211("line211", &line21); MyMenu line212("line212", &line21); MyMenu line213("line213", &line21); MyMenu line221("line221", &line22); MyMenu line222("line222", &line22); MyMenu line223("line223", &line22); MyMenu line224("line224", &line22); MyMenu line225("line225", &line22); MyMenu line231("line231", &line23); MyMenu line31("line31", &line3); MyMenu line32("line32", &line3); MyMenu line33("line33", &line3); MyMenu line111("line111", &line11); MyMenu line112("line112", &line11); MyMenu line113("line113", &line11); MyMenu line114("line114", &line11); MyMenu line1111("line1111", &line111); MyMenu line1112("line1112", &line111); MyMenu line1113("line1113", &line111); MyMenu line1114("line1114", &line111); #define KEY_ESC 27 #define KEY_ARROW_UP 72 #define KEY_ARROW_DOWN 80 #define KEY_ARROW_RIGHT 77 int main() {// представим, что при возникновении события меню мы попадает сюда static MyMenu* p = &line1;// создаем курсор , он указывает на верхний пункт верхнего уровня p->drawMenu(p);// прорисовка int c = 0; while ((c = _getch()) != KEY_ESC) {// пока не нажали Escape switch (c) { case KEY_ARROW_UP: p->moveUp(p); break;// если стрелка вверх case KEY_ARROW_DOWN: p->moveDown(p); break;// вниз case KEY_ARROW_RIGHT: p->moveIn(p); break;// вправо } } }
В чем я не уверен, так а стоит ли так вообще делать: динамический контейнер для хранения указателей на дочерние пункты. Не приведет ли это к перерасходу памяти из-за фрагментации?
Потом, с точки зрения расхода памяти, что лучше , дополнительный член 1 байт, или дополнительные метод, чтоб вычислить то значение, что хранится сейчас в члене index.
При количестве пунктов 100 обработку пунктов(пока не прикрутил) делать колбеками или виртуальными ф-циями? Все конечно интересует применительно к МК.
как в конструкторе убить экземпляр, если например выходишь за пределы по кол-ву пунктов одного уровня?
Глум, веселье, советы и критика - все велкам! :)
Пардон, я не понял о чем Вы.
UPD. видимо об этом: http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogourovnevogo-graficheskogo-menyu#comment-530773
я не видел этой ветки, жаль.
Всё смешалось в доме Облонских... Значит не Вам, но форум читать полезно. Например http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/sozdanie-mnogo... и много других. Он точно на днях давал ссылку на его видение построения меню. И на раздел ссылки обратите внимание.
Вообще правила хорошего тона форума предполагают всестороннее ознакомление с существующими темами и только потом, если остались непонятки задавать вопрос. Честно говоря очень трудно найти тем не обсуждавшихся на форуме. И поиск работает. А писать по нному кругу одно и тоже разным новичкам - точно не то чего хотелось бы на форуме.
о, там и мой скромный опыт построения меню есть. Без классов, но с деревьями :)
Да уж. Только глянул на структуру Евнения, и пазл в башке сошелся :) все переписалось легко и красиво. И заработало сразу! Всем спасибо!
А результат на общее обозрение?
Я, правда, при разработке меню исходил не из идей ООП, а из идей минимизации:
- памяти, занимаемой структурой меню (в проекте по факту менее одного байта на пункт меню, при том, что все оно хранится в ОЗУ, т.к. формируется динамически),
- работы программиста по его созданию. Поэтому, собственно, и динамическое меню - чтобы программист не проектировал вручную все структуры данных с перекрестными ссылками, а просто присоединял нужные пункты меню в нужные места.
http://arduino.ru/forum/proekty/menyu-dlya-dvukhstrochnogo-displeya
В новом проекте этот подход уже обобщен на создание многострочного меню с прокруткой пунктов на графическом дисплее, но этот вариант пока нигде не выкладывал.
А результат на общее обозрение?
Да без проблем! :)
Код стал на два порядка надежнее, ну и попроще. Создание экземпляров усложнилось, но это мелочи.
47я строка каробит правда... надо было ее так написать:
else if (p->parent != nullptr and p->parent->parent != nullptr ) {
Я, правда, при разработке меню исходил не из идей ООП, а из идей минимизации:
ПС. видосик прикольный
Вот только если бы пойти по пути цифрового автомата и создания поддержки упрощенной html то пошло еще эффектней.
А на каком проце делали?
Вообще-то от "проца" это не зависит. В конкретных проектах использовался либо Mega, либо stm32. Но, исходя из ресурсоемкости, вполне может быть использовано семейство Ардуин на 328.
ПС. видосик прикольный
А жена раскритиковала, говорит, "засыпаешь на второй минуте".
А жена раскритиковала, говорит, "засыпаешь на второй минуте".
Много они понимают! :) я на видосиках про их пиллинги-шмиллинги засну на 5й секунде! :))
Я, правда, при разработке меню исходил не из идей ООП, а из идей минимизации....
Вообще-то от "проца" это не зависит....
Тогда на минимизации чего?