Розширюємо фреймворк Kivy пакетом XPopup

Ці забавні звірятка
Не так давно переді мною постало завдання в стислі сра терміни написати працюючий прототип GUI-додатка, який без зайвої рядка коду добре дружило б як з Windows, так і з OS X. Вибір припав на зміїний фреймворк Kivy, який з легкістю вирішував вищесказане. А також, в базовій комплектації мав весь необхідний інструментарій для реалізації програми.

Ну… майже весь. Під катом розповім, що не так і як це побороти.

Їжак Ківі — птах горда
Фреймворк має корисний клас kivy.uix.popup.Popup для реалізації спливаючих вікон. Не буду вдаватися в подробиці того, що він вміє — стаття не про це. Кому цікаво — за посиланням можна почитати документацію цього класу і його предка — kivy.uix.modalview.ModalView.

Але є нюанси. Припустимо, перед вами стоїть банальна завдання — вивести текстове повідомлення у спливаючому вікні. Popup дозволяє це зробити досить просто, в один рядок:

Popup(title='Повідомлення', content=Label(text='Отримано нове повідомлення')).open()

Трохи ускладнимо завдання — додамо кнопку, натиснувши на яку спливаюче вікно має закритися:

layout = BoxLayout(orientation="vertical")
layout.add_widget(Label(text='Отримано нове повідомлення'))
button = Button(text='Закрити')
layout.add_widget(button)
popup = Popup(title='Повідомлення', content=layout)
button.bind(on_press=popup.dismiss)
popup.open()

Теж нічого військового, але не зовсім зручно для звичайного MessageBox'а, правда?

Як колишній Delphiнист, маю погану звичку викликати MessageBox одним рядком. І в жодному-то столітті погана звичка принесла користь — через кілька десятків чашок кави на сцену виходить XPopup

Головне — вчасно зупинитися
Мирна ідея спростити собі життя зі спливаючими повідомленнями в підсумку зросла в цілий пакет, який із задоволенням був віджатий включений командою розробників в пакет розширень фреймворка.

Для порівняння — вищеописана завдання з кнопкою реалізується наступним чином:

XNotifyBase(title='Отримано нове повідомлення!', text='Що будемо робити?',
buttons=['Відкрити', 'Позначити прочитаним', 'Нагадати пізніше'])

Але підемо по порядку. Ієрархія класів пакету виглядає так:

Popup
Створюємо зручності
Примітка У цій статті будуть розглянуті класи повідомлень (XNotifyBase та його нащадки). Детальніше про інших класах — в наступній статті.

Клас XNotification

Спливаюче вікно з заголовком і текстом, без кнопок. Його особливістю є здатність автоматично закриватися після закінчення вказаної кількості секунд:

XNotification(text='Це вікно закриється через 3 секунди', show_time=3)

Якщо show_time не вказувати — вікно буде закрито тільки при виклику методу .dismiss().

Клас XMessage

Аналог звичного MessageBox, тобто вікно, що має заголовок (title), якесь повідомлення (text) і кнопку «Ok». Приклад:

XMessage(text='Якесь повідомлення', title='Заголовок')

Стандартна підпис кнопки легко змінюється на будь-яку іншу:

XMessage(text='Якесь повідомлення', title='Заголовок', buttons=['Закрити'])

Також просто замінюється набір кнопок:

XMessage(text='Якесь повідомлення', title='Заголовок', buttons=['Закрити', 'Нагадати пізніше'])

Про те, як обробити натискання кнопки — буде розказано нижче.

Клас XError

По суті — той же XMessage. Різниця в тому, що даний клас задає заголовок за замовчуванням (тобто title можна не вказувати):

XError(text='Сталася сферична помилка в вакуумі')

Не подобається стандартний заголовок — задаємо свій:

XError(text='Сталася сферична помилка в вакуумі', title='щось пішло не так...')

Щоб постійно не вказувати title, робимо наступне:

class MyError(XError):
buttons = ListProperty(['Закрити'])
title = StringProperty('щось пішло не так...')

MyError(text='Сталася сферична помилка в вакуумі')

Клас XConfirmation

Спливаюче вікно з заголовком «Confirmation» і кнопками «Yes» і «No». Зручно використовувати у випадках, коли від користувача необхідно отримати підтвердження («Yes») якої-небудь дії або заборону («No») на виконання цієї дії.

Для початку нам потрібно оброблювач натискання кнопки:

def my_callback(instance):
# Метод класу XConfirmation, повертає True, якщо була натиснута кнопка "Так"
if instance.is_confirmed():
print('Ви погодилися')
else:
print('Ви відмовилися')

Після того, як обробник написаний, можна створювати спливаюче вікно:

XConfirmation(text='Необхідно підтвердження дії. Ви згодні?', on_dismiss=my_callback)

Знову ж таки, стандартні кнопки можна замінити своїми. Але в такому випадку перестане працювати метод .is_confirmed(), так як він орієнтується на натискання кнопки «Yes». Це легко вирішується шляхом використання властивості .button_pressed, зберігає назву натиснутої кнопки. Внесемо зміни в наш обробник:

def my_callback(instance):
# Властивість доступна всім нащадкам XBase
if instance.button_pressed == 'Підтвердити':
print('Ви погодилися')
else:
print('Ви відмовилися')

Тепер сміливо можна створювати вікно зі своїм набором кнопок:

XConfirmation(
text='Необхідно підтвердження дії. Ви згодні?',
on_dismiss=my_callback, buttons=['Підтвердити', 'Відмовити'])

Клас XProgress

Спливаюче вікно з індикатором прогресу і кнопкою «Cancel» (заголовок повідомлення — в комплекті). Для управління індикатором будемо використовувати наступні властивості:

  • value — поточний стан прогресу
  • max — максимальне значення прогресу (за замовчуванням, max=100)
Приклад:

popup = XProgress(value=100, max=1000, text='Йде обробка запиту...', title='Очікуйте', buttons=[])

Даний код відобразиться спливаюче вікно без кнопок, з 10% виконаного прогресу. Подальша зміна прогресу можливо двома способами.

Спосіб 1-й — за допомогою властивості .value:

# просто вказуємо нове значення прогресу
popup.value = 20

Спосіб 2-й — використовуємо метод .inc():

# збільшуємо поточний прогрес на 1 одиницю
popup.inc()
# збільшуємо поточний прогрес на 10 одиниць
popup.inc(10)

Особливість використання методу .inc() полягає в тому, що при досягненні максимального значення прогресу індикатор не зупиняється на максимумі, а відбувається «зациклення», т. е. прогрес скидається і відлік йде з нуля.

Приклад:

# Створюємо вікно і встановлюємо 90% прогресу
popup = XProgress(value=90, text='Йде обробка запиту...', title='Очікуйте')
# Додаємо 15 - тепер індикатор відображає 5%
popup.inc(15)

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

Поряд з методом .inc() буде корисний метод .complete(). Даний метод виконує наступне:

  • встановлює прогрес у максимальне значення
  • замінює наявне повідомлення «Complete»
  • ховає кнопки (якщо вони є)
  • автоматично закриває вікно через 2 секунди

Клас XNotifyBase

Вищеописаних класів може не вистачити на всі випадки життя. Не біда — беремо за основу XNotifyBase і малюємо все, що душі завгодно. Даний клас задає об'єкту наступна поведінка:

  • додає у спливаюче вікно міткуkivy.uix.label.Label для відображення повідомлення
  • містить властивість для управління даною міткою (.text)
Властивість .buttons успадковується від предка XBase, але про це пізніше.

Оперуючи наявними властивостями, можна створити власне повідомлення для одноразового використання:

XNotifyBase(title='Отримано нове повідомлення!', text='Що будемо робити?',
buttons=['Відкрити', 'Позначити прочитаним', 'Нагадати пізніше'])

або для багаторазового:

class NotifyNewMail(XNotifyBase):
buttons = ListProperty(['Відкрити', 'Позначити прочитаним', 'Нагадати пізніше'])
title = StringProperty('Отримано нове повідомлення!')
text = StringProperty('Що будемо робити?')
popup = NotifyNewMail()

Залишилося описати свій оброблювач натискання кнопки — і можна насолоджуватися отриманим результатом.

Післямова
Наочний посібник (відео демонстрацію) можна подивитися тут.
Завантажити пакет XPopup — тут.

Приємного всім кодинга.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.