Développement d'applications mobiles en Python. Bibliothèque KivyMD


Salutations! Aujourd'hui, nous parlerons à nouveau de la bibliothèque KivyMD - un ensemble de widgets pour le développement multiplateforme en Python dans le style de Material Design. Dans cet article, je ne passerai pas en revue les widgets KivyMD, comme dans un article récent , mais il s'agira plutôt de positionner les widgets. Quelque chose comme un tutoriel sur le développement d'applications mobiles en Python pour les débutants ne sera pas là, donc si vous entendez parler du framework Kivy pour la première fois, il est peu probable que vous soyez intéressé par tout cela. Eh bien, nous avons roulé sous la coupe!

L'autre jour, j'ai téléchargé l'application de démonstration Flutter UIKit depuis Google Play:


Et maintenant, nous allons essayer de répéter un écran de cette application. Regardons tout de suite les résultats: Flutter à gauche, Kivy & KivyMD à droite.

Certains éléments de l'interface utilisateur sont différents, non pas en raison de certaines caractéristiques techniques, à cause desquelles il était impossible d'obtenir un résultat identique, mais je pensais juste que ce serait plus organique (par exemple, la barre d'outils noire, à mon avis, ne semble pas du tout).

Alors! Qu'est-ce qui frappe quand on regarde l'écran que l'on va jouer? Disposition avant de fond transparent. Dans Kivy, cette fonctionnalité est fournie par FloatLayout, qui vous permet de placer des widgets et des contrôles les uns dans les autres comme suit:


Schématiquement, notre écran ressemblera à ceci:


La disposition de cet écran est assez simple:


Pourquoi est-ce que je parle de FloatLayout si notre écran est hérité de Screen?

<ProductScreen@Screen>: ... 

Tout simplement parce que Screen -> RelativeLayout -> FloatLayout.

Tous les widgets de FloatLayout sont positionnés à partir du coin inférieur gauche, c'est-à-dire qu'à l'écran, ils se voient automatiquement attribuer la position (0, 0). Dans le balisage, il n'est pas difficile de tracer l'ordre d'ajout d'éléments à l'écran de haut en bas:


Si quelqu'un a fait attention, nous avons indiqué la position à un seul widget:

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

En plus des coordonnées spécifiques (x, y), chaque widget dans Kivy peut recevoir un indice de position:

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

Donc, l'image de fond en bas ...

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

... grâce au widget FitImage (bibliothèque KivyMD), il est automatiquement étiré sur tout l'espace qui lui est alloué tout en conservant les proportions de l'image:



Par défaut, chaque widget et disposition dans Kivy reçoit 100% de l'espace, sauf indication contraire. Par exemple, si vous souhaitez ajouter un bouton à l'écran, vous allez évidemment procéder comme suit:

 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() 

Et obtenez le résultat:


Le bouton occupait 100% de l'espace. Pour placer un bouton au centre de l'écran, vous devez, d'une part, lui donner la taille requise et, d'autre part, indiquer où il sera situé:

 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() 

Maintenant, l'image a changé:


Vous pouvez également spécifier la propriété size_hint , de 0 à 1, (équivalant à 0-100%), c'est-à-dire l'indice de taille:

 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() 


Ou la même chose, mais l'indice de largeur ( 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 a une hauteur de 56dp, ne peut pas occuper tout l'espace, et si vous ne lui dites pas que sa place est en haut, il collera automatiquement au bas de l'écran:


La liste des cartes - OrderProductLayout (nous en parlerons ci-dessous) - est une ScrollView avec des éléments MDCard et elle occupe toute la hauteur de l'écran, mais grâce au rembourrage (valeurs de retrait en bas), il semble qu'elle soit située légèrement au-dessus du centre de l'écran. Eh bien, par défaut, MDBottomAppBar jette l'ancre sur la bordure inférieure de l'écran. Par conséquent, seul MDToolbar nous a indiqué où sa place.

Voyons maintenant ce qu'est le widget OrderProductLayout:


Comme vous pouvez le voir, ce sont quatre cartes intégrées dans ScrillView. Contrairement à l'écran parent, hérité de FloatLayout, ici tous les widgets sont lus de haut en bas.


Ceci est très pratique, car il existe une hiérarchie claire de widgets, une structure arborescente et en un coup d'œil, il est clair quel widget / contrôle appartient à quelle disposition. Dans Kivy, la disposition la plus couramment utilisée est BoxLayout - une boîte qui vous permet de placer des widgets à l'intérieur de vous verticalement ou horizontalement (par défaut - le dernier):


Cela peut être vu plus clairement sur le diagramme suivant, où l'orientation horizontale de BoxLayout est utilisée:


Nous avons interdit à BoxLayout d'utiliser 100% de l'espace - size_hint_y: None et dit - votre hauteur sera exactement la même que la hauteur de l'élément le plus haut imbriqué en vous - height: self.minimum_height .

Liste d'images:


Si nous voulions utiliser le défilement vertical de la liste, nous aurions besoin de changer le GridLayout comme suit:

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

Remplacez les lignes (colonnes) par des colonnes ( cols ) et indiquez au minimum non pas la largeur, mais la hauteur:

 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() 



Les cartes suivantes sont le choix de la couleur et de la taille (elles sont presque identiques):


Une caractéristique distinctive du langage de balisage Kv Language est non seulement la structure claire des widgets, mais aussi le fait que ce langage prend en charge certaines fonctionnalités du langage Python. A savoir: appeler des méthodes, créer / changer des variables, des opérations logiques, d'E / S et mathématiques ...


Calcul de la valeur déclarée dans Label ...

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

... se produit directement dans le balisage lui-même:

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

Et je ne croirai jamais que ce (code Flutter) ...



... plus logique et lisible que le code Kv Language:


Hier, on m'a demandé comment Kivy s'en sort avec l'environnement de développement. Existe-t-il des compléments automatiques, des chargements à chaud et d'autres équipements? Avec les compléments automatiques, tout va bien si vous utilisez PyCharm:


Quant au hotload ... Python est un langage interprété. Kivy utilise Python. En conséquence, pour voir le résultat, vous n'avez pas besoin de compiler le code, exécutez-le - voir / tester. Comme je l'ai dit, Kivy n'utilise pas d'API natives pour le rendu de l'interface utilisateur, il vous permet donc d'émuler différents modèles d'appareils et de plates-formes à l'aide du module d' écran . Il suffit d'exécuter votre projet avec les paramètres nécessaires pour que la fenêtre de l'application testée s'ouvre sur l'ordinateur comme si elle s'exécutait sur un appareil réel. Cela semble étrange, mais puisque Kivy résume la plate-forme lors du rendu de l'interface utilisateur, cela élimine le besoin d'émulateurs lourds et lents pour les tests. Cela ne s'applique qu'à l'interface utilisateur. Par exemple, l'application de test décrite dans cet article a été testée avec les options d' écran -m: droid2, portrait, scale = .75 .

A gauche - sur un appareil mobile, à droite - sur un ordinateur:

Liste complète des paramètres du module d'écran:
 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() } 


Eh bien, et enfin, le résultat final est le lancement sur un appareil mobile ...


La seule chose qui dérange est la vitesse de lancement. Au même Flutter, elle est tout simplement phénoménale!

J'espère avoir été utile à quelqu'un, à bientôt!

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


All Articles