Desarrollo de aplicaciones móviles en Python. Biblioteca KivyMD


Saludos! Hoy volveremos a hablar sobre la biblioteca KivyMD , un conjunto de widgets para el desarrollo multiplataforma en Python al estilo de Material Design. En este artículo, no revisaré los widgets de KivyMD, como en un artículo reciente , sino que más bien se tratará de posicionar widgets. Algo así como un tutorial sobre el desarrollo de aplicaciones móviles en Python para principiantes no estará aquí, por lo que si escuchas por primera vez sobre el marco de Kivy, es poco probable que te interese todo esto. Bueno, condujimos bajo el corte!

El otro día descargué la aplicación de demostración Flutter UIKit de Google Play:


Y ahora intentaremos repetir una pantalla desde esta aplicación. Veamos los resultados de inmediato: Aleteo a la izquierda, Kivy y KivyMD a la derecha.

Algunos elementos de la interfaz de usuario son diferentes, no debido a algunas características técnicas, por lo que fue imposible obtener un resultado idéntico, pero pensé que sería más orgánico (por ejemplo, la barra de herramientas negra, en mi opinión, no se ve en absoluto).

Entonces! ¿Qué es sorprendente al mirar la pantalla que vamos a jugar? Diseño frontal de fondo transparente. En Kivy, esta opción es proporcionada por FloatLayout, que le permite colocar widgets y controles uno encima del otro de la siguiente manera:


Esquemáticamente, nuestra pantalla se verá así:


El diseño de esta pantalla es bastante simple:


¿Por qué estoy hablando de FloatLayout si nuestra pantalla se hereda de Screen?

<ProductScreen@Screen>: ... 

Solo porque Screen -> RelativeLayout -> FloatLayout.

Todos los widgets en FloatLayout se colocan desde la esquina inferior izquierda, es decir, en la pantalla se les asigna automáticamente la posición (0, 0). En el marcado, no es difícil rastrear el orden de agregar elementos a la pantalla de arriba a abajo:


Si alguien prestó atención, entonces indicamos la posición a un solo widget:

 MDToolbar: ... pos_hint: {"top": 1} 

Además de las coordenadas específicas (x, y), cada widget en Kivy puede recibir una pista de posición:

 pos_hint: {"top": 1} #    pos_hint: {"bottom": 1} #    pos_hint: {"right": 1} #    pos_hint: {"center_y": .5} #     pos_hint: {"center_x": .2} #   20 %       ... ... 

Entonces, la imagen de fondo inferior ...

  BoxLayout: size_hint_y: None height: root.height - toolbar.height FitImage: source: "smokestackheather.jpeg" 

... gracias al widget FitImage (biblioteca KivyMD), se estira automáticamente a todo el espacio asignado mientras se mantienen las proporciones de la imagen:



Por defecto, cada widget y diseño en Kivy tiene el 100% del espacio, a menos que se especifique lo contrario. Por ejemplo, si desea agregar un botón a la pantalla, obviamente hará lo siguiente:

 from kivy.app import App from kivy.lang import Builder KV = """ Button: text: "Button" """ class MyApp(App): def build(self): return Builder.load_string(KV) MyApp().run() 

Y obtén el resultado:


El botón ocupaba el 100% del espacio. Para colocar un botón en el centro de la pantalla, primero debe establecer el tamaño requerido y, en segundo lugar, indicar dónde se ubicará:

 from kivy.app import App from kivy.lang import Builder KV = """ Button: text: "Button" size_hint: None, None size: 100, 50 pos_hint: {"center_y": .5, "center_x": .5} """ class MyApp(App): def build(self): return Builder.load_string(KV) MyApp().run() 

Ahora la imagen ha cambiado:


También puede especificar la propiedad size_hint , de 0 a 1 (equivalente a 0-100%), es decir, la sugerencia de tamaño:

 from kivy.app import App from kivy.lang import Builder KV = """ BoxLayout: Button: text: "Button" size_hint_y: .2 Button: text: "Button" size_hint_y: .1 Button: text: "Button" """ class MyApp(App): def build(self): return Builder.load_string(KV) MyApp().run() 


O lo mismo, pero la pista de ancho ( size_hint_x ):

 from kivy.app import App from kivy.lang import Builder KV = """ BoxLayout: Button: text: "Button" size_hint_x: .2 Button: text: "Button" size_hint_x: .1 Button: text: "Button" """ class MyApp(App): def build(self): return Builder.load_string(KV) MyApp().run() 


MDToolbar tiene una altura de 56dp, no puede ocupar todo el espacio, y si no le dice que su lugar está en la parte superior, se pegará automáticamente en la parte inferior de la pantalla:


La lista de tarjetas, OrderProductLayout (hablaremos de ello a continuación), es una ScrollView con elementos MDCard y ocupa toda la altura de la pantalla, pero gracias al relleno (valores de sangría en mínimos) parece que se encuentra ligeramente por encima del centro de la pantalla. Bueno, por defecto MDBottomAppBar coloca el ancla en el borde inferior de la pantalla. Por lo tanto, solo MDToolbar indicamos dónde está su lugar.

Ahora veamos qué es el widget OrderProductLayout:


Como puede ver, estas son cuatro tarjetas integradas en ScrillView. A diferencia de la pantalla principal, que se hereda de FloatLayout, aquí todos los widgets se leen de arriba a abajo.


Esto es muy conveniente, ya que existe una clara jerarquía de widgets, una estructura de árbol, y de un vistazo está claro qué widget / control pertenece a qué diseño. En Kivy, el diseño utilizado más comúnmente es BoxLayout, un cuadro que le permite colocar widgets dentro de usted vertical u horizontalmente (de forma predeterminada, el último):


Esto se puede ver más claramente en el siguiente diagrama, donde se usa la orientación horizontal de BoxLayout:


Prohibimos a BoxLayout usar el 100% del espacio - size_hint_y: None y dicho - su altura será exactamente la misma que la altura del elemento más alto anidado en usted - height: self.minimum_height .

Lista de imágenes:


Si quisiéramos usar el desplazamiento vertical de la lista, necesitaríamos cambiar el GridLayout de la siguiente manera:

  ScrollView: GridLayout: size_hint_y: None height: self.minimum_height cols: 1 

Reemplace las filas (columnas) con columnas ( cols ) e indique en el mínimo no el ancho, sino la altura:

 from kivy.app import App from kivy.lang import Builder from kivy.metrics import dp from kivy.uix.button import Button KV = """ ScrollView: GridLayout: id: box size_hint_y: None height: self.minimum_height spacing: "5dp" cols: 1 """ class MyApp(App): def build(self): return Builder.load_string(KV) def on_start(self): for i in range(20): self.root.ids.box.add_widget( Button( text=f"Label {i}", size_hint_y=None, height=dp(40), ) ) MyApp().run() 



Las siguientes tarjetas son la elección de color y tamaño (son casi idénticas):


Una característica distintiva del lenguaje de marcado Kv Language no es solo la estructura clara de los widgets, sino también el hecho de que este lenguaje admite algunas características del lenguaje Python. A saber: métodos de llamada, creación / cambio de variables, operaciones lógicas, de E / S y matemáticas ...


Calculando el valor declarado en Etiqueta ...

  Label: value: 0 text: str(self.value) 

... sucede directamente en el marcado en sí:

  MDIconButton: on_release: label_value.value -= 1 if label_value.value > 0 else 0 

Y nunca creeré que esto (código Flutter) ...



... más lógico y legible que el código Kv Language:


Ayer me preguntaron cómo le está yendo a Kivy con el entorno de desarrollo, ¿hay autocomplits, hotloads y otras comodidades? Con autocomplits, todo está bien si usa PyCharm:


En cuanto a la carga en caliente ... Python es un lenguaje interpretado. Kivy usa Python. En consecuencia, para ver el resultado, no necesita compilar el código, ejecutarlo, verlo / probarlo. Como dije, Kivy no usa API nativas para renderizar la interfaz de usuario, por lo tanto, le permite emular varios modelos de dispositivos y plataformas utilizando el módulo de pantalla . Es suficiente ejecutar su proyecto con los parámetros necesarios para que la ventana de la aplicación probada se abra en la computadora como si se estuviera ejecutando en un dispositivo real. Suena extraño, pero dado que Kivy extrae de la plataforma al procesar la interfaz de usuario, esto elimina la necesidad de emuladores pesados ​​y lentos para las pruebas. Esto solo se aplica a la interfaz de usuario. Por ejemplo, la aplicación de prueba descrita en este artículo se probó con las opciones de pantalla -m: droid2, portrait, scale = .75 .

A la izquierda, ejecutándose en un dispositivo móvil, a la derecha, en una computadora:

Lista completa de parámetros del módulo de pantalla:
 devices = { # device: (name, width, height, dpi, density) 'onex': ('HTC One X', 1280, 720, 312, 2), 'one': ('HTC One', 1920, 1080, 468, 3), 'onesv': ('HTC One SV', 800, 480, 216, 1.5), 's3': ('Galaxy SIII', 1280, 720, 306, 2), 'note2': ('Galaxy Note II', 1280, 720, 267, 2), 'droid2': ('Motorola Droid 2', 854, 480, 240, 1.5), 'xoom': ('Motorola Xoom', 1280, 800, 149, 1), 'ipad': ('iPad (1 and 2)', 1024, 768, 132, 1), 'ipad3': ('iPad 3', 2048, 1536, 264, 2), 'iphone4': ('iPhone 4', 960, 640, 326, 2), 'iphone5': ('iPhone 5', 1136, 640, 326, 2), 'xperiae': ('Xperia E', 480, 320, 166, 1), 'nexus4': ('Nexus 4', 1280, 768, 320, 2), 'nexus7': ('Nexus 7 (2012 version)', 1280, 800, 216, 1.325), 'nexus7.2': ('Nexus 7 (2013 version)', 1920, 1200, 323, 2), # taken from design.google.com/devices # please consider using another data instead of # a dict for autocompletion to work # these are all in landscape 'phone_android_one': ('Android One', 854, 480, 218, 1.5), 'phone_htc_one_m8': ('HTC One M8', 1920, 1080, 432, 3.0), 'phone_htc_one_m9': ('HTC One M9', 1920, 1080, 432, 3.0), 'phone_iphone': ('iPhone', 480, 320, 168, 1.0), 'phone_iphone_4': ('iPhone 4', 960, 640, 320, 2.0), 'phone_iphone_5': ('iPhone 5', 1136, 640, 320, 2.0), 'phone_iphone_6': ('iPhone 6', 1334, 750, 326, 2.0), 'phone_iphone_6_plus': ('iPhone 6 Plus', 1920, 1080, 400, 3.0), 'phone_lg_g2': ('LG G2', 1920, 1080, 432, 3.0), 'phone_lg_g3': ('LG G3', 2560, 1440, 533, 3.0), 'phone_moto_g': ('Moto G', 1280, 720, 327, 2.0), 'phone_moto_x': ('Moto X', 1280, 720, 313, 2.0), 'phone_moto_x_2nd_gen': ('Moto X 2nd Gen', 1920, 1080, 432, 3.0), 'phone_nexus_4': ('Nexus 4', 1280, 768, 240, 2.0), 'phone_nexus_5': ('Nexus 5', 1920, 1080, 450, 3.0), 'phone_nexus_5x': ('Nexus 5X', 1920, 1080, 432, 2.6), 'phone_nexus_6': ('Nexus 6', 2560, 1440, 496, 3.5), 'phone_nexus_6p': ('Nexus 6P', 2560, 1440, 514, 3.5), 'phone_samsung_galaxy_note_4': ('Samsung Galaxy Note 4', 2560, 1440, 514, 3.0), 'phone_samsung_galaxy_s5': ('Samsung Galaxy S5', 1920, 1080, 372, 3.0), 'phone_samsung_galaxy_s6': ('Samsung Galaxy S6', 2560, 1440, 576, 4.0), 'phone_sony_xperia_c4': ('Sony Xperia C4', 1920, 1080, 400, 2.0), 'phone_sony_xperia_z_ultra': ('Sony Xperia Z Ultra', 1920, 1080, 348, 2.0), 'phone_sony_xperia_z1_compact': ('Sony Xperia Z1 Compact', 1280, 720, 342, 2.0), 'phone_sony_xperia_z2z3': ('Sony Xperia Z2/Z3', 1920, 1080, 432, 3.0), 'phone_sony_xperia_z3_compact': ('Sony Xperia Z3 Compact', 1280, 720, 313, 2.0), 'tablet_dell_venue_8': ('Dell Venue 8', 2560, 1600, 355, 2.0), 'tablet_ipad': ('iPad', 1024, 768, 132, 1.0), 'tablet_ipad_mini': ('iPad Mini', 1024, 768, 163, 1.0), 'tablet_ipad_mini_retina': ('iPad Mini Retina', 2048, 1536, 326, 2.0), 'tablet_ipad_pro': ('iPad Pro', 2732, 2048, 265, 2.0), 'tablet_ipad_retina': ('iPad Retina', 2048, 1536, 264, 2.0), 'tablet_nexus_10': ('Nexus 10', 2560, 1600, 297, 2.0), 'tablet_nexus_7_12': ('Nexus 7 12', 1280, 800, 216, 1.3), 'tablet_nexus_7_13': ('Nexus 7 13', 1920, 1200, 324, 2.0), 'tablet_nexus_9': ('Nexus 9', 2048, 1536, 288, 2.0), 'tablet_samsung_galaxy_tab_10': ('Samsung Galaxy Tab 10', 1280, 800, 148, 1.0), 'tablet_sony_xperia_z3_tablet': ('Sony Xperia Z3 Tablet', 1920, 1200, 282, 2.0), 'tablet_sony_xperia_z4_tablet': ('Sony Xperia Z4 Tablet', 2560, 1600, 297, 2.0)TodoList() app.run() } 


Bueno, y finalmente, el resultado final es el lanzamiento en un dispositivo móvil ...


Lo único que molesta es la velocidad de lanzamiento. ¡Al mismo tiempo, Flutter es simplemente fenomenal!

Espero haber sido útil para alguien, ¡hasta pronto!

Source: https://habr.com/ru/post/480018/


All Articles