IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Tkinter Python Discussion :

Orienté objet : conteneurs [Python 3.X]


Sujet :

Tkinter Python

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut Orienté objet : conteneurs
    Lecteurs de ce message, je vous salue.

    Nous développons en équipe un programme avec une interface graphique Tkinter. Cette interface contient des classes de conteneurs (3 canvas et 2 frames) dont un est utilisé par un retour vidéo grâce à la librairie OpenCV. Ils sont tous créés à partir d'une classe de base, Fenetre, où est créée la fenêtre comportant tous les conteneurs. Plusieurs fois nous appelons un conteneur depuis un autre. Le but recherché est de mettre à jour les éléments de certains conteneurs (variables, labels, etc) depuis d'autres conteneurs.

    En langage Java par exemple, on peut faire un ConteneurX.getInstance(), en python pas explicitement. Pour cela on utilise des threads ou des appels d'instances bricolés :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    def getInstance(cls):
        instance = None
        def singleton(*args):
            nonlocal instance
            if not instance:
                instance = cls(*args)
            return instance
        return singleton
     
    @getInstance
    class ConteneurA(Conteneur):
        ...blabla...
    ou alors
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class ConteneurB(Conteneur, Thread):
        def __init__(self, argument):
            Thread.__init__(self)
        ...blabla...
    Est-ce une bonne approche ?

    Merci de vos futures réponses.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 396
    Points : 36 954
    Points
    36 954
    Par défaut
    Salut,

    Citation Envoyé par Leododo Voir le message
    En langage Java par exemple, on peut faire un ConteneurX.getInstance(), en python pas explicitement. Pour cela on utilise des threads ou des appels d'instances bricolés :
    Ce que vous désignez par "appels d'instances bricolés" est juste la construction d'un singleton.... soit mais je ne comprends pas pourquoi mettre çà sur le même plan que des threads. Quel est le rapport?

    Citation Envoyé par Leododo Voir le message
    Ils sont tous créés à partir d'une classe de base, Fenetre, où est créée la fenêtre comportant tous les conteneurs. Plusieurs fois nous appelons un conteneur depuis un autre
    Si vous utilisez tkinter, vous avez déjà tout çà prêt à l'emploi.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    >>> import tkinter as tk
    >>> frame = tk.Frame()[/QUOTE]
    note: j'aurais pu utiliser Toplevel(), c'est pareil.
    Puis je crée une classe qui hérite d'un Canvas:
    >>> class A(tk.Canvas):
    ...      pass
    ...
    >>>
    et une instance de cette classe qui aura le rôle "foo" mon IHM.
    tkinter me permet de récupérer l'instance via:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    >>> frame.children['foo']
    <__main__.A object at 0x0000000002F312B0>
    pas besoin de singleton (ni de connaître le nom de la classe pour faire un .getInstance dessus).

    Je ne dis pas que c'est préférable, juste que vous avez des objets qui remplissent des rôles et que si l'objet qui remplit le roleA doit récupérer l'objet qui remplit le roleC, il y a plein de façon de fabriquer une registry qui associe à une chaîne de caractères (le rôle), l'instance qui fera le boulot.
    Le singleton de la POO est une façon de résoudre le problème, utiliser ce qu'apporte déjà tkinter, fabriquer une registry ad hoc (c'est juste un dictionnaire), ...

    - W

  3. #3
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut
    Merci bien pour ta réponse,

    soit mais je ne comprends pas pourquoi mettre çà sur le même plan que des threads. Quel est le rapport?
    Le conteneur contenant le retour vidéo doit s’exécuter en même temps qu'un autre conteneur qui lui est chargé d'afficher des données toutes les secondes. Mais peut-être qu'avec ce que tu viens de proposer, on pourrait s'en passer.

    Demain je test ce que tu m'a fait découvrir et je fais parvenir mes résultats

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 396
    Points : 36 954
    Points
    36 954
    Par défaut
    Salut,

    Citation Envoyé par Leododo Voir le message
    Le conteneur contenant le retour vidéo doit s’exécuter en même temps qu'un autre conteneur qui lui est chargé d'afficher des données toutes les secondes.
    Dans ce cas, il est préférable(*) d'avoir une fonction/callable pour lire les vidéos qui poste le résultat dans une Queue. Et pour afficher les images, utiliser .after pour vider la Queue toutes les x secondes.
    (*) la première raison est que le support des threads par tkinter est un peu cafouilleux: çà a été écrit pour fonctionner mais il y a de nombreux rapports de bugs qui étant difficilement reproductibles n'ont pu être corrigé. In fine, si çà fonctionne, vous êtes content, et si çà cafouille, la solution sera de séparer threads et mises à jours du GUI.
    La deuxième raison est que sous classer Thread, c'est surcharger la méthode "run":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class ActivitéAsynchrone(Thread):
     
           def run(....)
                ...
    Et s'empêcher de tester ce que çà fait en dehors de ce fonctionnement asynchrone. Il est préférable d'écrire une fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def do_activité_asynchrone(....):
         ...
    Puis de l'associer à un thread via:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    p = Thread(target=do_activité_asynchrone, args=(...),...)
    p.start()
    note: et s'il y a besoin d'une "class", c'est pas interdit: on instancie l'instance et on passe la méthode qui doit être appelée lors du "run" en target.

    - W

  5. #5
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut
    Alors j'ai appliqué les réponses de tes deux messages.
    J'ai donc un script "main" qui lance une classe Fenetre, à l’intérieur duquel j'instancie des conteneurs.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    import tkinter as tk
     
    from fr.sandbox.vue.ConteneurC import ConteneurC
    from fr.sandbox.vue.ConteneurF import ConteneurF
    from fr.sandbox.vue.ConteneurT import ConteneurT
     
     
    class Fenetre:
     
        def __init__(self):
     
            self.fenetre = tk.Tk()
            self.fenetre.geometry('300x300+50+50')
     
            self.conteneurC = ConteneurC(self.fenetre, name='fooC')
            self.conteneurF = ConteneurF(self.fenetre, name='fooF')
            self.conteneurT = ConteneurT(self.fenetre, name='fooT')
     
            print(self.fenetre.children['fooC'])
     
            self.fenetre.mainloop()
    Je ne met qu'un des 3 conteneurs, celui auquel j'ai ajouté le thread.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    import tkinter as tk
    import threading as th
     
     
    class ConteneurT(tk.Canvas):
     
        def __init__(self, fenetre, name):
            super().__init__(width=100, height=100, bg='blue', highlightthickness=0)
            super().place(x=10, y=120)
            p = th.Thread(target=ConteneurT.fonction_asynchrone(self), args=())
            p.start()
     
        def fonction_asynchrone(self):
            print("ici")
    Avant d'appliquer ce que tu m'a dis avec OpenCv, une queue et .after dans la fonction fonction_asynchrone, je me penche sur la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print(self.fenetre.children['fooC'])
    qui ne fonctionne pas mais je pense savoir pourquoi. Le rôle dont tu me parlais, il semble qu'il passe comme un argument car j'ai dû mal comprendre son fonctionnement. En attendant, je continue de chercher avec notamment le registry dont tu parlais.

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 396
    Points : 36 954
    Points
    36 954
    Par défaut
    Salut,

    Citation Envoyé par Leododo Voir le message
    je me penche sur la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print(self.fenetre.children['fooC'])
    qui ne fonctionne pas mais je pense savoir pourquoi. Le rôle dont tu me parlais, il semble qu'il passe comme un argument car j'ai dû mal comprendre son fonctionnement.
    Si vous écrivez: ConteneurC(self.fenetre, name='fooC') pour que le "name" se retrouve être celui du Canvas, il faut passer cet argument à l'__init__ du Canvas et dans votre code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class ConteneurT(tk.Canvas):
     
        def __init__(self, fenetre, name):
            super().__init__(width=100, height=100, bg='blue', highlightthickness=0)
    c'est super().__init__(...) qui le fait (et qui doit récupérer le paramètre).

    Autre chose:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        p = th.Thread(target=ConteneurT.fonction_asynchrone(self)
    appelle la fonction et passe son résultat en guise de target.
    Pour que çà marche, il faudrait écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        p = th.Thread(target=ConteneurT.fonction_asynchrone, args=(self,)
    mais écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        p = th.Thread(target=self.fonction_asynchrone
    serait meilleur.

    - W

  7. #7
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut
    Alors sois je suis c** soit j'en sais rien.

    Si vous écrivez: ConteneurC(self.fenetre, name='fooC') pour que le "name" se retrouve être celui du Canvas, il faut passer cet argument à l'__init__ du Canvas et dans votre code
    c'est super().__init__(...) qui le fait (et qui doit récupérer le paramètre).
    Je l'ai mis de toutes les manières possible et j'ai à chaque fois récupéré une erreur, soit je mets trop d'argument, soit pas assez, soit il peut pas prendre mon type str en compte, blabla

    Pourtant ça a pas l'air compliqué, quand j'ouvre la librairie c'est marqué !!!

    A un moment donné quand j'ouvre le fichier __init__.py j'ai des lignes au niveau de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class BaseWidget(Misc):
    qui m'indiquent que je suis pas loin :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
        def _setup(self, master, cnf):
            """Internal function. Sets up information about children."""
            if _support_default_root:
                global _default_root
                if not master:
                    if not _default_root:
                        _default_root = Tk()
                    master = _default_root
            self.master = master
            self.tk = master.tk
            name = None
            if 'name' in cnf:
                name = cnf['name']
                del cnf['name']
            if not name:
                name = self.__class__.__name__.lower()
    ou encore

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        def __init__(self, master, widgetName, cnf={}, kw={}, extra=()):
            """Construct a widget with the parent widget MASTER, a name WIDGETNAME
            and appropriate options."""
            if kw:
                cnf = _cnfmerge((cnf, kw))
            self.widgetName = widgetName
    Enfin bref j'ai essayé comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class ConteneurC(tk.Canvas):
     
        def __init__(self, fenetre, name):
            super().__init__(self, fenetre, name, width=100, height=100, bg='blue', highlightthickness=0)
            super().place(x=10, y=10)
    => TypeError: __init__() takes from 1 to 3 positional arguments but 4 were given

    Ou encore
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class ConteneurC(tk.Canvas):
     
        def __init__(self, fenetre, name):
            super().__init__(self, name, width=100, height=100, bg='blue', highlightthickness=0)
            super().place(x=10, y=10)
    => ValueError: dictionary update sequence element #0 has length 1; 2 is required

    Enfin bref je vois bien que je fais de la m**** avec les arguments mais je résous pas du tout le truc.
    Et en plus je vois bien que ton truc fonctionne car si je fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print(self.fenetre.children)
    je récupère bien
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {'!conteneurc': <fr.sandbox.vue.ConteneurC.ConteneurC object .!conteneurc>}
    dans un dictionnaire ...

    Mais ça c'est si je garde le code comme cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class ConteneurC(tk.Canvas):
     
        def __init__(self, fenetre, name):
            super().__init__(width=100, height=100, bg='blue', highlightthickness=0)
            super().place(x=10, y=10)
    Or tu me dis bien que
    Si vous écrivez: ConteneurC(self.fenetre, name='fooC') pour que le "name" se retrouve être celui du Canvas, il faut passer cet argument à l'__init__ du Canvas et dans votre code:
    Donc voilà

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 396
    Points : 36 954
    Points
    36 954
    Par défaut
    Ben... c'est juste:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    super().__init__(name=name, width=100, height=100, bg='blue', highlightthickness=0)
    Il va falloir revoir un peu tout çà car, de fait, vous n'êtes pas très à l'aise avec...

    - W

  9. #9
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut
    Oui. Alors celui là aussi je l'avais essayé. Mais en fait j'avais fait le test avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
            self.conteneurC = ConteneurC(self.fenetre, name='fooC')
            #self.conteneurF = ConteneurF(self.fenetre, name='fooF')
            #self.conteneurT = ConteneurT(self.fenetre, name='fooT')
    en commentant deux conteneurs.
    Et je testais avec ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    print(self.fenetre.children['fooF'])
    soit un conteneur que j'avais mis en commentaire ... voilà voilà c'est la blague du jour.

  10. #10
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2015
    Messages
    67
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2015
    Messages : 67
    Points : 61
    Points
    61
    Par défaut
    Bref donc je peux bien récupérer mon conteneur à partir d'un autre conteneur (j'ai pris en compte ta remarque sur la fonction asynchrone)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    import tkinter as tk
    import threading as th
     
     
    class ConteneurT(tk.Canvas):
     
        fenetre = None
     
        def __init__(self, fenetre, name):
            super().__init__(name=name, width=100, height=100, bg='blue', highlightthickness=0)
            super().place(x=10, y=120)
            ConteneurT.fenetre = fenetre
            th.Thread(target=ConteneurT.fonction_asynchrone).start()
     
        @staticmethod
        def fonction_asynchrone():
            print(ConteneurT.fenetre.children['fooC'])
    Je vais maintenant intégrer Opencv dans le conteneur avec ce dont tu m'a parlé (Queue-.after)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Définitions de programmation impérative et orientée objet
    Par sjrd dans le forum Langages de programmation
    Réponses: 10
    Dernier message: 10/09/2005, 19h32
  2. Stack OverFlow ou Violation d'adresse - Orienté Objet
    Par JakeGrafton dans le forum Langage
    Réponses: 7
    Dernier message: 31/05/2005, 16h34
  3. [DEBUTANT] Conseil sur la programmation orienté objet
    Par etiennegaloup dans le forum Langage
    Réponses: 7
    Dernier message: 27/05/2005, 12h59
  4. Réponses: 2
    Dernier message: 01/05/2005, 14h43
  5. [SGBDOO] Base de données orientée objet
    Par Jaona dans le forum Décisions SGBD
    Réponses: 19
    Dernier message: 14/04/2003, 11h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo