Doob 2 751 Опубликовано: 8 мая, 2017 (изменено) Кнопки, это самый распространенный элемент интерфейса, и не потому, что их полно в каждой графической программе. Функционал кнопки наследует большинство средство ввода, например: ссылки, переключатели, ползунки, менюшки и т. д. Сейчас, огромные возможности для создания других, более подходящих ситуации способов ввода, но традиционно используются кнопки, которые появились в первых электронных устройствах, т. к. они просты и интуитивно понятны. Внешний вид.Обычная кнопка имеет прямоугольный вид, это позволяет не заморачиваться с алгоритмом взаимодействия, удобно отображать на ней текст/изображения, но если необходимо, ее можно сделать какой угодно формы.Самое главное в кнопке это ее интерактивность, т. е. она должна не только выполнять функцию, для которой назначена, но и сигнализировать свой статус (иметь обратную связь).Например, при нажатии на кнопку, она меняет цвет/визуально проваливается/издает звук, все это вместе или по-отдельности дает пользователю знать, что она активирована, а цветом или звуком можно даже передать состояние выполнения задачи, выполняемой этой кнопкой. А вот если пользователь нажимает на кнопку и ничего не происходит, то он может клацнуть по ней еще много раз, пока кнопка будет выполнять задачу. В большинстве случаев это довольно опасно - накликают беду, а винят программиста. Поэтому, если в текущей ситуации кнопка неактивна, надо это подчеркнуть, например, обесцветив ее или частично слив с фоном, можно дополнить специальным звуком.Все это очевидно, поэтому не стоит об этом забывать. Внутреннее устройство.В графическом интерфейсе, кнопка это определенно обозначенное место на экране, в которое нужно тыкнуть, чтобы получить определенный результат, обозначенный на кнопке в виде текста/изображения или понимаемый из контекста.Как это реализовать в OpenComputers?Нам доступен вывод информации на монитор, посредством компонента GPU. Пока рассмотрим функции назначения цветов и вывода символов. Работа с видеокартой.Список методов видеокарты можно посмотреть на вики.Для назначения цвета текста используется метод setForeground.А чтобы задать цвет фона, есть метод setBackground. Чтобы проверить, как это работает обратимся к компьютеру с установленной OpenOS.Запустим компьютер. Вводим команду edit gpu_test.lua, так мы запускаем текстовый редактор OpenOS, указывая, что нужно редактировать файл gpu_test.lua. Пока этого файла нет, но когда в редакторе нажмем Ctrl+S он сохранится. Чтобы мы могли обратится к компоненту gpu, нам надо загрузить обертку для компонентов, предоставляемую OpenOS, она немного облегчает работу. Для этого, в самом начале программы пишем local component = require('component'), так, что это за письмена? Эта строка создает локальную переменную (указываем словом local перед именем переменной), далее указываем имя переменной.Знак равенства указывает, что в переменной теперь будет хранится то, что дает функция require, в данном случае мы задали этой функции передать переменной component возможности обертки, которая тоже называется component. Так будет удобней понимать, к чему мы обращаемся.На следующей строке пишем local gpu = component.gpu Это мы назначили переменной gpu все методы видеокарты, т. е. теперь можем обращаться к ней через эту переменную. При загрузке, OpenOS сделала несколько скрытых операций: определила основную видеокарту, подключила ее к монитору и добавила их к остальным компонентам, чтобы можно было обратиться по имени компонента и получить готовый интерфейс. Итак, видеокарта готова к работе, теперь опишем, что она должна сделать.Создаем переменные, содержащие информацию о цветах и тексте, с которыми мы будем работать.local color1, color2, text = 0xff00ff, 0x0000ff, 'Hello, OpenComputers!'При помощи параллельного присваивания мы задали две переменные с числовыми значениями цветов и переменную с текстом.Теперь создадим простой цикл, в котором выведем текст на экран, назначая цвет текста и цвет фона. for i = 1, 10 do gpu.setForeground(color1) gpu.setBackground(color2) gpu.set(10, 5, text) color1, color2 = color2, color1 os.sleep(0.5) end Сам код назначает тексту цвет, указанный в color1, а фону color2, затем выводит текст, содержащийся в переменной text на монитор, указывая координаты первого символа (от левого верхнего угла 10 символов в право, 5 символов вниз). Далее, параллельным присваиванием, меняем значения переменных местами, чтобы при следующем выполнении кода, цвета фона и текста поменялись. В конце цикла ставим задержку в пол-секунды, используя системную функцию sleep.Цикл готов, теперь, в конце программы стоит добавить восстановление цветов текста и фона, т. к. по завершению ее работы, цвета останутся такими, которые были назначены в последний раз.Готовый алгоритм будет примерно такой: local component = require('component') local gpu = component.gpu local color1, color2, text = 0xff00ff, 0x0000ff, 'Hello, OpenComputers!' for i = 1, 10 do gpu.setForeground(color1) gpu.setBackground(color2) gpu.set(10, 5, text) color1, color2 = color2, color1 os.sleep(0.5) end gpu.setForeground(0xffffff) gpu.setBackground(0) Нажимаем Ctrl+S, чтобы сохранить файл, затем Ctrl+W, чтобы закрыть редактор. Теперь мы снова попали в шелл. Вызываем только-что созданную программу по имени: gpu_test, и наблюдаем ее выполнение. Так как координаты вывода жестко заданы, мы видим, что текст как-бы мигает. Чтобы увидеть эволюцию работы цикла, можно к координатам вывода добавить значения шага цикла. Для этого откроем файл edit gpu_test.lua, в функции gpu.set к каждому числу прибавим переменную-итератор. Теперь вызов функции gpu.set выглядит вот так: gpu.set(10+i, 5+i, text), сохраним изменения (Ctrl+S), закроем (Ctrl+W), и опять запустим тестовую программу gpu_test Работа с вводом.Со способом вывода информации более-менее разобрались, теперь разберемся с вводом, т. к. мы хотим создать графический интерфейс, хоть и 'псевдо', мы будем использовать возможность сенсорного ввода, клавиатуру поставим для других задач.Монитор I уровня это только средство вывода, а мониторы второго и третьего уровня могут работать и как средство ввода.Когда игрок прикасается к экрану, генерируется сигнал touch, содержащий в себе адрес монитора, координаты касания, код кнопки мыши и ник игрока. Нам остается отследить это событие и соответсвующим образом на него отреагировать.Для демонстрации работы напишем небольшую тестовую программу, которая будет ловить события касания экрана, отображать место и числовые координаты. Создаем файл edit click_test.luaПервым делом подключаем управление видеокартойlocal gpu = require('component').gpuЗатем, подключим мониторинг событий, который предоставляет OpenOS.local pull_e = require('event').pullОбщая документация тут, мы воспользуемся только одной функцией.Теперь позаботимся о пользователе, чтобы при завершении программы цвет фона остался такими же, как и перед запуском, мы его сохраним в переменную:local b_color = gpu.getBackground() Эта функция сообщает цвет фона, который сейчас используется, для цвета текста есть getForeground, но в данном тесте мы будем управлять только цветом фона.Так как у мыши две кнопки, в событии touch указывается код кнопки: ЛКМ = 1, ПКМ = 0Чтобы визуально различать касания, создадим таблицу с цветами для каждой кнопки. В качестве индекса будет использоваться код кнопки.local color = {[0] = 0x00ff00, 0x0000ff} т. е. ЛКМ будет обозначаться синим, ПКМ - зеленым.Теперь создадим цикл, в котором будем ловить и обрабатывать события.while true doКак следует из описания функции event.pull, можно фильтровать ненужное, передав в функцию название интересующего нас события. Так как возвращается довольно много переменных, будем создавать из них таблицу, для большего удобства.local tEvent = {pull_e('touch')}Теперь все события типа touch будут пересоздавать таблицу tEvent, в которой будет заключена информация об этом событии. Обратиться к интересующей информации можно, указав номер, в той же последовательности, как это описано в документации по сигналамДля начала добавим условие выхода из программы, т. к. цикл бесконечный и прервать его можно только грубо нарушив выполнение программы.if tEvent[3] == 1 and tEvent[4] == 1 thenЕсли клик произошел по координатам (1, 1), выполнить следующий код:gpu.setBackground(b_color) - вернем фону его первоначальное значение, полученное при запуске программы.os.exit() - вызовем системную функцию выхода из программы.Закроем условие:endА теперь опишем сам процесс отклика на касание.Присваиваем фону цвет, исходя из полученного кода кнопки мыщи:gpu.setBackground(color[tEvent[5]])Устанавливаем по полученным координатам символ пробела, чтобы подсветить фон:gpu.set(tEvent[3], tEvent[4], ' ')Затем, по координатм (1, 1) показываем информацию о событии:gpu.set(1, 1, 'x: '..tEvent[3]..' y: '..tEvent[4]..'\t\t')В конце добавляется пустое место, чтобы не заморачиваться с затиранием предыдущих значений.Закрываем циклend В итоге получается такой код: local gpu = require('component').gpu local pull_e = require('event').pull local b_color = gpu.getBackground() local color = {[0] = 0x00ff00, 0x0000ff} while true do local tEvent = {pull_e('touch')} if tEvent[3] == 1 and tEvent[4] == 1 then gpu.setBackground(b_color) os.exit() end gpu.setBackground(color[tEvent[5]]) gpu.set(tEvent[3], tEvent[4], ' ') gpu.set(1, 1, 'x: '..tEvent[3]..' y: '..tEvent[4]..' ') end Сохраним и запустим программу. Кликая мышкой по монитору мы видим, как она оставляет следы и обновляется информация в левом верхнем углу.Кликнув в левый верхний угол мы завершаем работу программы. Значит, мы между делом создали кнопку, хоть и невидимую.Принцип работы очень прост - пользователь кликнул по экрану, а программа сравнила координаты клика с координатами кнопки, если они совпали, то выполняется код, назначенный для этой кнопки. Теперь создадим нормальные кнопки, с более удобной конфигурацией.Все кнопки будут находиться в таблице, при каждом клике будем в цикле обходить таблицу, проверяя координаты.Дополнительно напишем пару функций - одна будет подсвечивать нажатую кнопку, другая переключать видимость.Каждая кнопка будет содержать следующие параметры: статус отображения (т. е. активна кнопка или нет), координаты, размер, цвет фона кнопки, цвет текста, текст и исполняемую функцию. { visible = boolean, X = number, Y = number, W = number, H = number, color = number, textColor = number, text = string, action = function } Загрузим видеокарту и захват событий.local gpu = require('component').gpulocal pull_e = require('event').pullДля того, чтобы очистить экран при запуске и поставить кнопку выхода в угол, нам понадобится узнать и сохранить текущее разрешение.local W, H = gpu.getResolution()Сохраняем цвета фона и текста, чтобы вернуть их при завершении программы.local b_color, f_color = gpu.getBackground(), gpu.getForeground() Создаем таблицу tButtons, в ней мы будем хранить все кнопки.Добавляем кнопку выхода из программы:Изначальное состояние кнопки: visible = false,Устанавливаем кнопку в правый верхний угол, задаем размер в один символ: X = W, Y = 1, W = 1, H = 1,Устанавливаем цвета: color = 0xff0000, textColor = 0xffffff,Задаем символ: text = 'X',Функция завершения программы немного изменилась - устанавливаются первоначальные цвета, очищается экран и вызывается функция выхода. action = function() gpu.setBackground(b_color) gpu.setForeground(f_color) gpu.fill(1, 1, W, H, ' ') os.exit() end Опишем функцию рисования кнопки, в которую будет передаваться индекс кнопки.local function drawButton(n)задаем цвет кнопкиgpu.setBackground(tButtons[n].color)задаем цвет текстаgpu.setForeground(tButtons[n].textColor)заливаем прямоугольник, который занимает кнопка (в данном случае, берем координаты и размер кнопки из ее таблицы)gpu.fill(tButtons[n].X, tButtons[n].Y, tButtons[n].W, tButtons[n].H, ' ')для большей красоты отцентрируем текст по границам кнопки, для этого вычислим половину высоты и половину ширины кнопки, прибавим их к координатам кнопки, а у ширины еще отберем половину длинны текстаgpu.set(tButtons[n].X+(tButtons[n].W/2)-(#tButtons[n].text/2), tButtons[n].Y+(tButtons[n].H/2), tButtons[n].text)закрываем функциюend Добавим переключение видимости кнопки.local function toggleVisible(n)if tButtons[n].visible thenесли кнопка видима, то изменим статус tButtons[n].visible = falseустановим цвет фона на тот, который был при запуске gpu.setBackground(b_color)зальем прямоугольник фоновым цветом gpu.fill(tButtons[n].X, tButtons[n].Y, tButtons[n].W, tButtons[n].H, ' ')else иначе:изменим статус tButtons[n].visible = true вызовем отрисовку этой кнопки drawButton(n) закроем условие и функцию end end Чтобы кнопка подмигивала при активации, создадим функцию меняющую цвет фона и текста, как в первом примере.Для получения и назначения цветов обратимся к параметрам кнопки по индексу, как и в прошлых функциях.меняем цвета tButtons[n].color, tButtons[n].textColor = tButtons[n].textColor, tButtons[n].colorрисуем кнопку drawButton(n)делаем задержку os.sleep(0.09)возвращаем цвета tButtons[n].color, tButtons[n].textColor = tButtons[n].textColor, tButtons[n].colorзаново рисуем кнопку, с номальными цветами drawButton(n)Функции описаны, теперь напишем саму программу.При запуске очистим экран gpu.fill(1, 1, W, H, ' ')Можно вынести ее в отдельную функцию, но в данном примере очистка вызвается только два раза - при запуске и при выходе.Теперь активируем все нужные кнопки. В данном примере можно запустить все кнопки, поэтому пройдем в цикле по таблице с кнопками и для каждой вызовем переключатель видимости. for i = 1, #tButtons do toggleVisible(i) end И опишем главный цикл.while true doловим событие в таблицу tEvent local tEvent = {pull_e('touch')}перебираем все кнопки for i = 1, #tButtons doесли кнопка видима if tButtons.visible thenсравниваем координаты клика с прямоугольником кнопкиif tEvent[3] >= tButtons.X and tEvent[3] <= tButtons.X+tButtons.W and tEvent[4] >= tButtons.Y and tEvent[4] <= tButtons.Y+tButtons.H thenесли клик попал по этой кнопке, заставляем ее мигнуть blink(i)выполняем функцию, которая назначена для нее tButtons.action()прерываем цикл проверки кнопок breakзакрываем циклы и условия end end end endНа этом все, программа готова, в готовом примере я добавил три кнопки - 'set' выводит строку, а 'del' стирает и бибикает спикером, 'reboot' перезагружает компьютер. local gpu = require('component').gpu local computer = require('computer') local pull_e = require('event').pull local W, H = gpu.getResolution() local b_color, f_color = gpu.getBackground(), gpu.getForeground() local tButtons = { { visible = false, X = W, Y = 1, W = 1, H = 1, color = 0xff0000, textColor = 0xffffff, text = 'X', action = function() gpu.setBackground(b_color) gpu.setForeground(f_color) gpu.fill(1, 1, W, H, ' ') os.exit() end }, { visible = false, X = 1, Y = 3, W = 7, H = 1, color = 0x008800, textColor = 0xffff00, text = 'set', action = function() gpu.setForeground(0xffffff) gpu.set(1, 1, 'Hello!') end }, { visible = false, X = 1, Y = 5, W = 7, H = 1, color = 0xAA0000, textColor = 0xffffff, text = 'del', action = function() computer.beep(1000, 0.1) gpu.setBackground(0) gpu.fill(1, 1, 6, 1, ' ') end }, { visible = false, X = 1, Y = 7, W = 7, H = 1, color = 0xffffff, textColor = 0, text = 'reboot', action = function() computer.shutdown(true) end } } local function drawButton(n) -- функция рисования кнопки gpu.setBackground(tButtons[n].color) -- задаем цвет кнопки gpu.setForeground(tButtons[n].textColor) -- задаем цвет текста gpu.fill(tButtons[n].X, tButtons[n].Y, tButtons[n].W, tButtons[n].H, ' ') -- заливаем область gpu.set(tButtons[n].X+(tButtons[n].W/2)-(#tButtons[n].text/2), tButtons[n].Y+(tButtons[n].H/2), tButtons[n].text) -- пишем текст по центру end local function toggleVisible(n) -- переключение видимости кнопки if tButtons[n].visible then -- если кнопка видима tButtons[n].visible = false -- отключаем gpu.setBackground(b_color) -- берем цвет фона, полученный при старте программы gpu.fill(tButtons[n].X, tButtons[n].Y, tButtons[n].W, tButtons[n].H, ' ') -- стираем кнопку else -- если кнопка не активна tButtons[n].visible = true -- активируем drawButton(n) -- запускаем отрисовку end end local function blink(n) -- мигание кнопки tButtons[n].color, tButtons[n].textColor = tButtons[n].textColor, tButtons[n].color -- меняем местами цвета фона и текста drawButton(n) -- отрисовываем кнопку os.sleep(0.09) -- делаем задержку tButtons[n].color, tButtons[n].textColor = tButtons[n].textColor, tButtons[n].color -- меняем цвета обратно drawButton(n) -- перерисовываем кнопку end gpu.fill(1, 1, W, H, ' ') -- очищаем экран for i = 1, #tButtons do toggleVisible(i) -- активируем каждую кнопку end while true do local tEvent = {pull_e('touch')} -- ждем клика for i = 1, #tButtons do -- перебираем все кнопки if tButtons[i].visible then -- если кнопка активна if tEvent[3] >= tButtons[i].X and tEvent[3] <= tButtons[i].X+tButtons[i].W and tEvent[4] >= tButtons[i].Y and tEvent[4] <= tButtons[i].Y+tButtons[i].H then -- если клик произведен в пределах кнопки blink(i) -- мигнуть кнопкой tButtons[i].action() -- выполнить назначенный код break end end end end Изменено 10 мая, 2017 пользователем Doob 10 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 10 мая, 2017 Картинки! Как можно писайть гайд по интерфейсам без единого скриншота? 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Doob Автор темы 2 751 Опубликовано: 10 мая, 2017 Тут видео надо, писать видео не могу, ибо негде писать звук. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Totoro 3 563 Опубликовано: 10 мая, 2017 Видео не обязательно. Тут нужен хотя бы скрин демо-интерфейса с кнопками. Чтобы видно было, что не кот в мешке, а кнопки. Ну и можно гифку прилепить, которая покажет смену состояний. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Barawik_ 0 Опубликовано: 25 июля, 2018 А можете написать код на кнопки по отдельности? пожалуйста. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Гость Kartze Опубликовано: 25 июля, 2018 @@Barawik_, да ты че, шутишь? там черным по белому написано как запилить кнопку. я тебе уже сказал, что не твое это. рано тебе в такое лезть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Asior 586 Опубликовано: 26 июля, 2018 Заюзал данную программу, офигенно сделано. Но есть небольшие правки. Надо подключить юникод, если хочется работать с русскими буковками в названии кнопок. --весь код нет смысла копировать поэтому куски кода вставлю local un = require("unicode") --подключить юникод ... local function drawButton(n) -- функция рисования кнопки gpu.setBackground(tButtons[n].color) -- задаем цвет кнопки gpu.setForeground(tButtons[n].textColor) -- задаем цвет текста gpu.fill(tButtons[n].X, tButtons[n].Y, tButtons[n].W, tButtons[n].H, ' ') -- заливаем область gpu.set(tButtons[n].X+(tButtons[n].W/2)-(un.len(tButtons[n].text)/2), tButtons[n].Y+(tButtons[n].H/2), tButtons[n].text) -- пишем текст по центру end Цитата Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах