Kivy - aún más fácil, más nativo
Continuamos una serie de artículos sobre el desarrollo de aplicaciones móviles con el marco Kivy . Hoy hablaremos sobre la maravillosa biblioteca KivyMD , una biblioteca para crear una interfaz nativa al estilo de Material Design de Android, escrita en el marco de Kivy.
Francamente, personalmente estoy infinitamente contento de no tener que
esculpir y contemplar curvas, widgets personalizados oscuros y
aterradores en las aplicaciones Kivy.
Al usar la biblioteca KivyMD en nuestros proyectos y un poco de
imaginación, es poco probable que alguien pueda distinguir visualmente
si su programa está escrito en Java o si usa el marco Kivy y Python.
Descargue, desempaquete KivyMD, vaya al directorio raíz del archivo desempaquetado y realice la instalación:
Después del lanzamiento, verá una aplicación que muestra los widgets nativos y los controladores que están disponibles para su uso en sus proyectos:
En el artículo no nos detendremos en ningún widget de biblioteca específico (su creación y sus parámetros están bellamente descritos en la misma kitchen_sink.py), pero crearemos una aplicación de demostración simple, "Contactos", utilizando KivyMD. Nuestra aplicación podrá crear contactos y grupos, así como agregar contactos creados a ellos. Bueno, en el camino cubriremos con más detalle algunos aspectos de la creación de una interfaz de aplicación en Kivy:
Para una creación simple de un proyecto predeterminado en Kivy, recomiendo CreatorKivyProject , una descripción detallada del trabajo con el que se describe en este artículo . Entonces, siguiendo las instrucciones del artículo en el enlace, se creó el proyecto DemoKivyContacts. Abra el archivo a lo largo de la ruta DemoKivyContacts / libs / uix / kv / startscreen.kv , elimine implacablemente todo su contenido y "dibuje" la pantalla de inicio de su aplicación.
Así es como se ve el marcado de esta interfaz en Kivy-Language:
Importamos estos widgets de la biblioteca KivyMD al comienzo del archivo de marca startscreen.kv:
Estas instrucciones en Kivy-Language son similares a las importaciones en scripts de python:
Por cierto, puede incluir otros archivos de marcas en el archivo kv si la interfaz, por ejemplo, es demasiado compleja:
Como puede ver, ScreenManager debe incluir uno o más widgets de pantalla (pantallas) que contendrán nuestro contenido (Actividad). En nuestro caso, estos son EmptyScreen (pantalla en blanco) y CreateContact (formulario para crear un nuevo contacto):
... y cambiando la Actividad pasando el nombre actual de la nueva pantalla al atributo actual :Descargue, desempaquete KivyMD, vaya al directorio raíz del archivo desempaquetado y realice la instalación:
python setup.py install
A continuación, instale las dependencias para KivyMD:
pip install kivy-garden
garden install recycleview
Después de instalar la biblioteca, puede ejecutar una muestra de prueba desde el archivo que descargó y desempaquetó:python kitchen_sink.py
Después del lanzamiento, verá una aplicación que muestra los widgets nativos y los controladores que están disponibles para su uso en sus proyectos:
En el artículo no nos detendremos en ningún widget de biblioteca específico (su creación y sus parámetros están bellamente descritos en la misma kitchen_sink.py), pero crearemos una aplicación de demostración simple, "Contactos", utilizando KivyMD. Nuestra aplicación podrá crear contactos y grupos, así como agregar contactos creados a ellos. Bueno, en el camino cubriremos con más detalle algunos aspectos de la creación de una interfaz de aplicación en Kivy:
Para una creación simple de un proyecto predeterminado en Kivy, recomiendo CreatorKivyProject , una descripción detallada del trabajo con el que se describe en este artículo . Entonces, siguiendo las instrucciones del artículo en el enlace, se creó el proyecto DemoKivyContacts. Abra el archivo a lo largo de la ruta DemoKivyContacts / libs / uix / kv / startscreen.kv , elimine implacablemente todo su contenido y "dibuje" la pantalla de inicio de su aplicación.
Así es como se ve el marcado de esta interfaz en Kivy-Language:
#:kivy 1.9.1
#:import CreateContact libs.uix.createcontact.CreateContact
#:import CallContact libs.uix.callcontact.CallContact
#:import EmptyScreen libs.uix.emptyscreen.EmptyScreen
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel
#:import MDTab kivymd.tabs.MDTab
###############################################################################
#
# СТАРТОВЫЙ ЭКРАН
#
###############################################################################
<StartScreen>:
id: root.manager
Screen:
name: 'root_screen'
BoxLayout:
#canvas:
# Rectangle:
# pos: self.pos
# size: self.size
# source: 'data/images/background.jpg'
orientation: 'vertical'
####################################################################
#
# ACTION BAR
#
####################################################################
Toolbar:
#canvas.before:
# Rectangle:
# pos: self.pos
# size: self.size
# source: 'data/images/background_toolbar.jpg'
id: action_bar
#background_color: app.data.alpha
background_color: app.theme_cls.primary_color
title: app.data.string_lang_contacts
left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
right_action_items: [['more-vert', lambda x: None]]
####################################################################
#
# TABBED PANEL
#
####################################################################
MDTabbedPanel:
id: tabs
tab_display_mode: 'text'
#tab_color: app.data.alpha
tab_text_color: app.data.tab_text_color
tab_indicator_color: app.data.tab_indicator_color
MDTab:
name: 'contacts'
text: app.data.string_lang_contacts
on_tab_press: app.on_tab_press(self.name)
ScreenManager:
id: screen_manager_tab_contacts
Screen:
name: 'empty_contacts_list'
EmptyScreen:
image: 'data/images/contacts.png'
text: app.data.string_lang_add_contacts
callback: app.show_form_create_contact
disabled: False
Screen:
name: 'create_contact'
CreateContact:
MDTab:
name: 'groups'
text: app.data.string_lang_groups
on_tab_press: app.on_tab_press(self.name)
ScreenManager:
id: screen_manager_tab_groups
Screen:
name: 'empty_groups_list'
EmptyScreen:
image: 'data/images/contacts.png'
text: app.data.string_lang_not_groups
callback: lambda: app.create_group()
disabled: False
Screen:
name: 'call_contact'
CallContact:
Toolbar:
id: action_bar
background_color: app.theme_cls.primary_color
title: app.data.string_lang_contacts
left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
right_action_items: [['more-vert', lambda x: None]]
MDTabbedPanel:
id: tabs
tab_display_mode: 'text'
tab_text_color: app.data.tab_text_color
tab_indicator_color: app.data.tab_indicator_color
MDTab:
name: 'contacts'
text: app.data.string_lang_contacts
on_tab_press: app.on_tab_press(self.name)
ScreenManager:
id: screen_manager_tab_contacts
Screen:
name: 'empty_contacts_list'
EmptyScreen:
image: 'data/images/contacts.png'
text: app.data.string_lang_add_contacts
callback: app.show_form_create_contact
disabled: False
Screen:
name: 'create_contact'
CreateContact:
MDTab:
name: 'groups'
text: app.data.string_lang_groups
on_tab_press: app.on_tab_press(self.name)
ScreenManager:
id: screen_manager_tab_groups
Screen:
name: 'empty_groups_list'
EmptyScreen:
image: 'data/images/contacts.png'
text: app.data.string_lang_not_groups
callback: lambda: app.create_group
Importamos estos widgets de la biblioteca KivyMD al comienzo del archivo de marca startscreen.kv:
#:import Toolbar kivymd.toolbar.Toolbar
#:import MDTabbedPanel kivymd.tabs.MDTabbedPanel
#:import MDTab kivymd.tabs.MDTab
Estas instrucciones en Kivy-Language son similares a las importaciones en scripts de python:
from kivymd.toolbar import Toolbar
from kivymd.tabs import MDTabbedPanel
from kivymd.tabs import MDTab
Por cierto, puede incluir otros archivos de marcas en el archivo kv si la interfaz, por ejemplo, es demasiado compleja:
#:include your_kv_file.kv
Tenemos dos pestañas en MDTabbedPanel - "Contactos" y "Grupos".
El primero ("Contactos") contendrá un widget de ScreenManager
(administrador de pantalla) en el que colocaremos dos, en Java,
Actividad: MDTab:
name: 'contacts'
text: app.data.string_lang_contacts
on_tab_press: app.on_tab_press(self.name)
ScreenManager:
id: screen_manager_tab_contacts
Screen:
name: 'empty_contacts_list'
EmptyScreen:
image: 'data/images/contacts.png'
text: app.data.string_lang_add_contacts
callback: app.show_form_create_contact
disabled: False
Screen:
name: 'create_contact'
CreateContact:
Como puede ver, ScreenManager debe incluir uno o más widgets de pantalla (pantallas) que contendrán nuestro contenido (Actividad). En nuestro caso, estos son EmptyScreen (pantalla en blanco) y CreateContact (formulario para crear un nuevo contacto):
Screen:
name: 'empty_contacts_list'
…
Screen:
name: 'create_contact'
…
... usando el objeto ScreenManager ...ScreenManager: id: screen_manager_tab_contacts... en el código del programa por su identificador del marcado que creamos:
self.manager_tab_contacts.current = 'create_contact'
Ahora
"dibujemos" nuestra actividad: pantalla vacía (pantalla en blanco) y
CreateContact (formulario para crear un nuevo contacto). Cree archivos de marcación de interfaz en el directorio del proyecto DemoKivyContacts / libs / uix / kv emptyscreen.kv y createcontact.kv y scripts de python del mismo nombre en el directorio DemoKivyContacts / libs / uix para administrar y transferir los parámetros a los widgets Creados en Pantalla vacía y CrearContacto:#:kivy 1.9.1
#:import MDLabel kivymd.label.MDLabel
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton
<EmptyScreen>:
id: empty_screen
Image:
source: root.image
pos_hint: {'center_x': .5, 'center_y': .6}
opacity: .5
MDLabel:
id: label
font_style: 'Headline'
theme_text_color: 'Primary'
color: app.data.text_color
text: root.text
halign: 'center'
MDFloatingActionButton:
id: float_act_btn
icon: 'plus'
size_hint: None, None
size: dp(56), dp(56)
opposite_colors: True
elevation_normal: 8
pos_hint: {'center_x': .9, 'center_y': .1}
background_color: app.data.floating_button_color
background_color_down: app.data.floating_button_down_color
disabled: root.disabled
on_release: root.callback()
#:kivy 1.9.1
#:import SingleLineTextField kivymd.textfields.SingleLineTextField
#:import MDIconButton kivymd.button.MDIconButton
#:import MDFlatButton kivymd.button.MDFlatButton
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton
<CreateContact>:
orientation: 'vertical'
FloatLayout:
size_hint: 1, .3
Image:
id: avatar
pos_hint: {'center_y': .5}
source: 'data/images/avatar_empty.png'
MDFloatingActionButton:
icon: 'plus'
size_hint: None, None
size: dp(56), dp(56)
opposite_colors: True
elevation_normal: 8
pos_hint: {'center_x': .9, 'center_y': .20}
background_color: app.data.floating_button_color
background_color_down: app.data.floating_button_down_color
on_release: app.choice_avatar_contact()
BoxLayout:
orientation: 'vertical'
padding: 5, 5
size_hint: 1, .3
BoxLayout:
MDIconButton:
icon: 'account'
disabled: True
SingleLineTextField:
id: name_field
hint_text: 'ИФО'
BoxLayout:
MDIconButton:
icon: 'phone'
disabled: True
SingleLineTextField:
id: number_field
hint_text: 'Номер'
BoxLayout:
MDIconButton:
icon: 'email'
disabled: True
SingleLineTextField:
id: email_field
hint_text: 'E-mail'
Widget:
size_hint: 1, .3
AnchorLayout:
anchor_x: 'right'
anchor_y: 'bottom'
size_hint: 1, None
height: dp(40)
MDFlatButton:
id: button_ok
text: 'OK'
on_release: app.save_info_contact()
emptyscreen.py
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty, ObjectProperty, BooleanProperty
class EmptyScreen(FloatLayout):
image = StringProperty()
text = StringProperty()
callback = ObjectProperty()
disabled = BooleanProperty()
createcontact.pyfrom kivy.uix.boxlayout import BoxLayout
class CreateContact(BoxLayout):
pass
En EmptyScreen, usamos otro widget de la biblioteca KivyMD - MDFloatingActionButton, que vale la pena describir. Esa misma mosca molesta que muchos usuarios quieren golpear: MDFloatingActionButton:
id: float_act_btn
icon: 'plus'
size_hint: None, None
size: dp(56), dp(56)
opposite_colors: True # иконка белого/черного цветов
elevation_normal: 8 # длинна тени
pos_hint: {'center_x': .9, 'center_y': .1} # самое нужное место на экране, которое кнопка обязательно закроет
background_color: app.data.floating_button_color
background_color_down: app.data.floating_button_down_color
disabled: root.disabled
on_release: root.callback()
CreateContact utiliza widgets de la biblioteca KivyMD:MDIconButton:
MDIconButton es un botón con un icono de vector. El conjunto completo de iconos oficiales de Google, vea el enlace . Todos ellos se usan y están disponibles en KivyMD.
SingleLineTextField:
MDFlatButton:
Llamará a la función de guardar la entrada del usuario:
MDFlatButton:
…
on_release: app.save_info_contact()
Recibiremos
la información ingresada por el usuario de los campos
SingleLineTextField utilizando el método ya descrito anteriormente, por
su identificación del atributo de texto :DemoKivyContacts/libs/uix/kv/createcontact.kv
DemoKivyContacts/libs/programclass/showformcreatecontact.py
def show_form_create_contact(self, *args):
'''Выводит на экран форму для создания нового контакта.'''
self.manager_tab_contacts.current = 'create_contact'
# <class 'libs.uix.createcontact.CreateContact'>
self._form_create_contact = \
self.manager_tab_contacts.current_screen.children[0]
...
def save_info_contact(self):
'''Сохраняет информацию о новом контакте.'''
name_contact = self._form_create_contact.ids.name_field.text
number_contact = self._form_create_contact.ids.number_field.text
mail_contact = self._form_create_contact.ids.email_field.text
...
Después
de guardar los datos, el programa crea una lista de contactos si no se
ha creado, o agrega uno nuevo a la lista ya existente y la muestra en la
pantalla: def show_contacts(self, info_contacts):
'''
:type info_contacts: dict;
:param info_contacts: {
'Name contact': ['Number contact\nMail contact', 'path/to/avatar']
};
'''
if not self._contacts_items:
# Создаем список контактов.
self._contacts_list = ContactsList()
self._contacts_items = Lists(
dict_items=info_contacts, flag='three_list_custom_icon',
right_icons=self.data.right_icons,
events_callback=self._event_contact_item
)
button_add_contact = Builder.template(
'ButtonAdd', disabled=False,
events_callback=self.show_form_create_contact
)
self._contacts_list.add_widget(self._contacts_items)
self._contacts_list.add_widget(button_add_contact)
self.add_screens(
'contact_list', self.manager_tab_contacts, self._contacts_list
)
else:
# Добавляет контакт к существующему списку
# и выводит список на экран.
self._add_contact_item(info_contacts)
self.manager_tab_contacts.current = 'contact_list'
DemoKivyContacts/program.py
def add_screens(self, name_screen, screen_manager, new_screen):
screen = Screen(name=name_screen) # cоздаем новый экран
screen.add_widget(new_screen) # добавляем Activity в созданный экран
screen_manager.add_widget(screen) # добавляем экран в менеджер экранов
screen_manager.current = name_screen # указываем менеджеру имя Activity, которое должно стать текущим экраном приложения
Escribí un enlace pequeño (pero torpe) para crear listas MDList - DemoKivyContacts / libs / uix / lists.py Puede crear fácilmente un elemento de lista con un icono a la izquierda y los iconos de vector a la derecha creando una instancia de la clase Listas con los parámetros necesarios.
def show_contacts(self, info_contacts):
'''
:type info_contacts: dict;
:param info_contacts: {
'Name contact': ['Number contact\nMail contact', 'path/to/avatar']
};
'''
…
self._contacts_items = Lists(
dict_items=info_contacts, flag='three_list_custom_icon',
right_icons=self.data.right_icons,
events_callback=self._event_contact_item
)
A continuación, la lista self._contacts_items arroja sobre cualquier widget deseado. Al crear el elemento de la lista, pasamos la función _event_contact_item al parámetro events_callback para manejar los eventos de conexión :
def _event_contact_item(self, *args):
'''События пункта списка контактов.'''
def end_call():
self.screen.current = 'root_screen'
instanse_button = args[0]
if type(instanse_button) == RightButton:
name_contact, name_event = instanse_button.id.split(', ')
if name_event == 'call':
self.screen.current = 'call_contact'
data_contact = self.info_contacts[name_contact]
call_screen = self.screen.current_screen.children[0]
call_screen.name_contact = name_contact
call_screen.number_contact = data_contact[0].split('\n')[0]
call_screen.avatar = data_contact[1]
call_screen.callback = end_call
elif name_event == 'groups':
self._show_names_groups(name_contact)
else:
name_contact, name_event = args
Los identificadores de eventos 'call' y 'group' son los nombres de los iconos que especificamos en el parámetro right_icons :DemoKivyContacts/libs/programdata.py
…
right_icons = ['data/images/call.png', 'data/images/groups.png']
Al hacer clic en el icono de llamada, se abrirá una pantalla de simulación de llamada saliente:
def _event_contact_item(self, *args):
def end_call():
self.screen.current = 'root_screen'
…
if name_event == 'call':
self.screen.current = 'call_contact'
call_screen = self.screen.current_screen.children[0]
…
call_screen.callback = end_call
Todos los widgets ya se han descrito, así que solo daré el diseño de esta Actividad:#:kivy 1.9.1
#:import MDIconButton kivymd.button.MDIconButton
#:import MDFloatingActionButton kivymd.button.MDFloatingActionButton
#:import MDLabel kivymd.label.MDLabel
<CallContact>:
id: call_contact
Widget:
id: title_line
canvas:
Color:
rgba: app.theme_cls.primary_color
Rectangle:
size: self.size
pos: self.pos
size_hint_y: None
height: root.height * 30 // 100 # 30% от высоты экрана
pos: 0, call_contact.height - self.size[1]
Widget:
canvas:
Ellipse:
pos: self.pos
size: 150, 150
source: root.avatar if root.avatar else 'data/logo/kivy-icon-128.png'
pos: (call_contact.width // 2) - 75, call_contact.height * 61 // 100
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: 50
pos: self.pos[0], call_contact.height * 45 // 100
MDLabel:
id: name_contact
font_style: 'Headline'
theme_text_color: 'Primary'
color: app.data.text_color
text: root.name_contact if root.name_contact else 'Abonent'
halign: 'center'
MDLabel:
id: number_contact
font_style: 'Subhead'
theme_text_color: 'Primary'
color: app.data.text_color
text: root.number_contact if root.number_contact else '12345'
halign: 'center'
BoxLayout:
size_hint: None, None
height: 60
width: volume.width + dialpad.width + account.width + mic.width
pos: (call_contact.width // 2) - (self.width // 2), call_contact.height * 18 // 100
MDIconButton:
id: volume
icon: 'volume-mute'
MDIconButton:
id: dialpad
icon: 'dialpad'
MDIconButton:
id: account
icon: 'account'
MDIconButton:
id: mic
icon: 'mic'
MDFloatingActionButton:
id: phone_end
icon: 'phone-end'
size_hint: None, None
size: dp(56), dp(56)
opposite_colors: True # иконка белого/черного цветов
elevation_normal: 8 # длинна тени
pos_hint: {'center_x': .5, 'center_y': .1}
background_color: app.data.floating_button_color_end_call
background_color_down: app.data.floating_button_down_color_end_call
on_release: root.callback()
El widget CallContact hereda de FloatLayout:
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import StringProperty, ObjectProperty
class CallContact(FloatLayout):
callback = ObjectProperty(lambda: None)
avatar = StringProperty(None)
name_contact = StringProperty(None)
number_contact = StringProperty(None)
Esto significa que todos los widgets y controladores se superpondrán entre sí, por lo que en el marcado utilicé una indicación porcentual de sus posiciones en relación con la altura de la pantalla principal:
pos: self.pos[0], call_contact.height * 45 // 100
Ahora que sabe cómo funciona ScreenManager, echemos otro vistazo a la clase de gestión de la Actividad inicial:
from kivy.uix.screenmanager import ScreenManager
from kivy.properties import ObjectProperty
class StartScreen(ScreenManager):
events_callback = ObjectProperty(lambda: None)
'''Функция обработки сигналов экрана.'''
y marcado esqueleto:<StartScreen>:
Screen:
name: 'root_screen'
…
# Экран с вкладками — MDTabbedPanel
Screen:
name: 'call_contact'
CallContact:
Es
decir, cuando presiona el botón de llamada en el elemento de la lista
de contactos, abrimos la Actividad para simular una llamada saliente y
la cerramos cuando presiona el botón de finalizar llamada: def _event_contact_item(self, *args):
def end_call():
self.screen.current = 'root_screen'
…
if name_event == 'call':
self.screen.current = 'call_contact'
call_screen = self.screen.current_screen.children[0]
…
call_screen.callback = end_call
No consideraremos el proceso de crear un grupo, ya que es similar al proceso de crear un nuevo contacto. Veamos el widget NavigationDrawer:Para usar el panel NavigationDrawer, necesitamos crear su marcado y clase de control heredada de NavigationDrawer:
#:kivy 1.9.1
<NavDrawer>:
NavigationDrawerIconButton:
icon: 'settings'
text: app.data.string_lang_settings
on_release: app.events_program(self.text)
NavigationDrawerIconButton:
icon: 'view-module'
text: app.data.string_lang_plugin
on_release: app.events_program(self.text)
NavigationDrawerIconButton:
icon: 'info'
text: app.data.string_lang_license
on_release: app.events_program(self.text)
NavigationDrawerIconButton:
icon: 'collection-text'
text: 'About'
on_release: app.events_program(self.text)
NavigationDrawerIconButton:
icon: 'close-circle'
text: app.data.string_lang_exit_key
on_release: app.events_program(app.data.string_lang_exit_key)
DemoKivyContacts/program.py
from kivy.app import App
from kivy.properties import ObjectProperty
from kivymd.navigationdrawer import NavigationDrawer
class NavDrawer(NavigationDrawer):
events_callback = ObjectProperty()
class Program(App):
nav_drawer = ObjectProperty()
def __init__(self, **kvargs):
super(Program, self).__init__(**kvargs)
def build(self):
self.nav_drawer = NavDrawer(title=data.string_lang_menu)
Eso es todo por ahora. Puede ver el escenario completo del proyecto en github.
PS
¡Sin duda, la biblioteca KivyMD es una gran adición al marco Kivy! Espero que lo domines y lo apliques en tus proyectos.
Tengo una sugerencia para cambiar el formato de los artículos sobre el desarrollo de aplicaciones móviles usando Kivy: tome una aplicación nativa de Android preparada escrita en Java y cree una similar, pero escrita en Python usando el marco Kivy, cubriendo todo el proceso de desarrollo desde cero: cómo Los widgets y controladores se crean en Kivy, cómo usar clases dinámicas, qué es FloatLayout, etc.
Fuente: https://m.habr.com/en/post/313160/
Comentarios
Publicar un comentario