Как стать автором
Обновить

Расширяем фреймворк Kivy пакетом XPopup (Часть 2-я)

Время на прочтение8 мин
Количество просмотров6.8K
Продолжаем скрещивать змей и птиц (или — цирк и фрукт, кому как больше нравится). В предыдущей статье мы научились создавать уведомления, теперь перейдем к не менее востребованным форточкам: формы ввода данных и файловые навигаторы.

Формы ввода данных


Началось с простейшей потребности сделать аналог InputBox'a — окна с полем ввода для текста. В результате этой «простейшей потребности» на свет появилось еще пара полезных плюшек и базовый класс для создания UI-форм.

Класс XTextInput


Тот самый InputBox. Здесь все просто — за основу был взят kivy.uix.textinput.TextInput и повешен на всплывающее окно. Применяем так:
XTextInput(title='Что будем искать?', text='Как пройти в библиотеку?')

Результат:
image

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

Кстати, если в поле ввода нажать «Enter», окно воспримет это как нажатие кнопки «Ok».

Как получить введенные данные? Очень просто — пишем очередной обработчик закрытия окна:
def my_callback(instance):
    # Если не была нажата кнопка "Cancel"
    if not instance.is_canceled():
        # Получаем текст из поля ввода
        search = instance.get_value()
        print(u'Ищем: %s' % search)

XTextInput(title='Что будем искать?', text='Как пройти в библиотеку?',
           on_dismiss=my_callback)

Метод .get_value() возвращает значение элемента размещенного на UI-форме, подробнее об этом здесь.
Метод .is_canceled() возвращает True, если была нажата кнопка «Cancel». Подробнее об этом здесь.
Как заменить стандартные кнопки своими — смотрим здесь.

Финальный аккорд — добавим немного «юзерфрендли» (в случае unexpected exception пустого поля сообщим пользователю что он об ошибке):
def my_callback(instance):
    # Если не была нажата кнопка "Cancel"
    if not instance.is_canceled():
        # Получаем текст из поля ввода
        search = instance.get_value()
        # Поле ввода пусто
        if not search:
            # Сообщим пользователю об ошибке
            XError(text='Вы так и не сказали, что будем искать')
            # Запрещаем закрывать окно
            return True
        print(u'Ищем: %s' % search)

XTextInput(title='Что будем искать?', on_dismiss=my_callback)

Обратите внимание на место в коде:
            # Запрещаем закрывать окно
            return True

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

Класс XNotes


Тот же XTextInput, только в профиль для ввода многострочного текста:
XNotes(title='Заметки', text='Строка\nДругая строка...\nЕще одна')

Результат:
image

XNotes является потомком XTextInput и все, что описано выше о XTextInput справедливо для XNotes, за исключением:
  • нажатие «Enter» в поле ввода выполняет переход на новую строку.
  • параметр text ожидает на входе строку, многострочность достигается путем добавления "\n".


Класс XSlider


Всплывающее окно с kivy.uix.slider.Slider на борту. Употреблять так:
XSlider(title='Громкость', buttons=['Закрыть'])

По умолчанию, ползунок двигается в диапазоне [0 (min), 1 (max)], а его текущая позиция (value) на отметке 0.5. При желании эти параметры легко заменяются на другие:
XSlider(title='Громкость, dB', buttons=['Закрыть'], min=-100, max=12, value=-10)

Результат:
image

image

Как любому потомку XForm, данному классу также доступен метод .get_value(), которым можно воспользоваться в обработчике события закрытия окна. Помимо этого, класс позволяет обрабатывать изменение положения ползунка «на лету» с помощью события «on_change». Как обычно, опишем обработчик события:
def volume_change(instance, value):
    instance.title = 'Громкость: %0.0f' % (value * 100)

XSlider(title='Громкость', buttons=['Закрыть'], on_change=volume_change)

В видео демонстрации показан более «наглядный» пример работы события «on_change». Здесь код обработчика изменяет размеры всплывающего окна.

Класс XAuthorization


Данный класс позволяет создать простую форму для авторизации пользователя. Форма имеет поле для ввода логина, поле с маскированием символов для ввода пароля и чекбокс «Login automatically». Данный пример:
XAuthorization()
создаст пустую форму авторизации. Заполнить поля можно с помощью параметров: login, password и autologin:
XAuthorization(login='login', password='password', autologin=True)

Результат:
image

Для получения данных с формы пишем обработчик события закрытия окна. С помощью метода .get_value() получаем содержимое соответствующего поля:
def my_callback(instance):
    if not instance.is_canceled():
        login = instance.get_value('login')
        password = instance.get_value('password')
        autologin = instance.get_value('autologin')
        print('LOGIN: %s, PASS: %s, AUTO: %s' % (login, password, autologin))

XAuthorization(login='login', password='password', autologin=True,
               on_dismiss=my_callback)


Класс XForm


Что нужно знать о базовом классе?
  • Базовый класс имеет метод .get_value(id), который ищет на форме элемент с указанным id и возвращает его значение. Параметр необязательный, если его не указать — метод возьмет с формы 1-й элемент, у которого задан id и вернет его значение. Данная реализация поддерживает получение значений элементов: TextInput, Switch, CheckBox, Slider.
  • Класс имеет абстрактный метод ._get_form(), результатом работы которого должен быть контент формы.
  • Класс задает следующий набор кнопок по умолчанию: «Ok», «Cancel».

Хорошо, пробуем написать свою форму:
class MyForm(XForm):
    def _get_form(self):
        # Создаем контейнер формы
        layout = BoxLayout()
        # Добавим текст без id (значение этого элемента не нужно)
        layout.add_widget(Label(text='Show must go'))
        # Добавим чекбокс с id (значение этого элемента пригодится)
        layout.add_widget(Switch(id='main_switch', active=True))
        # Возвращаем контейнер
        return layout

def my_callback(instance):
    # обработчик закрытия окна
    if not instance.is_canceled():
        print('Switch value: ' + str(instance.get_value('main_switch')))

MyForm(title='Party switch', on_dismiss=my_callback)

и смотрим на результат:
image

Помимо .get_value(id), класс имеет свойство values. Это dict, в котором ключ — id элемента формы, а значение — значение этого элемента. Используя это свойство, можно реализовать обработчик по-другому:
def my_callback(instance):
    # обработчик закрытия окна
    if not instance.is_canceled():
        print('Switch value: ' + str(instance.values['main_switch']))


Файловые навигаторы


Т.е. окна для выбора файлов\каталогов или для ввода имени сохраняемого файла. Выглядят они приблизительно так:
image

Класс XFileOpen


Создает окно для выбора файла. Пример:
XFileOpen()

Нужно выбрать несколько файлов? Выбираем несколько. Заодно укажем путь по умолчанию и опишем обработчик для получения списка выбранных файлов:
def my_callback(instance):
    if not instance.is_canceled():
        print(u'Path: ' + instance.path)
        print(u'Selection: ' + str(instance.selection))

XFileOpen(multiselect=True, path=u'c:\python27', on_dismiss=my_callback)

Класс уже содержит реализацию проверки на пустой выбор и не даст пользователю закрыть окно по кнопке «Open», пока не будет выбран хотя бы один файл. Поэтому в обработчике пишем проверку только на нажатие «Cancel».

И пробуем создать русскую локализацию класса. Простой подменой кнопок здесь не обойтись, придется немного потанцевать с бубном:
class XFileOpenRU(XFileOpen):
    title = StringProperty('Открытие файла')
    # константа используется в коде проверки на пустой выбор
    BUTTON_OPEN = 'Открыть'
    # теперь метод .is_canceled() правильно реагирует на новое имя кнопки
    BUTTON_CANCEL = 'Отмена'
    # меняем сообщение об ошибке
    TXT_ERROR_SELECTION = 'Может, сначала выберем файл?'
    # переопределяем набор кнопок для использования новых констант
    buttons = ListProperty([BUTTON_OPEN, BUTTON_CANCEL])

XFileOpenRU()

Не совсем удобно, но для текущей версии это выход. В версии 0.3.0 будет реализовано больше удобств для локализации.

Класс XFileSave


Окно для ввода имени сохраняемого файла. Используется аналогично, за исключением свойств selection и multiselect — здесь они нам не понадобятся. Вместо них будем использовать:
  • filename — привязано к полю ввода имени файла, можно задать значение по умолчанию или считать введенный текст;
  • .get_full_name() — метод, возвращающий путь с именем файла.

Пример:
def my_callback(instance):
    if not instance.is_canceled():
        # только путь
        print(u'Path: ' + instance.path)
        # только имя файла
        print(u'Filename: ' + instance.filename)
        # путь с именем файла
        print(u'Full name: ' + instance.get_full_name())

popup = XFileSave(filename='file_to_save.txt', on_dismiss=my_callback)

Класс также содержит реализацию проверки на отсутствие текста в поле ввода, так что не заморачиваемся на этот счет.

Класс XFolder


Окно для выбора каталога. Используется аналогично XFilePopup. Нюанс в том, что при multiselect=False свойство selection будет содержать текущий каталог (т.е. выбираем текущий открытый каталог; это позволяет выбрать корень, при острой необходимости), а при multiselect=True свойство selection содержит список выделенных каталогов.
Подробнее о тонкостях выбора каталога см. ниже.

Класс XFilePopup


За основу взят kivy.uix.filechooser.FileChooser — виджет для навигации по файловой системе. Кроме него всплывающее окно содержит следующие элементы:
  • Кнопка «Icons» — включает режим отображения содержимого в виде иконок;
  • Кнопка «List» — включает режим отображения содержимого в виде списка;
  • Кнопка «New folder» — создает новый каталог;
  • Метка, отображающая текущий каталог.

FileChooser имеет большой список полезных свойств, но в XFilePopup вынесены только свойства «первой необходимости»:
  • path — каталог, содержимое которого отобразится при создании окна; по умолчанию — '/'. Меняет значение при смене каталога;
  • multiselect — разрешить/запретить выбор нескольких элементов; по умолчанию — False;
  • dirselect — разрешить/запретить выбор каталогов; по умолчанию — False. Важно: при dirselect=False открытие каталога происходит в один клик; при dirselect=True — одинарный клик помечает каталог выбранным, а открытие выполняется двойным кликом (что не совсем удобно на мобильных девайсах);
  • selection — список, содержащий выбранные элементы;
  • browser — объект класса FileChooser, «вынесен наружу» для возможности воспользоваться его остальными свойствами.

Создадим окно, укажем ему путь и набор кнопок (XFilePopup не имеет собственного набора кнопок). И сразу опишем обработчик для получения результатов:
def my_callback(instance):
    print(u'Path: ' + instance.path)
    print(u'Selection: ' + str(instance.selection))

XFilePopup(title='Файловый навигатор', buttons=['Select', 'Close'],
           path=u'c:/python27', on_dismiss=my_callback)

Обратим внимание на параметр path — значение ему передается в виде unicode string. Это необходимо для корректного отображения имен файлов и каталогов, содержащих не-ASCII символы (например, символы кодировки windows-1251 итд)

В результате получим навигатор, позволяющий выбрать только один файл, без возможности выбора папок (аналог XFileOpen).

3 2 кита


И напоследок, пару слов о рамах для форточек

Класс XBase


Как видно из иерархии классов, XBase приходится предком всем рассмотренным в статье классам. С некоторыми из его свойств и методов мы уже бегло ознакомились, здесь приведу полный список фич, которые передаются объектно-ориентированным путем:
  • auto_open — boolean свойство; если True, то при создании экземпляра класса окно будет отображатся автоматически. Иначе — для отображения окна у экземпляра нужно вызвать метод .open(). По умолчанию — True.
  • buttons — list свойство, определяющее набор управляющих кнопок всплывающего окна. По умолчанию — [].
  • button_pressed — string свойство, содержит имя нажатой управляющей кнопки. Доступно в обработчике события on_dismiss.
  • .is_canceled() — метод, возвращающий True, если имя нажатой кнопки = XBase.BUTTON_CANCEL.
  • ._get_body() — абстрактный метод, результатом которого должен быть контент окна.

Также данный класс задает значения по умолчанию некоторым свойствам суперклассов:
  • min_width=300dp, min_height=150dp, fit_to_window=True — свойства из XPopup, об этом — ниже;
  • size_hint=(.6, .3) — свойство из Popup, задает относительные размеры окна;
  • auto_dismiss=False — свойство из Popup, запрещает закрытие окна по клику в любой области интерфейса за пределами самого окна.

Примечание. Данный класс игнорирует свойство content класса Popup.

Класс XPopup


Потомок Popup и суперкласс для XBase. Создан для тщательной обработки напильником коррекции отображения окон на мобильных девайсах. Его свойства:
  • min_width и min_height — минимальные ширина и высота окна, значения по умолчанию отсутствуют. Работают в паре с size_hint. Суть: при заданных min_width и/или min_height происходит коррекция значений size_hint так, чтобы размеры окна были не меньше допустимых минимальных значений.
  • fit_to_window — boolean свойство «вместить в окно», по умолчанию — False. Работает в паре с size. Суть: при fit_to_window=True происходит коррекция значений size так, чтобы размеры всплывающего окна были не больше размеров окна приложения.

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

Ссылки


Расширяем фреймворк Kivy пакетом XPopup (Часть 1-я)
Наглядное пособие (видео демонстрация).
Скачать пакет XPopup.

На сегодня все.

Спасибо за внимание и приятного вам кодинга.
Теги:
Хабы:
+3
Комментарии4

Публикации

Истории

Работа

Data Scientist
63 вакансии
Python разработчик
142 вакансии

Ближайшие события