======Kivy: Les fichiers kv====== **{{tagpage>kivy|Toutes les pages Kivy}}** **[[http://translate.google.com/translate?hl=&sl=auto&tl=en&u=https%3A%2F%2Fressources.labomedia.org%2Fkivy_les_fichiers_kv|English Version]]** **[[les_pages_kivy_en_details|Les pages Kivy en détails]]** {{ media_05:comments_in_kv.png?600 |}}\\ Avec des commentaires ! ===== Le langage kivy dans les fichiers *.kv ===== * [[https://kivy.org/docs/guide/lang.html|Documentation Kivy sur le langage kivy]] * Ce qui suit est une adaptation de kivy-vision.fr qui n'est plus en ligne ===== Ressources sur les fichiers kv ===== * [[http://kivy.org/docs/api-kivy.lang.html|Kivy Language]] * [[http://kivy.org/docs/guide/lang.html|Programming Guide Kv language]] ===== Nommage pour le fichier kv ===== * [[http://www.blog.pythonlibrary.org/2013/11/25/kivy-101-how-to-use-boxlayouts/|Un tuto]] repris en partie ici pour le nommage. ==== Exemple ==== **app_with_kv.py** ''' Application from a .kv The root application is created from the corresponding .kv. Check the test.kv file to see what will be the root widget. ''' import kivy kivy.require('1.0.7') from kivy.app import App class TestApp(App): pass if __name__ == '__main__': TestApp().run() **test.kv** #:kivy 1.0 Button: text: 'Hello world' Le fichier .kv doit avoir le même nom que la class App, mais avec une extension .kv et tout en minuscule. Cela signifie que le fichier doit s'appeler kvboxlayout.kv, puisque la class App s'appelle KVBoxLayoutApp. class TestApp(App) --> Test --> test --> test.kv Si vous ne respectez pas la convention, vous n'aurez pas d'erreur mais seulement une fenêtre noire. ===== Comment adapter un main.py avec Builder.load_string() pour un fichier *.kv ===== Permet de comprendre comment les éléments du fichier kv sont lu! === Sans kv === **without_kv.py** #! /usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.uix.button import Button from kivy.lang import Builder Builder.load_string(''' : Button: text: "bla bla !" ''') class Withoutkv(FloatLayout): pass class TestApp(App): def build(self): return Withoutkv() if __name__ == '__main__': TestApp().run() === Avec kv === **with_kv.py** #! /usr/bin/env python3 # -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.floatlayout import FloatLayout class Withkvtest(FloatLayout): pass class With_kvApp(App): def build(self): # Retourne le widget root return Withkvtest() if __name__ == '__main__': With_kvApp().run() **with_kv.kv** : Button: text: "bla bla !" ===== Pricipes des fichiers *.kv ===== === Règles === Une règle s'applique à des widgets en particulier (ou par catégorie) dans votre arbre de widgets et les modifie d'une certaine façon. Vous pouvez utiliser des règles pour spécifier des comportements interactifs ou pour ajouter des représentations graphiques aux widgets auxquelles elles s'appliquent. Vous pouvez créer une règle qui s'applique à une classe particulère de widget en utilisant l'attribut cls (e.g. cls=MonWidgetTest). === Un widget racine === Vous pouvez utiliser le langage pour créer tout l'interface utilisateur. Un fichier kv doit contenir un seul root widget. === Des templates === Des templates seront utilisés pour construire des morceaux de votre application, comme des listes de contenu, ou par exemple, si vous voulez concevoir l'aspect d'une ligne dans une liste(icone à gauche, texte à droite). ===== Syntaxe d'un fichier kv ===== Le fichier peut contenir des défitions de règles, un widget racine et des templates: # Syntaxe de la définition d'une règle. Plusieurs règle peuvent partager la même définition. #(as in CSS). Remarquez les caractère <>; Ils font partie de la définition. : # .. definitions .. : # .. definitions .. # Syntaxe pour créer un widget racine RootClassName: # .. definitions .. # Syntaxe pour créer un template [TemplateName@BaseClass1,BaseClass2]: # .. definitions .. Selon que vous soyiez en présence de règle, widget racine ou templates, la définition devrait ressembler à ceci. # Avec les "<" et ">", il s'agit d'une règle; sans, il s'agit d'un widget racine. : prop1: value1 prop2: value2 canvas: CanvasInstruction1: canvasprop1: value1 CanvasInstruction2: canvasprop2: value2 AnotherClass: prop3: value1 Ici prop1 et prop2 sont des propriétés de ClassName et prop3 est une propriété de AnotherClass. Si le widget ne possède pas de propriété avec le même nom,un ObjectProperty sera automatiquement créé et ajouté à l'instance. AnotherClass sera donc créé et ajouté en tant qu'enfant de l'instance de ClassName. La valeur d'une propriété doit être donnée sur une ligne unique. La propriété canvas est spéciale: vous pouvez indiquer des instructions de graphisme à l'intérieur pour créer des dessins dans la classe courrante. ===== Expressions Valeur et mots réservés ===== Lorsque vous indiquez la valeur d'une propriété, l'expression est évaluée comme une expression python. Cette expression peut être statique ou dynamique, ce qui veut dire que cette valeur peut utiliser les valeurs d'autres propriétés en utilisant des mots réservés. ==== self ==== Le mot clé self fait référence à l'instance du widget courant ici l'object MainScreen du main.py: : Button: text: "My state is {}".format(self.state) ==== root ==== Ce mot clé n'est disponible que dans la définition des règles, et représente le widget racine de la règle (la première instance de la règle). Ce n'est pas le widget racine de tous les widgets. : custom: 'Hello world' Button: text: root.custom La fonction build du main.py: class TestApp(App): def build(self): # return a Button() as a root widget return Button(text='hello world') if __name__ == '__main__': TestApp().run() ou def build(self): root = BoxLayout() for idx, word in enumerate(('Hello', 'World')): wid = Builder.template('BlehItem', **{ 'idx': idx, 'word': word, }) root.add_widget(wid) return root ==== app ==== Ce mot clé se réferre toujours à l'instance de votre application.\\ Les fichiers kv n'acceptent pas les "print", utilisez le texte d'un Label: Label: text: app.name Avec TestApp(App) retourne testapp ==== args ==== Ce mot clé est disponible dans les on_action callbacks. Il fait référence aux arguments passés au callback: TextInput: on_focus: self.insert_text("I'm focused!") if args[1] else self.insert_text("I'm not focused.") Par ailleurs, si la définition d'une classe contient un id, vous pouvez l'utiliser comme un mot clé: : Button: id: btn1 Button: text: 'The state of the other button is %s' % btn1.state Attention, l'id ne sera pas disponible dans l'instance du widget. L'attribut id ne sera pas utilisé. ====Exemple de récupération d'une propriété d'une autre class==== Extrait de [[smartcitizen|Kivy: Application Android Smart Citizen]] {{ media_05:kivy_text_explication.png?800 |}} Cet exemple pourrait être un excercice dans une interro surprise! On applique bêtement le cours ! "Mais, M'sieur, j'ai pas compris, j'ai pas compris, .... !" * app: Ce mot clé se réferre toujours à l'instance de votre application, soit SmartCitizenApp * root: Ce mot clé n'est disponible que dans la définition des règles, et représente le widget racine de la règle, soit SmartCitizen * ids: [[https://kivy.org/doc/stable/api-kivy.uix.widget.html#kivy.uix.widget.Widget.ids|appel d'un widget par son id]], ici l'id est sm, soit le screen manager * get_screen('first') est le premier écran Screen1 * owner_detail: est un attribut de class de Screen1, défini dans cette class par self.owner_detail ===== Lien entre valeurs et propriétés ===== Lorsque vous utilisez le langage Kivy, vous aurez remarqué que nous effectuons un certains travail en tâche de fond pour faire que les choses se déroulent correctement. Vous devriez savoir que les propriétés implémentent le logiciel observateur de modèle conception: cela veut dire que vous pouvez lier vos propres fonctions qui seront appelée quand la valeur d'une propriété changera. Le langage Kivy détecte les propriétés dans votre expression value et créé des callbacks pour mettre à jour automatiquement la propriété en utilisant votre expression lorsqu'un changement intervient. Voici un exemple qui souligne ce comportement: Button: text: str(self.state) Dans cet exemple, le parser détecte que self.state est une valeur dynamique (une propriété). La propriété state du bouton peut changer à tout moment lorsque l'utilisateur le touche. Nous voulons à présent que le bouton affiche son propre état sous forme de texte, même si celui-ci change. Pour cela, nous utilisons la propriété state du bouton dans l'expression valeur de la propriété text du bouton lui-même, qui contrôle ce qui est affiché sur le bouton. A présent, lorsque l'état du bouton change la propriété text est automatiquement mise à jour. Rappelez-vous: La valeur est une expression python! On peut donc faire des choses très intéressantes comme: Button: text: 'Plop the world' if self.state == 'normal' else 'Release me!' Le texte du bouton change en fonction de l'état de celui-ci. Par défaut, le texte sera 'Plop the world', mais lorque le bouton est appuyé, le texte sera alor 'Release me!'. ===== Instructions graphiques ===== ==== Classes dynamiques ==== Les classes dynamiques vous permettent de créer de nouveaux widgets à la volée, sans aucune déclaration python au préallable. La syntaxe des classes dynamiques est semblable à celle des règles, mais vous devez spécifier la ou les classes de base. La syntaxe ressemble à cela: # Simple inheritance : ... # Multiple inheritance : ... Le caractère @ est utilisé pour le nom de la classe dont on hérite. En python, cela donnerait: # Simple inheritance class NewWidget(Button): pass # Multiple inheritance class NewWidget(Label, ButtonBehavior): pass Toute nouvelle propriété ajoutée au code python doit d'abord être déclarée. Si la propriété n'existe pas dans la classe dynamique, celle-ci sera automatiquement créée en tant que ObjectProperty. Voyons cela en implémentant un bouton Image. On peut dériver nos classe à partir d'une classe Button, nous devons simplement ajouter une propriété pour le nom du fichier image: : source: None Image: source: root.source pos: root.pos size: root.size # let's use the new classes in another rule: : BoxLayout: ImageButton: source: 'hello.png' on_press: root.do_something() ImageButton: source: 'world.png' on_press: root.do_something_else() En python, on peut créer une instance de classe dynamique comme ceci: from kivy.factory import Factory button_inst = Factory.ImageButton() ===== Redéfinir le style d'un widget ===== Nous aimerions quelques fois hériter d'un widget de manière à utiliser ses propriétés python sans pour cela utiliser le style défini dans le fichier .kv. Par exemple, nous voudrions hériter d'un label, mais avec nos propres instructions pour le canvas. On peut réaliser cela en mettant un tiret '-' davant le nom de la classe dans le fichier .kv. Dans myapp.py: class MyWidget(Label): pass Dans my.kv: <-MyWidget>: canvas: Color: rgb: 1, 1, 1 Rectangle: size: (32, 32) MyWidget aura a présent ses propres instructions dans son canvas. ===== Les directives de Lang ===== Vous pouvez utiliser des directives pour contrôler les fichiers du langage (lang). Une directive doit commencer avec un caractère #: #: import ou #:import #:import os os : Button: text: os.getcwd() #:import ut kivy.utils : canvas: Color: rgba: ut.get_random_color() Vous pouvez directement importer une classe depuis un module: #: import Animation kivy.animation.Animation : on_prop: Animation(x=.5).start(self) set #:set Positionner une clé qui sera disponible n'importe où dans le fichier kv. Par exemple: #:set my_color (.4, .3, .4) #:set my_color_hl (.5, .4, .5) : state: 'normal' canvas: Color: rgb: my_color if self.state == 'normal' else my_color_hl =====Définir l'héritage d'un widget===== from kivy.app import App from kivy.lang import Builder kv = ''' : step: 1 Label: text: '{}'.format(int(root.value)) size: self.texture_size top: root.center_y - sp(20) center_x: root.value_pos[0] ''' class ShortenText(App): def build(self): return Builder.load_string(kv) ShortenText().run() équivaut à from kivy.app import App from kivy.lang import Builder from kivy.uix.slider import Slider kv = ''' : step: 1 Label: text: '{}'.format(int(root.value)) size: self.texture_size top: root.center_y - sp(20) center_x: root.value_pos[0] ''' class LabeledSlider(Slider): pass class ShortenText(App): def build(self): return Builder.load_string(kv) ShortenText().run() {{tag> kivy python sb }}