Библиотека для работы с кнопкой shButton
- Войдите на сайт для отправки комментариев
Пока наша почта пытается доставить мне кой-какие запчасти, от нечего делать немного поизобретал велосипед. Вроде полезно и для общего развития, и на будущее. В общем изобрел еще одну библиотеку для работы с кнопками.
shButton - библиотека для отработки нажатия кнопки. Возможности:
- Работа с подключением PULL_UP и PULL_DOWN
- Опрос кнопки как с программным антидребезгом контактов, так и без него
- Отработка нажатия, удержания, отпускания кнопки, двойного клика
- Настройка интервалов антидребезга, двойного клика и удержания кнопки
Результат опроса кнопки заносится в поле btnState и может иметь следующие значения
BTN_RELEASED - кнопка отпущена
BTN_UP - кнопка только что отпущена
BTN_PRESSED - кнопка нажата, но время удержания не вышло
BTN_HOLDED - кнопка удерживается нажатой дольше времени удержания
BTN_DOWN - кнопка только что нажата
BTN_DBLCLICK - кнопка только что нажата, двойной клик
Файл shButton.h
#pragma once #include <Arduino.h> /* shButton - библиотека для отработки нажатия кнопки. Возможности: - Работа с подключением PULL_UP и PULL_DOWN - Опрос кнопки как с программным антидребезгом контактов, так и без него - Отработка нажатия, удержания, отпускания кнопки, двойного клика - Настройка интервалов антидребезга, двойного клика и удержания кнопки */ // типы подключения кнопки #define PULL_UP 0 // кнопка подтянута к VCC #define PULL_DOWN 1 // кнопка подтянута к GND // состояние кнопки #define BTN_RELEASED 0 // кнопка отпущена #define BTN_UP 1 // кнопка только что отпущена #define BTN_PRESSED 2 // кнопка нажата, но время удержания не вышло #define BTN_HOLDED 3 // кнопка удерживается нажатой дольше времени удержания #define BTN_DOWN 4 // кнопка только что нажата #define BTN_DBLCLICK 5 // двойной клик class shButton { private: byte _PIN = 0; // пин, на который посажена кнопка word _debounce = 50; // интервал подавления дребезга контактов word _timeout = 500; // интервал удержания кнопки нажатой word _dblclck = 300; // интервал двойного клика byte _clckcount = 0; // счетчик кликов byte _type = PULL_UP; // тип подключения bool _flag = false; // сохраненное состояние кнопки - нажата/не нажата bool _deb = false; // флаг включения подавления дребезга - пока включено, изменения состояния не принимаются unsigned long btn_timer = 0; // таймер удержания кнопки нажатой unsigned long deb_timer = 0; // таймер подавления дребезга контактов unsigned long dbl_timer = 0; // таймер двойного клика // получение мгновенного состояния кнопки - нажата/не нажата с учетом типа подключения и без учета дребезга контактов bool getButtonFlag(); // установка кнопке состояния "только что нажата" или "только что отпущена" void setBtnUpDown(bool flag, unsigned long thisMls); public: // Варианты инициализации: // shButton btn(пин); - с привязкой к пину и без указания типа (по умолч. PULL_UP) // shButton btn(пин, тип подключ.); - с привязкой к пину и указанием // типа подключения (PULL_UP / PULL_DOWN) shButton(byte pin, byte mode = PULL_UP); // получение состояния кнопки - отпущена/нажата/удерживается void getButtonState(); // установка времени антидребезга (по умолчанию 50 мс) void setDebounce(word debounce); // установка таймаута удержания (по умолчанию 500 мс) void setTimeout(word new_timeout); // установка интервала двойного клика void setDblClickTimeout(word new_timeout); // установка типа кнопки (PULL_UP - подтянута к VCC, PULL_DOWN - к GND) void setType(byte type); // текущее состояние кнопки byte btnState = BTN_RELEASED; };
Файл shButton.cpp
#include "shButton.h" #include <Arduino.h> shButton::shButton(byte pin, byte mode) { _PIN = pin; setType(mode); } void shButton::getButtonState() { bool flag = getButtonFlag(); unsigned long thisMls = millis(); // состояние кнопки не поменялось с прошлого опроса if (flag == _flag) { // и не поднят флаг подавления дребезга if (!_deb) { if (!flag) { // кнопка находится в отжатом состонии btnState = BTN_RELEASED; if (millis() - dbl_timer > _dblclck) { // обнулить счетчик кликов, если период двойного клика закончился _clckcount = 0; } } else if (millis() - btn_timer < _timeout) { // кнопка находится в нажатом состоянии, но время удержания еще не вышло btnState = BTN_PRESSED; } else { // кнопка удерживается нажатой дольше времени удержания btnState = BTN_HOLDED; } } } // состояние кнопки поменялось с прошлого опроса else { // если задано подавление дребезга контактов if (_debounce > 0) { // если флаг подавления еще не поднят - поднять и больше ничего не делать if (!_deb) { deb_timer = thisMls; _deb = true; } // иначе, если поднят, и интервал вышел - установить состояние кнопки else if (millis() - deb_timer >= _debounce) { setBtnUpDown(flag, thisMls); } } else // если подавление вообще не задано, то сразу установить состояние кнопки { setBtnUpDown(flag, thisMls); } } } void shButton::setType(byte type) { _type = type; switch (type) { case PULL_UP: pinMode(_PIN, INPUT_PULLUP); break; default: pinMode(_PIN, INPUT); break; } } void shButton::setDebounce(word debounce) { _debounce = debounce; } void shButton::setTimeout(word new_timeout) { _timeout = new_timeout; } void shButton::setDblClickTimeout(word new_timeout) { _dblclck = new_timeout; } bool shButton::getButtonFlag() { bool val = digitalRead(_PIN); if (_type == PULL_UP) { val = !val; } return val; } void shButton::setBtnUpDown(bool flag, unsigned long thisMls) { _deb = false; _flag = flag; if (flag) { // если кнопка только что нажата, то запустить таймер удержания btn_timer = thisMls; if (_clckcount == 0) { // если это первый клик, запустить таймер двойного клика и увеличить счетчик кликов btnState = BTN_DOWN; _clckcount++; dbl_timer = thisMls; } else if (millis() - dbl_timer <= _dblclck) { btnState = BTN_DBLCLICK; _clckcount = 0; } } else { btnState = BTN_UP; } }
Файл keywords.txt
####################################### # Syntax Coloring Map For shButton ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### shButton KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### setDebounce KEYWORD2 setTimeout KEYWORD2 setType KEYWORD2 setDblClickTimeout KEYWORD2 btnState KEYWORD2 getButtonState KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### PULL_UP LITERAL1 PULL_DOWN LITERAL1 BTN_RELEASED LITERAL1 BTN_UP LITERAL1 BTN_PRESSED LITERAL1 BTN_HOLDED LITERAL1 BTN_DOWN LITERAL1 BTN_DBLCLICK LITERAL1
Демо-скетч
#include <shButton.h> // инициализация кнопки (пин, режим кнопки: PULL_UP (значение по умолчанию) - кнопка подтянута // к VCC; PULL_DOWN - кнопка подтянута к GND) shButton but(10, PULL_UP); void setup() { // режим пина устанавливается автоматически // необязательные установки but.setDebounce(80); // установка времени антидребезга, мс (по умолчанию 50 мс) but.setTimeout(800); // установка времени удержания кнопки нажатой, мс (по умолчанию 500 мс) Serial.begin(9600); } void loop() { static int8_t bt = -1; // опрос кнопки, для уверенной обработки состояния кнопки опросы нужно делать как можно чаще but.getButtonState(); if (bt != but.btnState) { bt = but.btnState; switch (bt) { case BTN_RELEASED: // Serial.println("button released"); break; case BTN_UP: Serial.println("button up"); break; case BTN_PRESSED: // Serial.println("button pressed"); break; case BTN_HOLDED: Serial.println("button holded"); break; case BTN_DOWN: Serial.println("button down"); break; case BTN_DBLCLICK: Serial.println("button dblclick"); break; } } }
Скачать одним файлом можно здесь
Просьба к опытным товарищам покритиковать, желательно конструктивно )))
сильно лучше, чем первый вариант скетча для часов. Так сходу прям и поругать не за что :))
продолжайте развиваться, у вас получается
BTN_HOLDED
Исправьте, пожалуйста, глаза режет. Это неправильный глагол, его формы: hold, held, held.
Это "конструктивно"?
Исправьте, пожалуйста, глаза режет. Это неправильный глагол, его формы: hold, held, held.
Это "конструктивно"?
Вполне. У меня с английским туговато, это я подсмотрел, кажется, у Гайвера. BTN_WITHHELD будет нормально?
//Просьба к опытным товарищам покритиковать, желательно конструктивно )))
Это можно.
1. Пример, строки 22-23. Логично возвращать состояние как результат but.getButtonState(); В конце концев она ж так и называется. А but.btnState; можно убрать, но и оставить не вредно.
2. Состояния кнопок BTN_хххх. В принципе подход верный, но там не все - именно состояния. Например BTN_UP, BTN_DBLCLICK - скорей события возникающие единоразово в процессе обработки нажатий. В теории это грех. На практике почти пофиг, но и порядок их нумерации тоже задан произвольно. Отсюда простой лайфхак - состояния все подряд нумеруем от 0, а события за ними.
3. Явно не хватает "логического клика" - событие из числа BTN_хххх, возникает разово, после отпускания кнопки через интервал времени исключающий возможность дальнейшего даблклика. На него тогда вешается обработчик обычного нажатия и он тогда не срабатывает в других случаях.
4. Иногда по ходу программы возникает нужда просто узнать нажата кнопка или нет. Ну просто проверить 0 или 1, игноря все остальные подробности. Удобно это делать через тот же but.getButtonState(), но указав ему параметром специфику. Или завести еще метод.
5. Даблклик есть, а где длинное нажатие? Не совсем понялBTN_HOLDED, может оно?
6. Настройки таймингов. Ну подумайте, кто по ходу работы будет их менять? Никто и никогда. Максимум при ините зададут, если умолчания не устроят. Экономим ресурс, делаем их константами. Шаблонами можно поигратся, если не страшно ;)
7.Формирование временных интервалов. Их много, миллисов да еще с лонгами тоже. Это усложняет проверки и забирает ресурсы. Введите внутри либы дискретный интервал, чаще которого она не отрабатывает. Чего ей ловить тысячи раз в сек? Например в примере ниже такт 20мсек. Тогда нужные интервалы типа word _timeout = 500;, превратятся в байтовые счетчики uint8_t cnt_timeout. Когда надо его запустить то присвоим ему значение 500/2=25. На каждом такте 20мсек вычитаем 1. Как получится ноль - интервал завершен. Да, точность снизится, но никто не заметит что вместо 500мсек прошло 519.
8. В примере логика проверки повторного состояния на переменной bt портит впечатления.
Мой аналогичный код. Не из библиотеки, прямо из скетча, кнопка енкодера обрабатывается.
Попробую отвертеться )))
1. Возвращать, конечно, можно, где-то это будет экономить одну строку кода. Но убирать but.btnState тоже не стоит, имхо. Почему? Потому, что это значение может понадобиться не один раз за итерацию и тогда либо каждый раз вызывать but.getButtonState(), либо заводить отдельную переменную, типа byte state = but.getButtonState(). А зачем, если есть but.btnState?
2. Изначально так и было. Но потом поменял. Логика была следующая - если нужно проверить именно состояние контактов - замкнуты или нет, то достаточно сделать проверку
3. Это чтобы отфильтровывать одиночные и двойные клики? Честно говоря, я противник запихивания таких вещей в библиотеки, если кому-то нужно отрабатывать и то, и другое, разделение нужно делать в скетче.
Например, так:
Этот пример я уже засунул в архив с библиотекой
4. Кажется, на это я ответил в п.2 ))
5. Этого есть у меня, даже два ))
BTN_HOLDED - действительно неудачное имя, заменил на BTN_WITHHELD
6. Тут не согласен. Естественно, я не предполагаю, что тайминги будут меняться по ходу выполнения, но если сделать их константами, то как их менять-то хотя бы и в ините?))
7. Честно говоря, не уверен, что правильно понял, но, имхо, это некоторое усложнение. Впрочем, над этим стоит подумать, вы меня натолкнули на немного другую мысль))
8. А тут точно не понял)) Имеете в виду вот так?
1. Так уж принято, если функцм GetЧегототам, то от нее и ожидают значение Чегототам. Тем более что и в Вашем примере и как правило по жизни сразу же это значение и востребовано. Ну и сыкономить не лишне. but.btnState - дело вкуса, можно убрать, можно оставить. "заводить отдельную переменную" выглядит предпочтительней т.к. на её поведение класс уже не повлияет. Смотрите свой пример, в стр 25 вы её всеравно присваиваете.
2. Ну вот это условие "but.btnState > BTN_UP" - как раз то, о чем писал. "В теории это грех. На практике почти пофиг" Как бы это плохо, но для квалифицированного пользователя... сойдет ;))) Потому различать таким путем состояния и события, еще ладно квалифицированный поймет. А вот нажата или нет - это любому профану может понадобится.
3. Неее... это явно попытка отвертется ))) Без этого как различить одинарное и двойное нажатия. Тем более что в коде по сути все есть, просто выплюнуть наружу нужно. С практической точки зрения пользователя из всех проблем это наибольшая. Вот тот код, что "Например, так:" - он жеж так и просится в либу.
5. ИМХО новое название хуже. Но речь не о нем. Нужно событие которое генерируется только один раз, если кнопку удерживали долго. Это чисто с практической стороны удобно. Логика такая. На три действия пользователя: короткое нажатие, долгое нажатие и двойное нажатие, без шаманства снаружи либы, просто в свичь, вешаются 3 обработчика, например отправка транзакции на сервер. И отправка один раз на одно действие.
6. Шаблон класса - один из путей, но есть и другие.
7. Не. Не сложней. Найдите в моем коде работу с временными интервалами для даблклика например. А она есть! Я её сам еле нашел)) Ну понятно
if
(uint8_t(t-takt)>=20)
задает период вызова основной функции и в ней049
t++;
050
i=t-btn_press;
да пару btn_press=t; Куда уж проще. Все байтовое. t - текущее "время" в библиотеке, единица равна 20мсек, i-интервал от прошлого какого либо события. Ну и сравнения прошло ли нужное время но не более - типа
if
((i<BTN_TIME_LONG) && (i>=BTN_TIME_CLICK))
А можно и жестко проверять завершение интервалаif
(i==BTN_TIME_LONG)
, пропуска не будет.8. Да. Человек который впервые увидит эту либу и пример смутится. Потом конечно поймет, если не сбежит стр 26-27 из примера #5 куда приятней.
1. Согласен. Сделаю return. Про btnState таки подумаю.
2. Видимо да, стоит сделать отдельный метод, типа isBtnClosed() (или isBtnPressed() ), в котором уже и проверять флаги
3. Вообще-то это стандартное (по крайней мере по меркам ПК) поведение - сначала onClick, потом onDblClick. Если делать разделение в библиотеке, то клик всегда будет идти с сильным запаздыванием. У Гайвера, кажется, так сделано - честно говоря, напрягает, когда реакция на нажатие кнопки следует через полсекунды, когда кнопка уже отпущена )) Если только действительно к BTN_DOWN добавить еще и BTN_CLICK, но не запутает ли это неискушенного пользователя?
5. С названиями у меня беда. Я в школе учил французский ))) И было это еще в прошлом веке. Если предложите что-то более подходящее, буду благодарен. Новое название, по крайней мере, гуглопереводчик переводит, старое игнорировал ))
По поводу однократного события. Наверное, можно сделать статус типа BTN_LONGCLICK со сменой на следующей итерации на BTN_WITHHELD. Может быть даже с возможностью настройки - нет, однократно, серийно через заданный интервал. Нужно подумать.
6. Все-таки тут я оставлю как есть))
7. Буду думать ))
8. Исправил ))
Вот и славно, надеюсь общение пойдет на пользу в плане совершенствования кода.
//Если только действительно к BTN_DOWN добавить еще и BTN_CLICK, но не запутает ли это неискушенного пользователя?
Думаю нет. Если будет много событий с разной логикой их генерации, то пользователь просто выберет подходящее, может даже просто методом проб и ошибок, если описание не поймет. Хуже когда выбрать не из чего.
Обновил библиотеку, в том числе с учетом замечаний. Что изменилось:
- Добавил возможность работы с нормально замкнутыми кнопками, может кому понадобится.
- Процедура getButtonState() теперь возвращает значение, поле btnState убрал в private.
- Добавил процедуру isButtonClosed(), возвращающую true, если кнопка по результатам последнего опроса нажата.
- Добавил виртуальный клик. Генерирует событие BTN_ONECLICK в случае, если по истечении интервала двойного клика собственно двойной клик не последовал. Кроме того, это событие не будет сгенерировано, если кнопка в этот момент удерживается нажатой. Т.е. появилась возможность раздельно обрабатывать одиночный, двойной клик и удержание кнопки. По умолчанию этот режим выключен, включается процедурой setVirtualClickOn(true).
- Состояние BTN_WITHHELD удалено и заменено состоянием BTN_LONGCLICK. По умолчанию оно так же генерируется при удержании кнопки нажатой дольше интервала удержания кнопки.
- Расширена обработка удержания кнопки, добавлено три режима: LCM_CONTINUED - режим по умолчанию, LCM_ONLYONCE - переводит BTN_LONGCLICK из состояния кнопки в событие, которое генерируется один раз по истечении интервала удержания кнопки. LCM_CLICKSERIES - то же событие, но регулярное - после истечения интервала удержания оно начинает генерироваться через равные интервалы времени. Это позволяет, например, организовать ускоренное изменение значений в каких-нибудь настройках. Режим удержания кнопки устанавливается процедурой setLongClickMode(mode). Интервал выдачи событий в режиме LCM_CLICKSERIES настраивается процедурой setLongClickTimeout(timeout), по умолчанию - 200 мс.
Файл shButton.h
Файл shButton.cpp
Файл keywords.txt
Демо-скетч:
Скетч - пример обработки одинарного и двойного клика:
Скетч - пример обработки удержания кнопки:
Скачать одним архивом можно здесь
Загрузил демку. Пи компиляции сразу сообщение об ошибке на строке "oldState = but.btnState;":
'class shButton' has no member named 'btnState'; did you mean '_btnstate'?
Где в библе капкан искать?
В библе все нормально, это в скетче поправить забыл. Нужно так
В библе все нормально, это в скетче поправить забыл. Нужно так
Ага, понил. Тока перезалей скетч, а то такие дуболомы, как я, впадают в ступор :)
Да уже перезалил, спасибо ))
Да уже перезалил, спасибо ))
Попробовал. Подключил 4 кнопки. Отрабатывает (совершенно адекватно) только одна. К каждой надо свою библиотеку цеплять?
Попробовал. Подключил 4 кнопки. Отрабатывает (совершенно адекватно) только одна. К каждой надо свою библиотеку цеплять?
приведите код
Да уже перезалил, спасибо ))
Попробовал. Подключил 4 кнопки. Отрабатывает (совершенно адекватно) только одна. К каждой надо свою библиотеку цеплять?
Для каждой создавать свой экземпляр класса. Давайте свой код
приведите код
а что значит "адекватно срабатывает одна кнопка"?
Неадекватность в чем заключается?
а что значит "адекватно срабатывает одна кнопка"?
Адекватно - значит безотказно и четко.
Неадекватность в чем заключается?
В том, что меняется значение только переменной but_hea_val. Остальные переменные не меняются при нажатии соответствующих кнопок.
Как подключены кнопки?
Все подтянуты к +5v. То есть, при нажатии на пине кнопки имеем LOW. Это имеется ввиду?
Да, это. В принципе в коде все верно. Если кнопки действительно посажены на обозначенные пины, то должны работать.
Кнопки сидят именно на этих пинах. В моей программе все они работают нормально. Просто хотел с помощью shButton немного упростить код. А вот фигу :)
Щас попробую ваш код воспроизвести. Только у меня три кнопки в наличии, но, думаю, это непринципиально ))
Щас попробую ваш код воспроизвести. Только у меня три кнопки в наличии, но, думаю, это непринципиально ))
Канешшна не принципиально. Достаточно для пробы и 2 кнопок.
Хе-хе, а вот и не буду пробовать, нашел ваш косяк - посмотрите, какие кнопки вы настроили в сетапе? ))
Подсказываю - что настроили, то и работает )))
Хе-хе, а вот и не буду пробовать, нашел ваш косяк - посмотрите, какие кнопки вы настроили в сетапе? ))
Вот я козёл-то старый! Прости, старик, начал батон на тебя крошить, а сам - пень обоссанный! Всегда всем говорит, что копипаст до добра не доводит, а сам...
И спасибо!
И спасибо!
Бывает. Не за что ))
Мне нравятся библиотеки на все случаи жизни для ОДНОЙ кнопки. А вот если нужно фиксировать одновременные нажатия?
Мне нравятся библиотеки на все случаи жизни для ОДНОЙ кнопки. А вот если нужно фиксировать одновременные нажатия?
Одновременность с точностью до микросекунды? В противном случае никто не мешает вам проводить опрос хоть десяти кнопок за одну итерацию
Боюсь что комбинации нажатий таким способом не получить. Либо ещё нужна будет дополнительная возня.)
Дык, это библиотека для одиночной кнопки, клавиатуры, что матричные, что резистивные, тут идут мимо ))
Причём здесь матричная. К примеру, у меня 3 "одиночных" кнопки, но я использую комбинации, т.к. без них совсем скучненько бегать по меню.
Тут без дополнительных движений никак ))
Дык, кнопки разные и ведут себя по разному, и нажимаются не абсолютно одновременно, со всеми отсюда вытекающими. Вы понимаете?
Тут понимать нечего - любая клавиатурная комбинация есть КЛИК основной кнопки при условии удерживаемой нажатой кнопки-модификатора. Причем кнопка-модификатор должна быть нажата заранее. Стало быть вам нужно ловить событие клика основной кнопки и проверять, не нажата ли при этом кнопка-модификатор. Примерно так:
))Какой ещё модификатор при 3-х кнопках! Нет у меня никакого модификатора. Это непозволительная роскошь. 3 кнопки дают 7 комбинаций, при этом ложных быть не должно. А в вашем варианте они будут, т.к. каждая кнопка сама по себе.
))Какой ещё модификатор при 3-х кнопках! Нет у меня никакого модификатора. Это непозволительная роскошь. 3 кнопки дают 7 комбинаций, при этом ложных быть не должно. А в вашем варианте они будут, т.к. каждая кнопка сама по себе.
В моем варианте две кнопки дают четыре комбинации. И ничего ложного ))
И да, все кнопки сами по себе - это же не клавиатура
Ничего ложного - это если вы нажимаете их СТРОГО одновременно, и дребезг у всех одинаковый. Иначе сначала будет одна, а затем уже комбинация.
Я взращен ПК и твердо знаю, что одновременно нажимать кнопки - плохая идея. Например, если мне нужно вызвать диалог поиска текста, я должен сначала нажать Ctrl, а затем F. Если я сделаю наоборот, нужный мне диалог не появится. А, нажимая эти клавиши одновременно, я не могу быть уверен, что они сработают в нужной последовательности. И даже когда последовательность вроде бы не важна, все равно лучше ее придерживаться. Например, что Alt+Shift, что Shift+Alt одинаково переключат раскладку клавиатуры, но во втором случае активируются акселераторы главного меню, и вместо продолжения набора текста я внезапно попаду в какой-то из пунктов меню редактора. Если сильно не повезет, то этот пункт еще и сработает. Т.е. и тут последовательность нажатий тоже имеет значение.
Поэтому повторю - ОДНОВРЕМЕННОЕ нажатие кнопок есть ОЧЕНЬ плохая идея. Последовательность действий должна быть строго определенной, а результат - прогнозируемый ))
ЗЫ: в приведенном мною варианте никто не помешает вам поставить в строках 17 и 29 один и тот же обработчик
Так и МК тоже не очень хорошая идея, в некоторых так совсем не развернёшься. И кнопок много иной раз не поставишь, да и по 34 байта ОЗУ на каждую кнопку тоже как то не очень... Но это так, лирика, что б поговорить.)
Вполне терпимая идея. Есть у меня тоже девайсик на три кнопки. Контроллер самогонного аппарата. Экран, менюшки, переферии куча. Проект в развитии лет 5 уже. По ходу чет добавляется. Удобства тоже. Наращивать клавиатуру вообще нет желания, размер корпуса не позволяет. Для навигации по многоуровневому меню предостаточно. Но есть несколько случаев, когда меню не удобно, а комбинации кнопок - то что надо. Самое банальное - аварийное выключение. Сейчас это сделано как все три кнопки нажато. Это намного быстрей чем в меню лазить. Другой пример - диагностика. Если ниче не нажато работаем сразу, если что зажато - проверяем соответствующую часть. Также частые действия (вкл/выкл звуковой сигнализации) или вызовы редких веток меню на комбинации иногда просятся.
Реализуется это довольно не сложно. И без особых затрат ресурса. Кстати, заодно с поддержкой матриц кнопок легко делается. Абстрактно выглядит так- пишем обработчик 3-х событий: на очередном такте обнаружено нажатие, на очередном такте обнаружено отжатие, такт прошел но состояние не менялось (это событие одно, без номера кнопки и последнее). И этот обработчик в качестве параметра принимает и номер кнопки к которому относится. Считаем это физические события. Номер кнопки, событие и условное его время запоминаем в очереди событий. Больше нигде. Затем в этой очереди разбираем для данной кнопки какие и когда происходили физические события. И генерим логические события (сразу простейшие, типа кнопка удержана дольше дребезга, затем более сложные вплоть до даблклика). Их тоже в очередь. И сверху ложим разбор комбинаций. Если, к примеру, есть все три физических нажатия за небольшой интервал времени то генерим событие для тройной комбинации и не генерим три логических события нажатия 3-х кнопок. В конце обработки - поток событий валим на обработчик. В общем такая хитрая архитектурка. Не сложно но непревычно.
Работает на ура. Хорошо раскладывается по уровням обработки и просится в иерархию классов. Позволяет ловить всякую экзотику типа даблклика при зажатой другой кнопке. Но есть и подводные камни. При попытке сунуть в либу выясняется, что если все эти понты не надо, для простейшего случая 1-2 кнопки, то код сильно избыточный. Ну еще есть моменты по многократному вызову методов потомка из соответствующего вызова предка (на одно событие на входе может быть целая гора на выходе, причем их размножение идет на разных уровнях иерархии).
Нет, я не утверждаю, что комбинации кнопок - зло. Я писал, что ОДНОВРЕМЕННОЕ нажатие кнопок - плохая идея, т.к. не гарантирует предсказуемого результата. Тем более, что МК все равно опрашивает их по очереди, т.е. неодновременно. А по поводу отработки одновременного нажатия кнопок - см. #39, в строках 17 и 29 ставится одинаковый обработчик и все, даже очередность нажатия влиять не будет. Только двойное срабатывание обработчика придется исключить
v258, спасибо за библиотеку. Впихнул в свой скетч, порадовало четкое отрабатывание кнопок (в моем варианте они, порой, хулиганили). Адекватно отрабатываются все события (клик, даблклик и удержание). Немного сократился и стал более читаемым текст программы. Возможно, наши титаны в ней и обнаружат недостатки, но в моем случае результат вполне приличный.
PS: Не знаю, почему получилось таким жирным и крупным шрифтом. Модераторы, если есть возможность, исправьте на штатный шрифт. Заранее спасибо.
v258, спасибо за библиотеку. Впихнул в свой скетч, порадовало четкое отрабатывание кнопок (в моем варианте они, порой, хулиганили). Адекватно отрабатываются все события (клик, даблклик и удержание). Немного сократился и стал более читаемым текст программы. Возможно, наши титаны в ней и обнаружат недостатки, но в моем случае результат вполне приличный.
Пожалуйста. Кстати, и спасибо за тестирование ))
PS: Не знаю, почему получилось таким жирным и крупным шрифтом. Модераторы, если есть возможность, исправьте на штатный шрифт. Заранее спасибо.
Скорее всего это результат копипаста из другого редактора. Лечится сбросом форматирования. Нужно выделить текст и нажать на кнопку "Убрать форматирование" - это крайняя справа кнопка в виде ластика
Кстати, и спасибо за тестирование ))
Да пожалуйста, только какой из меня тетильщик? Просто тупо применил и посмотрел на результат.
"Не кочегары мы, не плотники
Для нас наука - целый лес чудес
А мы научные работники
И не хватаем звезд с небес.
В цел'ях природы обуздания
В цел'ях рассеять неученья тьму
Берем картину мироздания
И тупо смотрим, что к чему"
А. и Б. Стругацкие
Как-то так и у меня :)