Как сделать меню flash

Обновлено: 03.07.2024

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

Попутно, из проекта в проект, развивалась своя псевдоОС — таймеры, события, диспетчеры.
Я ее полностью писал на си, оптимизацией и вылизыванием еще не занимался.

Перебирая разные системы, наткнулся на MicroMenu:

Попробуем разобрать ее на части и прикрутить к системе.

Структура данных:
Меню организовано в виде четырехсвязного списка. Каждый элемент меню (пункт меню) ссылается на предыдущего и последующего элемента, также ссылается на своего родителя (пункт меню предыдущего уровня) и потомка (пункт подменю). Если это первый пункт, то предыдущего элемента у него нет, соответствующая ссылка пустая.

Изобразим это на рисунке:

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

  • Перейти вверх или вниз (предыдущий или следующий пункт)
  • Вернуться в родительское меню (если есть)
  • Выбрать текущий пункт. При этом мы или переходим в подменю (ели оно есть), или выполняется команда, прикрепленная к этому пункту меню.

Соответственно, все эти действия отражают четыре указателя. В оригинальной системе указатель на потомка обозван SIBLING, но я считаю это идеологически неверным. Sibling – это родственник того же уровня. Брат или сестра. Но никак не потомок. Поэтому мы будем использовать идеологически выверенное CHILD.

Итак, описание структуры пункта меню:

Добавлен байт Select – это код команды, привязанный к текущему пункту. Если у данного пункта есть подменю, код нулевой. Также есть поле Text. Капитан Очевидность подсказывает, что это, собственно, текст пункта меню. По расходам памяти — на каждый пункт меню расходуется 9 байт плюс длина текстовой части. И это все — кладется во флеш.

Самое полезное, почерпнутое у MicroMenu – набор дефайнов для быстрого и удобного определения меню.

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

Теперь самое интересное: описание структуры меню, как на рисунке.

Естественно, пункты меню можно описывать и вперемешку, в порядке обхода дерева. Типа такого:

MAKE_MENU(m_s1i1, m_s1i2, NULL_ENTRY, NULL_ENTRY, m_s2i1, 0, "Запуск"); // подменю Запуск MAKE_MENU(m_s2i1, m_s2i2, NULL_ENTRY, m_s1i1, NULL_ENTRY, MENU_MODE1, "Режим 1"); MAKE_MENU(m_s2i2, m_s2i3, m_s2i1, m_s1i1, NULL_ENTRY, MENU_MODE2, "Режим 2"); MAKE_MENU(m_s2i3, NULL_ENTRY,m_s2i2, m_s1i1, NULL_ENTRY, MENU_MODE3, "Режим 3"); MAKE_MENU(m_s1i2, m_s1i3, m_s1i1, NULL_ENTRY, m_s3i1, 0, "Настройка");

Можно даже пойти дальше — строить меню в какой-нибудь визуальной среде, а потом автоматически генерировать такой список. Но это на потом.

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

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

Дальше. Как ходить по меню. Автор предлагает несколько дефайнов. Я их сохранил, хотя можно и без них обойтись.

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

Далее, процедура реакции на нажатие клавиш (в качестве параметра передается код нажатой клавиши):

Процедура отрисовки меню. Зависит от выбранного экранчика, а также от используемой анимации при выборе. Например у меня экранчик 128х64 точки, текущий пункт меню всегда по середине экрана, сверху и снизу выводятся два предыдущих и два последующих элемента (если есть). Отрисовка вызывается после каждого нажатися на кнопку и по таймеру, два раза в секунду. Мало ли, может изменится что.
В шапке можно выводить текст родителя, чтобы знать, где находимся. Можно двигать курсор по пунктам, а не пункты прокручивать. На вкус и цвет все фломастеры разные.

И последний штрих — инициализация меню:

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

Краткое описание того, что делает процедура setHandler — она привязывает обработчик к событию. В данном случае, при возникновении события MSG_KEY_PRESS вызовется функция keyMenu для обработки этого события.

Для демонстрации системы меню, описанной в предыдущем посте, собрал модель в протеусе. На базе двухстрочного LCD-индикатора, контроллераatmega32 и пяти кнопок (влево-вправо-вверх-вниз и выбор). В своих схемах использую джойстики от мобилок, они тоже пятипозиционные. Также воткнул три светодиода, чтобы хоть как-то реагировать на выбор пунктов меню.

Обработка меню:

  • при выборе пунктов Start/Mode 1, Start/Mode 2, Start/Mode 3 загорается соответствующий светодиод
  • при выборе пункта Reset — все светодиоды гаснут
  • после выбора любого конечного пункта, возвращаемся обратно в корень меню.

Некоторые модификации, связанные с моделированием:

  • заменил весь текст на английский, потому что в оригинале модель экранчика не поддерживает русский язык. Да, я знаю, про замену dll, но не у всех она есть, а для просмотра — пойдет.
  • моя боевая библиотека работы с LCD почему-то отказалась работать с моделью. Поэтому взял какую-то первую попавшуюся из древнего проекта.
  • отключил автоповтор кнопок (на модели неудобно работать), но он есть

Ну и, надеюсь, мне простят подключение светодиодов без балластного резистора? ;)

Файлы к статье

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

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

121 thoughts on “Организация древовидного меню”

Прикольно.
А как организовать такое меню на 7-сегментных индикаторах?

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

Все то же самое, только вместотекста — цифры )

Проектик, не помешал бы.

В конец знакогенератора (здесь это DcMatrix) дописываем новых символов (например А Б В Г Е и т.п.). Дальше просто выводим символы как и обычные цифры, только что код >9.

P.S. Надеюсь не сильно туплю, время позднее …

На холодильных установках стоят веселые менюшки :) Как они бедные извращались, чтобы названия параметров вместить в 3 семисегментника …

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

Просто пронумеровать пункты, а расшифровку в инструкцию. И пусть читают.

namespace LedTest
public partial class Form1 : Form
bool ledOn = false; // Флаг включен (true) или нет (false) светодиодик на плате
ushort vid = 0x16C0, pid = 0x05DC; // Тут комментарии излишни — это VID и PID
ATMega8 dev; // Объявляем объект типа ATMega16(так в оригинале я заменил на М8)

public Form1()
InitializeComponent();
>

>
Close();
>
else // Если все хорошо, настроим микроконтроллер по USB
dev.DDRB |= 0x3f; // Пины bx00111111 порта B — на вывод
dev.PORTB &= 0x00; // Выключим светодиодик на плате
dev.DDRC |= 0x3f; // Пины bx00111111 порта C — на вывод
dev.PORTC &= 0x00; // Выключим светодиодик на плате
dev.DDRD |= 0xeb; // Пины bx11101011 порта D — на вывод
dev.PORTD &= 0x00; // Выключим светодиодик на плате
//dev.DDRA |= 0x55; // для меги 16-32
//dev.PORTA &= 0x00; // Выключим светодиодик на плате
>
>

// работа с портом B ++++++++++++++++++++++++++++++++++++++++++++++++++++++
private void button1_Click(object sender, EventArgs e)

ledOn = !ledOn;
if (ledOn)

dev.PORTB |= 0x01; // Включим светодиодик на плате
//System.Threading.Thread.Sleep(5000); // это таймер

dev.PORTB &= 0x00; // Выключим светодиодик на плате
dev.PORTC &= 0x00; // Гасим все в порту С это на пробу!
dev.PORTD &= 0x00; // Гасим все в порту D это на пробу!
panel1.BackColor = Color.LightYellow;
panel2.BackColor = Color.LightYellow;
panel3.BackColor = Color.LightYellow;
panel4.BackColor = Color.LightYellow;
panel5.BackColor = Color.LightYellow;
panel6.BackColor = Color.LightYellow;
panel7.BackColor = Color.LightYellow;
panel8.BackColor = Color.LightYellow;
panel9.BackColor = Color.LightYellow;
panel10.BackColor = Color.LightYellow;
panel11.BackColor = Color.LightYellow;
panel12.BackColor = Color.LightYellow;
panel13.BackColor = Color.LightYellow;
panel14.BackColor = Color.LightYellow;
panel15.BackColor = Color.LightYellow;
panel16.BackColor = Color.LightYellow;
panel17.BackColor = Color.LightYellow;
panel18.BackColor = Color.LightYellow;

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

Есть небольшое замечание:

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

По поводу колбэк функции. Да, в оригинале было сделано именно так. В моей парадигме — возникает событие
sendMessage(MSG_MENU_SELECT, sel);
а кто его будет обрабатывать — это не головная боль меню.

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

Здесь речь не идет о супер-скоростной обработке информации, типа сотни тысяч раз в секунду. Даже если работа с этим деревом будет затянется на десятки миллисекунд (что очень и очень врядли), то все равно это будет быстрее, чем скорость обновления картинки на недорогом ЖК индикаторе. Более приоритетные прерывания во время выполнения этой задачи — тоже не проблема, пусть себе выполняются.

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

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

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

Да, зато нету необходимости ручной работы. Забудешь ссылку поменять (изменилось имя переменной) и кушай гемморой с битой ссылкой. А тут можно компактно обработать рекурсивной функцией. Да займет больше ресурсов, зато красивое и масштабируемое решение. Конечно если пишешь для Тини прийдётся немного попрыгать на углях :3

А я делал подобную систему на нескольких массивах структур — получилось довольно компактно (почти без дыр) и удобно. А главное — почти всё в статике, кроме полей ввода/вывода. Правда доделать до конца пока ещё не успел (появился срочный проект), как руки дойдут, может опишу поподробнее.

Что в данном случае контекст? Какие-то внешние переменные?

Я наоборот хочу все упихать в один модуль, чтобы процедура стала реентерабельной.

Ето меню для WinAVR или еще для Codevision подойдет?

WinAVR. Диалекта Codevision не знаю, скорее всего будут различия в работе с флэшем. А так — связный список он и в Африке связный список.

Переход вправо имхо логичней было бы повесить на кнопку вправо

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

А, точно, это же С… Привык к паскалевскому case.

Проходил через такое описание, когда текст отдельно, а пункты — отдельно.

// массив меню изменения режима меню
PROGMEM t_menu m_mode[] = item_sim(mmm1,menu_one),
item_sim(mmm2,menu_multi)
>;

Мозг можно поломать увидев m_s1i1 — что это.
Только скачав оригинал, понял что имелось ввиду что то типа Level1Item1.

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

У меня тоже как-то встала проблема постоения меню. Честно говоря смотреть что-то готовое даже в голову не приходило, да и тогда наверное этого не так много было. Писал я на Си, знаю я правда его достаточно посредственно( всякие тайпдефы, указатели и прочее я практически не знаю и очень редко применяю, из-за чего иногда получается громоздко).

Задача была аналогичная — сделать меню, на экранчике 128х64, древовидное. Причем напротив некоторых пунктов нужно было сделать чекбокс, а в некоторых возможность ввода чисел, не выходя из интерфейса меню. Впрочем это больше проблемы отображения чем построения самого меню.
Структуру меню я решил хранить ввиде строки для минимального использования памяти

^ — означает начало новой страницы меню,
| — разделяет пункты меню. причем первая строчка после ^ является заголовком страницы, а не пунктом
$ — означает то место куда должно вписываться число, которое можно редактировать с клавиатуры.

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

Кроме всего этого необходимы таблицы переходов, которые выглядели так(специально укоротил чтобы суть ясна была):

//указатель меню
flash signed char menu_page[]= < -2, 1, 1, 1, 2, 4, 5, 3>; // номер страницы меню
flash unsigned char menu_line[]= < 0, 0, 1, 2, 1, 4, 6, 0>; // номер пункта меню
flash signed char menu_to_page[]= < 1, 2, 3, 4, 5, -5, -4, -6>; // куда посылает данный пункт меню

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

ТАБЛИЦА СО ЗНАЧЕНИЯМИ

// Всякие значения в менюшках
// ПЕРВУЮ КОЛОНКУ ОСТАВИТЬ ПУСТОЙ
flash unsigned char page[]= < 0, 0, 5, 5, 5, 5, 5, 5>; // страница меню
flash unsigned char line[]= < 0, 5, 0, 1, 2, 3, 4, 5>; // пункт меню
unsigned int value[]= < 0, 100, 0, 0, 0,59048, 1, 0>; // значение (хранится в оперативной памяти)
flash unsigned int max_value[]= < 0, 1000, 1000, 255,59048,59048, 255, 255>; // максимально допустимое число
flash unsigned char digits[]= < 0, 4, 4, 3, 5, 5, 3, 3>; // сколько позиций отводить на написание числа(минимум)

ТАБЛИЦА С ЧЕКБОКСАМИ
тут все аналогично. в моей программе они пока не использовались, поэтому и пусто везде.

// чекбоксы в меню
// ПЕРВУЮ КОЛОНКУ ОСТАВИТЬ ПУСТОЙ
flash unsigned char box_page[]= < 0, 0, 0, 0, 0, 0, 0, 0>;
flash unsigned char box_line[]= < 0, 0, 0, 0, 0, 0, 0, 0>;
unsigned char box_value[]= < 2, 2, 2, 2, 2, 2, 2, 2>;

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

Конечно к этому всему нужны функции вывода, но они весьма специфичны ибо писались под один конкретный проект и выкладывать их нет смысла.

>Главный недостаток этой схемы — нельзя добавить пункт меню между другими без перенумерации в пределах одной страницы.

Вот это меня всегда убивало и сподвигло на поиск новой системы.

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

запятую пропустил. Да и дальше — строки 7 и 8 вместе не имеют смысла. Строка 11 — нет метки куда переходить.

с запятой та же ситуация..
а в остальном все в порядке…

понимаете в чем тут дело, прога выполняет подсчет совпадений с маской, счетчик в регистре L

Откройте программу, вам будет предложено выбрать шаблон. Если этого не будет или вы случайно закроете его, то выберите команду File- >New –> New from template
Выбираем раздел Navigation Button

Выбираем внешний вид кнопок нашего меню на flash из предложенных:

Добавляем названия кнопок (пункт text), ссылки на страницы (link to), открытие в новом окне (trget _blank).
Ставим флажок напротив “continue adding), если собираемся продолжать добавлять пункты меню.

Когда все пункты меню заполнены, жмем кнопку “Далее”:

Выбираем горизонтальное или вертикальное отображение:

Получили меню:

Сохраняем меню в формате swf при помощи команд File –> Export –> Export Movie…
Загружаем меню во флэш аналогично любому другому swf ролику или на сайт ЯПfiles.

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

Название 123 Flash Menu программа получила на основании того, что руководство пользователя предлагает создать меню за три простых шага: выбор шаблона, модификация шаблона, опубликование. Программа позволяет создавать динамические эффекты (анимация, звуки, прозрачность), но при этом от пользователей не требуется знания программирования. Сотни заранее подготовленных шаблонов дают возможность быстро создать персонализированные меню. Рublish Wizard помогает сгенерировать совершенный HTML-код. Flash-меню будет работать при просмотре различными браузерами. Зарегистрировавшись однажды, можно периодически получать шаблоны с сайта разработчика.

image001 (354x314, 19Kb)

Работа с программой достаточно проста. На первом этапе в окне Wizard Window нужно выбрать категорию шаблона (Тemplate Сategory) в левой панели (рис. 1), после чего соответствующие шаблонные файлы (Тemplate Files), имеющие расширение *fmp, будут перечислены в правой панели. При листании шаблонов можно просматривать их вид в окне Рreview Рane.

На втором этапе можно изменять параметры меню (рис. 2). Существует три настраиваемых элемента — Menu Parameters (Параметры меню), Flash Size (Размер Flash) и Page Property (Свойство страницы). В панели Menu Parameters можно менять направление, размер и цвет меню, шрифт, тип линий, стиль границ, теневой стиль, вид эффектов, вид появления pop-up-окошек. В панели Flash Size, можно настроить ширину и высоту Flash, а в панели Page Property — цвет фона и режим прозрачности.

Maior аватар

" Вы хотите современное Flash (Флеш) меню с впечатляющими эффектами, анимацией, притягивающей взгляд и нетривиальной структурой, но не хотите тратить месяцы на кодирование, отладку и настройку внешнего вида? С помощью Flash Menu Labs Вы получите все эти и многие другие возможности всего за несколько минут!

Flash Menu Labs - это мощный инструмент для быстрого создания Flash (Флеш) меню для Вашего сайта. Даже новички без знания ActionScript могут успешно создавать меню за считанные минуты "

Программа для создания красивых Flash меню для сайта(ов)

Программа для создания красивых Flash меню для сайта(ов)

Программа для создания красивых Flash меню для сайта(ов)

1. Выбираем дизайн будущего меню
2. Подставляем ссылки на свой сайт, меняем стандартные надписи: Например ДОМОЙ меняем на ГЛАВНАЯ.
3. Редактируем меню (убираем лишние пункты, упорядочиваете их и добавляете еще пункты)
4. Ну а дальше: Просматриваем работёнку в браузере, редактируем и нажимаем "сохранить". Вот и всё. Меню готово.

И вот вам еще официальный сайт, где можете скачать пробные версии программ (Flash Menu Labs Standard Edition и Flash Menu Labs Professional Edition) посмотреть обновления, задать вопросы, просмотреть примеры меню и т.д. Ну думаю разберетесь

Читайте также: