Comme je le sais très bien, cette question a déjà été posée plusieurs fois. Mais après avoir essayé les solutions suivantes:

Je suis arrivé à la conclusion que j'ai besoin d'aide pour mon problème spécifique. Les solutions énumérées ne semblent pas fonctionner dans mon cas spécifique.

Situation suivante:

J'essaie actuellement de développer une application pour smartphones utilisant kivy. Puisque j'aime mon code assez clair et structuré, j'ai divisé mon code Kivy en plusieurs fichiers kv. Le code python est censé avoir principalement la logique et rien de plus. Afin de le faire fonctionner correctement, j'ai besoin de référencer les instances des différents objets dans les différents fichiers kv. Afin de clarifier mon problème, j'ai construit un exemple assez simple:

FICHIER: try.py

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.factory import Factory
from kivy.uix.label import Label
from kivy.lang import Builder

x= 1

class ComplexBox(Widget):
    def testit(self):
        self.ids.layout.add_widget(Label(text = "Requirement A met."))
    def addsome(self):
        global x
        self.ids.layout.add_widget(SomeWidget(id="XXX"+str(x)))
        x = x +1
    pass

class SomeWidget(Widget):
    def change(self):
        self.ids.REQB.text = "Requirement B met."
    pass

class RequirementC(Widget):
    def triggerC(self):
        self.ids.ERRORBUTTON.text = "Requirement C met"
    pass

class Attempt(App):
    def build(self):
        return presentation
    pass


presentation = Builder.load_file("attempt.kv")
Attempt().run()

FICHIER: tentative.kv

#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv

# root
<ComplexBox>:
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
BoxLayout:
    orientation: 'vertical'
    ComplexBox:
    RequirementC:

FICHIER: tentativesupp.kv

#:kivy 1.0

# rules for the widget
<SomeWidget>:
    BoxLayout:
        pos: root.pos
        size: root.size
        orientation: "vertical"
        Label:
            id: REQB
            text: "hello"
        Button:
            text: "world"
            on_release: root.change()

FICHIER: tentativesuppC.kv

#:kivy 1.0

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.triggerC()

image du programme en cours - appuyez sur le bouton "Press" - pour obtenir l'erreur

Fonctionnant avec kivy version 1.10 et Python version 3.7.2, le programme démarre d'abord parfaitement. Mais lorsque j'appuie sur le bouton intitulé "appuyez" avec l'ID ERRORBUTTON, j'obtiens cette erreur:

...--default --nodebug --client --host localhost --port 57777...\attempt.py "
[INFO   ] [Logger      ] Record log in...\.kivy\logs\kivy_19-03-15_31.txt
[INFO   ] [Kivy        ] v1.10.1
[INFO   ] [Python      ] v3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 
...
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[WARNING] [Lang        ] attemptsupp.kv has already been included!
[WARNING] [Lang        ] attemptsuppC.kv has already been included!
[INFO   ] [Base        ] Start application main loop
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "kivy\properties.pyx", line 838, in kivy.properties.ObservableDict.__getattr__
 KeyError: 'ERRORBUTTON'

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "...\ptvsd_launcher.py", line 45, in <module>
     main(ptvsdArgs)
   ...
   File "e:\Daten\Github_Projects\pc-clicker\attempt.py", line 35, in <module>
     Attempt().run()
   File "...\lib\site-packages\kivy\app.py", line 826, in run
     runTouchApp()
...
   File ...\lib\site-packages\kivy\lang\builder.py", line 64, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File ...\attemptsuppC.kv", line 7, in <module>
     on_release: root.triggerC()
   File "...\attempt.py", line 25, in triggerC
     self.ids.ERRORBUTTON.text = "Requirement C met"
   File "kivy\properties.pyx", line 841, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__'

Même si j'ai raccourci le message d'erreur, il devrait être clair ce qui se passe. L'ID ERRORBUTTON auquel je fais référence dans la classe RequirementC est introuvable dans le dictionnaire. Maintenant à ma question:

Comment puis-je le faire fonctionner? Que suis-je manquant?

Voici en bref quelques choses que j'ai essayées:

  • J'ai essayé d'encapsuler les BoxLayouts dans Screen et d'y accéder via le screenmanager.
  • J'ai essayé de réorganiser l'ordre dans le code python. (par exemple le chargement du fichier kv principal en premier)
  • J'ai essayé d'utiliser Builder Factory et d'y enregistrer les différentes classes.
  • J'ai essayé de changer les références. (Par exemple self.ids. ['ERRORBUTTON'] ...)

Aucune de ces tentatives ne semble avoir fonctionné dans mon cas.

Donc, pour résumer le tout:

Comment puis-je faire fonctionner correctement mes références kivy dans différentes classes et pourquoi l'ID du BOUTON D'ERREUR n'est-il pas dans le dict que je regarde?

3
Franz Dahmann 16 mars 2019 à 02:27

2 réponses

Meilleure réponse

Donc, après un peu de recherche, j'ai trouvé la réponse que je cherchais. Pour ceux qui pourraient également trébucher sur mon problème ici, il vient:

class RequirementC(Widget):
def triggerC(self):
    presentation.children[1].ids.ERRORBUTTON.text = "Requirement C met"
pass

En parcourant les enfants de la présentation, il est possible d'utiliser la méthode "ids".

Mais pour ceux qui sont tentés de l'utiliser maintenant, je recommanderais de parcourir les enfants pour trouver l'id correct au lieu de donner une référence "dure" (children [1]).

0
Franz Dahmann 19 mars 2019 à 00:04

Le problème est dû à une erreur courante, les identifiants sont relatifs à un widget, par exemple dans votre cas analysons l'expression:

self.ids.ERRORBUTTON

Qui est soi? soi est l'instance de RequirementC.

quelle instance avez-vous? alors voyons le .kv où RequirementC est implémenté:

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.triggerC()

Si vous remarquez que le seul identifiant ayant accès est REQC, l'ID ERRORBUTTON n'existe donc pas pour RequirementC.

À quelle classe appartient l'ID ERRORBUTTON? Voyons donc où ERRORBUTTON a été créé:

 # ...

<ComplexBox>:
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
 # ...

Comme vous pouvez le voir, ERRORBUTTON est un identifiant de ComplexBox.


Avec ce qui a été mentionné dans la partie précédente, nous connaissons déjà la cause du problème. Avant de donner une solution, nous comprenons d'abord un principe de base de la programmation: Une classe est une abstraction d'un comportement, elle doit clairement définir que vous voulez exposer à l'extérieur (donc si vous vérifiez la documentation d'une bibliothèque, ne documentez pas toutes les méthodes ou toutes les classes puisque l'idée est d'abstraire la classe, c'est-à-dire que celui qui utilise cette bibliothèque ne veut pas savoir comment elle fonctionne en interne avec une telle précision), il est donc bon de concevoir en pensant aux méthodes que les classes auront . Par exemple, disons que nous créons une classe Personne, cette classe a certains attributs tels que la taille ou le poids, que se passe-t-il si vous pensez qu'il est nécessaire d'exposer combien votre cœur ou votre cerveau pèse? Et bien non. La même chose se produit dans votre cas.

La solution consiste à exposer l'événement on_release afin qu'il fasse partie de la classe RequirementC, en plus d'exposer ERRORBUTTON en tant que propriété (dans mon cas, je n'aime pas utiliser les identifiants car ils rendent le code moins lisible), puis rendre le connexion dans un lieu de portée commune.

* .py

# ...

class RequirementC(Widget):
    def __init__(self, **kwargs):
        self.register_event_type('on_release')
        super().__init__(**kwargs)

    def on_release(self):
        pass

# ...

tentative.kv

#:kivy 1.0
#:include attemptsupp.kv
#:include attemptsuppC.kv

# root
<ComplexBox>:
    error_button: ERRORBUTTON # <---
    BoxLayout:
        id: layout
        size: root.size
        Button:
            id: ERRORBUTTON
            text: "add"
            on_press: root.addsome()
            on_release: root.testit()
BoxLayout:
    orientation: 'vertical'
    ComplexBox:
        id: complex_box
    RequirementC:
        on_release: complex_box.error_button.text = "Requirement C met"

tentativesuppC.kv

#:kivy 1.0

<RequirementC>:
    Button:
        id: REQC
        text: "Press"
        on_release: root.dispatch('on_release')
1
eyllanesc 15 mars 2019 à 23:59