Мой опыт работы с компьютерами (и текстовыми графическими пользовательскими интерфейсами) начался еще в восьмидесятых, когда я был ребенком, на дне рождения друга. Это был «новенький» Amstrad CPC с памятью 64 Кбайт и кассетным ленточным накопителем. Включение его в первый раз было особенным моментом, мягко говоря (и забавным тоже!), Так как в конечном итоге у нас остался зеленый статический экран и командная строка, и все в комнате смотрели друг на друга, не понимая, что мы должны были сделать.

Никаких цветов, картинок или причудливой графики, и для некоторых людей это могло быть мрачным и пугающим опытом, но для других этот «жесткий» текстовый графический интерфейс был основой будущего. Изучение того, как пользоваться этим компьютером (и, конечно же, заставить его играть в игры) открыло для нас совершенно новый мир. Устаревшие технологии сейчас могут выглядеть не так впечатляюще, как тогда, но они, безусловно, оказали огромное влияние на развитие современных технологий.

Графические чипы для видеоигр существуют с 1970-х годов; специально разработан для великолепных аркадных видеосистем.

Вход в текстовую зону

Разработчики программного обеспечения имеют широкий арсенал библиотек и фреймворков графического интерфейса, но бывают случаи, когда вам просто нужно, чтобы ваша программа запускалась на стандартной консоли. С другой стороны, программирование водопада операторов печати было бы невозможным; вы хотите создать что-то более сложное, но не покидая текстовую зону. К сожалению, никто не гарантирует, что приложение, управляемое текстовым графическим интерфейсом пользователя, будет согласовано на разных терминалах и системах (и это было так, особенно когда-то), хотя большинство терминалов теперь идентифицируют себя как xterm (эмулятор терминала X Window System) через TERM переменная окружения.

Визуализируя нашу цель

Вот скриншот нашего приложения, работающего через CRT. Также есть небольшое видео нашего приложения на Vimeo и DailyMotion.

Мы используем библиотеку Java под названием Lanterna для создания этого текстового графического интерфейса терминала в контексте системы мониторинга температуры. Lanterna позволяет нам писать простые полуграфические пользовательские интерфейсы в текстовой среде, очень похожей на curses библиотеки C, но с большей функциональностью.

«Lanterna поддерживает xterm-совместимые терминалы и эмуляторы терминалов, такие как konsole, gnome-terminal, putty и многие другие. Одним из основных преимуществ lanterna является то, что он не зависит от какой-либо собственной библиотеки, а работает на 100% на чистой Java ».

Анализ проекта и исходный код

Я использую Apache Maven для управления этим Java-проектом и согласования его с Lanterna 3. Lanterna доступна на Maven Central через Sonatype OSS hosting. Теперь давайте кратко рассмотрим пакеты и файлы нашего проекта.

  • tms: Здесь живет класс, содержащий наш основной метод, и делает все необходимые приготовления, такие как создание экземпляров некоторых основных классов и создание пары потоков; он оживляет все.
  • gui: удерживает класс, ответственный за использование тех мощных заклинаний Lanterna, и включает отображение и оценку данных системы мониторинга температуры.
  • Physics: содержит код для моделирования и управления изменениями температуры окружающей среды, которые регистрируются тепловыми датчиками системы.
  • Внутри других пакетов у нас есть основные функции нашего приложения для мониторинга температуры. Идея здесь в том, что у нас есть система мониторинга тепловых датчиков, в которой все датчики размещены в разных местах.

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

Слои Lanterna и интерфейс Screen.

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

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

Наш класс LanterminalEngine

Этот класс реализует функциональные возможности текстового графического интерфейса Lanterna. Обратите внимание на три переменные-члены вверху, первая из которых имеет тип TemperatureMonitoringSystem. Хотя это поле не имеет ничего общего с Lanterna, в его декларации подчеркивается важность наличия объекта, способного передавать данные о температуре датчиков в потоковом режиме.

За реальную реализацию графического интерфейса пользователя отвечает поле типа Screen, импортированное из библиотеки Lanterna. Также от Lanterna идет TextColor, абстрактный базовый класс для определения цвета терминала. Наше путешествие по текстовому графическому интерфейсу начинается с вызова метода createScreen () внутри объявления initialize (). Обратите внимание, что мы устанавливаем наши основные цвета с помощью констант Enum, которые представляют цвета ANSI.

Создание экрана с DefaultTerminalFactory

Даже если мы напрямую не используем низкоуровневый терминал, слой Screen должен получить объект Terminal в качестве своей цели. Доступно небольшое количество реализаций терминала (например, UnixTerminal, SwingTerminal и TelnetTerminal), и хотя мы могли бы явно использовать одну из них, мы будем использовать класс DefaultTerminalFactory, который использует механизм обнаружения для автоматического создания правильной реализации терминала. исходя из характеристик работающей системы. Вот скриншот нашей программы в SwingTerminalFrame, работающей через нашу IDE (Spring Tool Suite) в Manjaro Linux.

Для всех систем с графической средой будет выбрана рама Swing Terminal Frame. Вы также можете использовать его, если ваша среда IDE неправильно реализует escape-коды ANSI.

Система контроля температуры и интерфейс датчика

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

И реализация датчика:

Датчик температуры окружающей среды не знает никаких местоположений, координат или каких-либо имен; все, что ему нужно, - это экземпляр AmbientTemperature. Что касается реализованного интерфейса, он довольно прост; измеряет тепло от объекта AmbientTemperature и предоставляет метод getTemperature (), который будет доступен для системы TemperatureMonitoringSystem.

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

А вот и метод simulate (); суть поведения регулятора:

Внутри блока моделирования мы используем один из генераторов случайных чисел Java, вызывая два разных метода в текущем потоке; один возвращает псевдослучайное целочисленное значение от 0 до 1 (2 - исключительная спецификация), а другой возвращает двойное значение от 0,0 до N, умноженное на значение переменной смещения. Этот простой алгоритм имитирует изменения температуры и предотвращает выход значений из-под контроля. Кроме того, мы используем синхронизацию методов при инициализации температуры окружающей среды для предотвращения пересечения потоков и ошибок согласованности памяти.

Использование заклинаний Lanterna: рисование нашего пользовательского графического интерфейса

Прежде чем мы начнем, нам нужно убедиться, что курсор не отображается при рисовании нашего графического интерфейса, используя метод setCursorPosition () экрана и передавая «null». Lanterna попытается скрыть курсор, если реализация терминала это поддерживает. Мы также назначаем размеры экрана, как они представлены буфером, целочисленным переменным ширины и высоты, для ясности при использовании длинных операторов. Также приятно знать, что здесь нам не нужно беспокоиться о пикселях, потому что все дело в строках и столбцах!

Для выполнения наших операций рисования нам нужно получить объект TextGraphics вне нашего экрана. Любые операции, выполняемые с этим объектом, не будут видны до тех пор, пока не будет вызван метод refresh (). Кроме того, имеется поддержка объединения методов, что особенно полезно при рисовании текстовой графики.

  • Вызов метода drawLine () в нашем объекте TextGraphics рисует линию от указанной позиции экрана к другой, используя предоставленный символ (также можно указать дополнительные метаданные, такие как цвета и модификаторы, с помощью объекта TextCharacter). В частности, мы печатаем простой символ пробела с перевернутой окраской, создавая иллюзию верхнего и нижнего колонтитула, каждый из которых занимает пространство экрана всего в одну строку.
  • Вызов метода putCSIStyledString () помещает строку на экран в указанную позицию. Существует также более простой метод putString (), который не поддерживает дополнительную функциональность встраивания управляющих последовательностей ANSI.
  • Внизу мы просто сопоставляем описания данных (или заголовки таблиц) с определенными позициями строк.

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

Вот остальная часть метода drawUI ():

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

Класс Symbols

Мы можем нарисовать двухстрочный прямоугольник с помощью класса Symbols Lanterna для передачи специальных символов при вызове метода setCharacter ().

При работе с текстовой графикой особенно полезен класс Symbols, и здесь, с левой стороны, у нас есть несколько снимков экрана с символами, взятых из документации Lanterna. Чтобы увидеть полный список, посетите эту страницу.

Кодовая страница 437 - это набор символов исходного IBM PC (персонального компьютера) или DOS. … В набор входят коды ASCII 32–126, расширенные коды для акцентированных букв (диакритические знаки), некоторые греческие буквы, значки и символы рисования линий ». - Из Википедии, бесплатной энциклопедии

Отображение данных и создание анимированных температурных полос

Переходя от drawUI (), поведение LanterminalEngine включает еще один метод, называемый displayData (). Вот первая часть:

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

Затем мы настраиваем индикацию состояния в правой части нашего графического интерфейса.

Сопоставление кратких описаний температуры с конкретными цветами текста ANSI и добавление объекта KeyStroke, который Lanterna предоставляет для обнаружения ввода с клавиатуры пользователя. Мы выбрали F10 как горячую клавишу для выхода из программы. Дополнительный исходный код этой простой функциональности горячих клавиш можно найти в репозитории GitHub.

Получение данных о температуре из системы мониторинга температуры и их размещение в соответствующих координатах экрана (помните, никаких пикселей; только строки и столбцы)

Внизу мы используем класс Java DecimalFormat; это способ справиться с проблемой сокращения длинных двойных значений температуры.

Наконец, рассчитываем длину стержней температуры и рисуем их.

Вот и все!

Наш настраиваемый текстовый графический интерфейс готов! Надеюсь, теперь вы имеете представление о том, как Lanterna работает с интерфейсом Screen. Имейте в виду, что Lanterna - огромная и замечательная библиотека. Он имеет множество различных применений и может быть предпочтительным инструментом во многих различных сценариях.

Для получения полного исходного кода или если у вас есть какие-либо предложения / идеи и вы хотите присоединиться и сотрудничать, посетите этот проект на GitHub.