Outils pour utilisateurs

Outils du site


kivy_les_fichiers_kv

Kivy: Les fichiers kv

Le langage kivy dans les fichiers *.kv

Ressources sur les fichiers kv

Nommage pour le fichier kv

  • 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('''
<Withoutkv>:
    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

<Withkvtest>:
    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.
<Rule1,Rule2>:
    # .. definitions ..
 
<Rule3>:
    # .. 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.
<ClassName>:
    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:

<MainScreen>:
    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.

<Widget>:
    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é:

<Widget>:
    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 Kivy: Application Android Smart Citizen 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: 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
<NewWidget@Button>:
    ...
 
# Multiple inheritance
<NewWidget@Label,ButtonBehavior>:
    ...

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:

<ImageButton@Button>:
    source: None
 
    Image:
        source: root.source
        pos: root.pos
        size: root.size
 
# let's use the new classes in another rule:
<MainUI>:
    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 #:

#:<directivename> <options>
import <package>
 
ou
 
#:import <alias> <package>
#:import os os
 
<Rule>:
    Button:
        text: os.getcwd()
 
#:import ut kivy.utils
 
<Rule>:
    canvas:
        Color:
            rgba: ut.get_random_color()

Vous pouvez directement importer une classe depuis un module:

#: import Animation kivy.animation.Animation
<Rule>:
    on_prop: Animation(x=.5).start(self)
 
 set <key> <expression>
 
#:set <key> <expr>

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)
 
<Rule>:
    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 = '''
<LabeledSlider@Slider>:
    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 = '''
<LabeledSlider>:
    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()
kivy_les_fichiers_kv.txt · Dernière modification : 2020/10/29 13:50 de serge