Привет, Хабр! В этой статье я расскажу про npyscreen — библиотеке для создания текстовых интерфейсов для терминальных и консольных приложений.
Установка
Пакет доступен для скачивания через PyPI.
sudo pip3 install npyscreen
Типы объектов
Npyscreen использует 3 основных типа объектов:
- Application objects — обеспечивают запуск и завершение приложения, создание форм, обработку событий.
В основном используются NPSAppManaged и StandardApp(с поддержкой событий). - Form objects — область экрана, которая содержит виджеты.
Основные формы:
- FormBaseNew — пустая форма.
- Form — форма с кнопокой «ok».
- ActionForm — форма с двумя кнопками: «ok» и «cancel».
- FormWithMenus — форма, поддерживающая работу с меню.
- Widget Objects — различные элементы, расположенные на формах.
Некоторые виджеты:
- Textfield, PasswordEntry, MultiLineEdit, FilenameCombo* — формы для ввода данных.
- DateCombo, ComboBox, FilenameCombo — выпадающие списки.
- MultiSelect, MultiSelect, BufferPager — виджеты с возможностью выбора.
- Slider, TitleSlider — слайдеры.
Больше информации можно найти на официальном сайте с документацией.
Напишем Hello World
Формы удобно создавать, наследуя их от встроенных классов. Таким образом, можно переопределить встроенные методы для расширения функционала приложения.
#!/usr/bin/env python3
import npyscreen
class App(npyscreen.StandardApp):
def onStart(self):
self.addForm("MAIN", MainForm, name="Hello Habr!")
class MainForm(npyscreen.ActionForm):
# Конструктор
def create(self):
# Добавляем виджет TitleText на форму
self.title = self.add(npyscreen.TitleText, name="TitleText", value="Hello World!")
# переопределенный метод, срабатывающий при нажатии на кнопку «ok»
def on_ok(self):
self.parentApp.setNextForm(None)
# переопределенный метод, срабатывающий при нажатии на кнопку «cancel»
def on_cancel(self):
self.title.value = "Hello World!"
MyApp = App()
MyApp.run()
Расположение элементов
По умолчанию виджеты занимают максимально возможное пространство.
Чтобы задать точные координаты, нужно задать параметры:
- relx, rely — позиция виджета относительно начала координат формы.
- width, height, max_width, max_height — ограничения размеров виджета.
#!/usr/bin/env python3
import npyscreen
class App(npyscreen.StandardApp):
def onStart(self):
self.addForm("MAIN", MainForm, name="Hello Habr!")
class MainForm(npyscreen.FormBaseNew):
def create(self):
# Узнаем используемое формой пространство
y, x = self.useable_space()
self.add(npyscreen.TitleDateCombo, name="Date:", max_width=x // 2)
self.add(npyscreen.TitleMultiSelect, relx=x // 2 + 1, rely=2, value=[1, 2], name="Pick Several", values=["Option1", "Option2", "Option3"], scroll_exit=True)
# Можно использовать отицательные координаты
self.add(npyscreen.TitleFilename, name="Filename:", rely=-5)
MyApp = App()
MyApp.run()
Боксы и пользовательские цвета
Сделать обертку в виде бокса просто — нужно создать класс, наследованный от BoxTitle и переопределить атрибут _contained_widget, положив туда виджет, который будет находиться внутри.
В npyscreen доступно несколько встроенных цветовых тем. При желании можно добавить свои. Установить их можно с помощью метода setTheme.
С настройкой цвета текста все немного сложнее. Мне пришлось расширить функционал библиотеки, чтобы это работало.
#!/usr/bin/env python3
from src import npyscreen
import random
class App(npyscreen.StandardApp):
def onStart(self):
# Устанавливаем тему. По умолчанию используется DefaultTheme
npyscreen.setTheme(npyscreen.Themes.ColorfulTheme)
self.addForm("MAIN", MainForm, name="Hello Habr!")
class InputBox(npyscreen.BoxTitle):
# MultiLineEdit теперь будет окружен боксом
_contained_widget = npyscreen.MultiLineEdit
class MainForm(npyscreen.FormBaseNew):
def create(self):
y, x = self.useable_space()
obj = self.add(npyscreen.BoxTitle, name="BoxTitle",
custom_highlighting=True, values=["first line", "second line"],
rely=y // 4, max_width=x // 2 - 5, max_height=y // 2)
self.add(InputBox, name="Boxed MultiLineEdit", footer="footer",
relx=x // 2, rely=2)
color1 = self.theme_manager.findPair(self, 'GOOD')
color2 = self.theme_manager.findPair(self, 'WARNING')
color3 = self.theme_manager.findPair(self, 'NO_EDIT')
color_list = [color1, color2, color3]
first_line_colors = [random.choice(color_list) for i in range(len("first line"))]
second_line_colors = [random.choice(color_list) for i in range(len("second"))]
# Заполняем строки кастомными цветами
obj.entry_widget.highlighting_arr_color_data = [first_line_colors, second_line_colors]
MyApp = App()
MyApp.run()
События и обработчики
Класс StandardApp в npyscreen поддерживает очередь событий.
В качестве обработки нажатий используется метод add_handlers.
#!/usr/bin/env python3
import npyscreen
import curses
class App(npyscreen.StandardApp):
def onStart(self):
self.addForm("MAIN", MainForm, name="Hello Habr!")
class InputBox1(npyscreen.BoxTitle):
_contained_widget = npyscreen.MultiLineEdit
def when_value_edited(self):
self.parent.parentApp.queue_event(npyscreen.Event("event_value_edited"))
class InputBox2(npyscreen.BoxTitle):
_contained_widget = npyscreen.MultiLineEdit
class MainForm(npyscreen.FormBaseNew):
def create(self):
self.add_event_hander("event_value_edited", self.event_value_edited)
new_handlers = {
# Устанавливаем ctrl+Q для выхода
"^Q": self.exit_func,
# Устанавливаем alt+enter для очистки inputbox
curses.ascii.alt(curses.ascii.NL): self.inputbox_clear
}
self.add_handlers(new_handlers)
y, x = self.useable_space()
self.InputBox1 = self.add(InputBox1, name="Editable", max_height=y // 2)
self.InputBox2 = self.add(InputBox2, footer="No editable", editable=False)
def event_value_edited(self, event):
self.InputBox2.value = self.InputBox1.value
self.InputBox2.display()
def inputbox_clear(self, _input):
self.InputBox1.value = self.InputBox2.value = ""
self.InputBox1.display()
self.InputBox2.display()
def exit_func(self, _input):
exit(0)
MyApp = App()
MyApp.run()
Ссылки:
Официальная документация
Оригинальные исходные коды
Обновленный мною репозиторий (основной гитхаб, кажется, умер)
Телеграм клиент на npyscreen (который на первом скриншоте)
Комментарии (34)
PashaWNN
05.04.2018 17:00Эх, а я как раз начал писать что-то подобное, только сразу для терминала и для веба, этакое два-в-одном. Теперь есть, откуда черпать вдохновение.
JC_IIB
05.04.2018 17:34+1А мне BOOTSTRA.386 вспомнилась, тема для Bootstrap.
p.s. кликайте на Demo — там даже построчная отрисовка курсором имитируется, офигеть!
synedra
06.04.2018 06:51Интересно, люди просто регулярно пишут такие либы или оно нынче в тренде? Потому что я как раз пописываю подобную обёртку вокруг bearlibterminal, который рисует ASCII-интерфейс на SDL. Смысл в том, чтоб выглядело одинаково независимо от терминала пользователя, для геймдева.
PashaWNN
06.04.2018 14:41Да нет, просто я только-только встаю на путь разработчика и пока что просто пишу всякое ради получения опыта.
Alex_ME
05.04.2018 23:34+1Конечно, прожженных и умудренных опытом людей таким не удивить, но все же…
Однажды, в качестве лабораторной работы по каким-то там сетям надо было сделать чат. Пока мои одногруппники штурмовали Qt, я извратился и написал такое:
Заголовок спойлераdomix32
06.04.2018 15:50Куда смотреть исходники не слишком опытным windows-программистам?
Alex_ME
07.04.2018 12:10+1Вот оно. Хотя, признаюсь, тот еще говнокод. Особенно, ввод текста.
domix32
07.04.2018 23:23А можно попросить добавить лицензию?
Alex_ME
08.04.2018 00:52+1Добавил. Если Вас удивит такой выбор лицензии — работа в значительной мере была сделана как шутка, да и примером «как надо делать консольный GUI» не является. Да и мне абсолютно не жалко любые использования. Если кому-то поможет — только рад.
oxygenh
06.04.2018 00:39Что-то с русским языком не подружился приведенный телеграм клиент.
Библиотека приятная, ламповая. Спасибо.ogustbiller
06.04.2018 10:18+1Прошу прощения, вс1 с русским там нормально. «Сам дурак» :)
Еще раз спасибо. Будем изучать.iggyov
08.04.2018 02:35А можно подробнее, пожалуйста?
Установил по инструкции с github, на Kubuntu 16.04.4 — terminator / konsole и после запуска и логина отображается только латиница.
vin2809
06.04.2018 08:08Большое спасибо за пост, прямо то, что доктор прописал.
С Python я только еще начинаю дружить, зато TUI использую часто: удобно создавать приложения, состоящие практически только из меню и логов…
ananazzz
06.04.2018 17:30Кстати интересно. Поставил Python 3.6.5 для Windows x86_64, и вот такой баг ловлю при попытке установки npyscreen…
Посмотреть снимок экрана
KEugene
Pascal… TurboVision вспомнилось… Как давно это было.
ananazzz
Ат опередили, а (:
flacon
Зашел, чтобы написать этот комментарий