logo Debian Debian Debian-France Debian-Facile Debian-fr.org Forum-Debian.fr Debian ? Communautés logo inclusivité

Debian-facile

Bienvenue sur Debian-Facile, site d'aide pour les nouveaux utilisateurs de Debian.

Vous n'êtes pas identifié(e).

#1 01-02-2022 17:08:06

tartare
Membre
Inscription : 18-11-2021

Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

Bonjour a vous,

je rencontre un souci (encore?) pr arriver à cibler 1 widget ou 1 fenetre depuis 1 emplacement exterieur a cette fenetre
j'explique et je montre.
3 fichiers .py

le 1° est le fichier principal, celui qui contient le mainloop()
voici ce qui d'après moi compte


import tkinter as tk
import fichier_Fenetre_Avec_Button_Personnalise

FP= tk.Tk()
FP.geometry("1000x700+300+0")
FP.minsize(410, 200)
FP.maxsize(1100, 700)
FP.title("FP - accueil")

## methode qui appelle le fichier fichier_Fenetre_Button_Personnalise
def creer_Fenetre_Button_Personnalise():
    F01K= fichier_Fenetre_Avec_Button_Personnalise.creer_Fenetre_Avec_Button_Personnalise(FP,
                                                                                "F01K: ",
                                                                                600,
                                                                                400)
 


bien, là, on voit que
-1 je n'utilise pas de classe pr définir ma FP (Fenetre Principale, celle qui contient le mainloop())
ça va changer
-2 je crée 1 instance d'une seconde fenetre grâce à la méthode et à l'importation de mon second fichier

le second fichier, celui qui crée ma seconde fenêtre


import tkinter as tk
import fichier_Btn_Retour_Personnalise

class creer_Fenetre_Avec_Button_Personnalise():
    ## déclarat° du constructeur
    def __init__(self, FP, titre, largeur, hauteur):
        ## déclarat°, initialisat° des attributs d'instance
        self.FP= FP
        self.titre= titre
        self.largeur= largeur
        self.hauteur= hauteur

        ## initialisat° de la fenetre
        self.F01K= tk.Toplevel(self.FP)
        self.F01K.title(self.titre)
        self.F01K.geometry(str(self.largeur) + "x" + str(self.hauteur))
        self.F01K.wm_attributes("-zoomed", 1)

        ## créat° d' 1 Frame
        self.cadre01= tk.Frame(self.F01K, width= 300, height= 500, padx= 5, pady= 5, bg= "blue", bd= 1)
        self.cadre01.grid(column= 0, row= 0, padx= 10, pady= 10, sticky= tk.N + tk.W)
        self.cadre01.grid_propagate(False)

        ## creer 1 instance de la classe Btn_retour définie ds son fichier de classe
        self.btn02_retour= fichier_Btn_Retour_Personnalise.Btn_retour(self.cadre01, "detruire Frame bleu", 5, 5, 1, 0, 10, 10, "nw")
 


bien, là on voit que
-1 j'utilise 1 classe pr construire ma fenetre ms qu'elle n' herite de rien
-2 derniere L: je crée 1 instance d'un bouton grâce au fichier que j'importe ET que ce bouton est crée DANS le Frame de la fenetre

le 3° fichier, celui qui crée un bouton personnalisé


import tkinter as tk

## Déclarat° de la classe
class Btn_retour():
    ## déclarat° du constructeur
    def __init__(self, oM, libelle, padx, pady, grid_column, grid_row, grid_padx, grid_pady, grid_sticky):

        ## déclarat°, initialisat° des attributs d'instance
        self.oM= oM
        self.libelle= libelle
        self.padx= padx,
        self.pady= pady,
        self.grid_column= grid_column
        self.grid_row= grid_row
        self.grid_padx= grid_padx
        self.grid_pady= grid_pady
        self.grid_sticky= grid_sticky

        ## initialisat° du widget
        self.btn_retour= tk.Button(self.oM, text= self.libelle, padx= self.padx, pady= self.pady, command= self.f1)
        self.btn_retour.grid(column= self.grid_column, row= self.grid_row, padx= self.grid_padx, pady= self.grid_pady, sticky= self.grid_sticky)

    def f1(self):
        ## print("bjour")
        ##self.destroy()
        ##self.destroy(self)
        self.oM.destroy() ## fonctionne ms detruit le Frame
        ##self.toplevel.destroy()
        ##toplevel.destroy()
        ##self.oM.toplevel.destroy()
 


bien là on voit que
-1 je crée 1 classe qui crée 1 bouton personnalisé (personnalisable) ms qui encore 1x n' hérite de rien
-2 lorsque je clic sur le bouton, celui-ci détruit le Frame qui le contient (Frame du second fichier et donc de la seconde fenetre)

hors je ne veux pas détruire le Frame de la seconde fenetre, je veux détruire la seconde fenetre.
Comment je fais cela?
Avec ce que j'ai écrit, je ne px pas cibler cette seconde fenetre

je vois 2 manières de faire.
- ecrire la fonction qui detruit la fenetre directement ds son propre fichier (le second dc) et voir si mon bouton personnalisé peut appeller cette méthode
- faire appel a qque chose que je ne connais pas encore et que je n'arrive pas a traduire en langage humain et dc que je n'utilise pas


class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
 


vu ici
docs.python.org/fr/3.8/library/tkinter.html
Ds ce dernier morceau de code
je vois bien que la classe Application hérite de la classe Frame
je vois bien qu'il y a 2 constructeurs, celui de la classe Application, celui de la classe .........super()? mère? la classe Frame?
qu'est ce que le master?
leur code est un fichier applicat° (avec le mainloop)
ma structure fait appel a différents fichiers, comment je dois l'adapter?
dc... si vs avez des pistes et/des explications

En vs remerciant
tartare


ce n'est pas à la machine de dire à l'homme ce qu'il peut faire, c'est a l'homme de dire a la machine ce qu'elle doit faire

Hors ligne

#2 01-02-2022 22:30:15

David5647
Membre
Distrib. : Debian Sid
Noyau : 5.15.0-2-amd64
(G)UI : i3wm + des bouts de kde
Inscription : 27-08-2017

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

Faut vraiment que tu puisses fournir un code fonctionnel, là ça va être difficile de t'apporter une aide.
Je vois bien que tu as fait l'effort de détailler, mais comme le code n'est pas complet, qu'il faudrait le debugger et que ce n'est pas très clair franchement, c'est compliqué.
Essaie de présenter un code fonctionnel le plus minimal possible, sans variables non nécessaires.

De ce que je peux déjà te dire/te rappeler:

espace de nom (namespace)
fondamentalement, que tes classes soient dans le même fichier ou dans des fichiers différents ça ne change rien.
Quand tu fais un import, ce que tu fais c'est que tu fusionnes des espaces de nom

De manière générale, évite d'employer des variables globales, essaie de confiner tes variables à tes classes. Et normalement tu n'auras pas de problème peu importe où se trouve ta classe.

tout est objet
Tu peux passer en paramètre d'une fonction un entier, une string aussi bien qu'une fonction, qu'une instance d'une classe... Tout quoi


class A:
    def __init__(self, func):
        self.fonction = func

def myfonction(text):
    print(text)

a = A(myfonction)
a.fonction("Du texte")
 



structure du programme
Tu peux organiser ton code ainsi:

class MainWindow(tk.Tk):
    pass

class Popup(tk.Toplevel):
    pass

class Bouton(tk.Button):
    pass

 



super()
Oui, la fonction super permet de faire appel à la classe héritée, et par conséquent de l'initialiser via sa méthode __init__().
Il faut employer cette ligne à chaque fois qu'on hérite d'une classe, je pense pas que ce soit utile de creuser son fonctionnement pour l'instant.

Qu'est ce que master?
Tu as tronqué l'exemple, master est le paramètre passé à l'initialisation de Application
Il est ensuite passé comme paramètre à l'initialisation de tk.Frame,
soit super().__init__(master) l'équivalent de tk.Frame(master)

Donc le paramètre master doit être un objet tkinter susceptible d'êrte parent d'un autre widget, si il n'est pas spécifié la fenêtre est indépendante, sinon, elle est  la fille de master

ps : si tu souhaites, il y a des forums plus spécifiques à python, je traîne parfois par là : https://www.developpez.net/forums/f96/a … es/python/. Essaie de suivre un max les conseils que je te donne en début de post, le forum y est plus rigoureux tongue

Dernière modification par David5647 (01-02-2022 22:35:04)

Hors ligne

#3 16-02-2022 11:31:21

tartare
Membre
Inscription : 18-11-2021

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

Bjour a vous tous, toutes,

@David5647,

Désolé de ne pas avoir répondu plus tôt.
J'ai eu qques obligations ces 2 dernières semaines
+
je (re)decouvre la prog, je vois plein de trucs nveaux et cô je suis du genre a courir plusieurs lapins a la x, je me perds rapidement.
+
il a fallu que je ré ecrive tt ce que j'avais deja fait (pr 1 code + propre)

bref, tt ça pr te dire que j'ai pas creusé la chose

j'ai fait un code + minimal

je poste 2 fichiers, le fichier principal, celui qui a la mainloop() n'est pas interessant

le fichier qui importe le fichier du bouton personnalisé c'est a dire
fichier_F01R_copie.py
qui importe
fichier_Btn_Retour_Personnalise_copie.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-


import tkinter as tk
import fichier_Btn_Retour_Personnalise_copie

## Déclarat° de la classe
class classe_F01R(tk.Toplevel):

    def __init__(self, parent, titre):

        super().__init__()  ## si la classe herite de tk.Tk
                            ## (2L au-dessus)
                            ## ce constructeur de super()
                            ## permet la creat° de la fenetre
                            ## ms celle-ci a 1 vie propre

        ## appairage des attributs d'instance
        ## et
        ## des parametres du constructeur
        self.parent= parent
        self.titre= titre

        ## iconifie la fenetre parente c-a-d FP
        self.parent.withdraw()

        ## definit° des attributs d'instance
        self.title(self.titre)

        ## permet le plein ecran de la fenetre
        self.width= self.winfo_screenwidth()
        self.height= self.winfo_screenheight()
        self.geometry(str(self.width) + "x" + str(self.height))

        ## appel de la méthode pr initialiser les widgets de la fenetre
        ## l'invocat° de la méthode doit-etre ecrite à la tte fin du
        ## constructeur.
        ## si on invoque la méthode qui initialise les widgets (et dc les
        ## variables qui y st rattachées (comme les variables de ctrl)
        ## AVT la déclarat° de ces variables, alors le programme sort
        ## 1 AttributeError
        self.creer_Widgets()

    ## methode
    def on_close(self):

        self.destroy()
        self.parent.deiconify()

    ## methode qui initialise les widgets de la fenetre
    def creer_Widgets(self):

        ## créat° d'au moins 1 widget invisible de type Frame
        self.cadre01= tk.Frame(self,
                               width= 300,
                               height= 500,
                               padx= 5,
                               pady= 5,
                               bg= "blue",
                               bd= 1)

        self.cadre01.grid(column= 0,
                          row= 0,
                          padx= 5,
                          pady= 5,
                          sticky= "nw")

        self.cadre01.grid_propagate(False)

        ## 1° L du Frame
        ## widget Btn + son positionnement
        ## appel de la méthode on_close() au clic btn
        ## bouton retour: ferme et fait apparaitre FP
        self.btn01_retour= tk.Button(self.cadre01,
                                     text= "retour",
                                     padx= 5,
                                     pady= 5,
                                     command= self.on_close)

        self.btn01_retour.grid(column= 0,
                               row= 0,
                               padx= 5,
                               pady= 5,
                               sticky= "nw")

        ## creer 1 instance de la classe Btn_retour définie ds
        ## le fichier de classe
        ## /home/laurent/Documents/python/test14/fichier_Btn_Retour_Personnalise.py
        ## les arguments sont définis ds le fichier
        ## les arguments propres au bouton st:
        ## son conteneur, son libellé, son padx, son pady
        ## les arguments propres au grid() st:
        ## sa colonne, sa ligne, son padx, son pady, son sticky

        ## instance située ds la fenetre secondaire, ds le fr bleu
        self.btn02_retour= fichier_Btn_Retour_Personnalise_copie.Btn_retour(self.cadre01,
                                                                            "detruire Fr bleu",
                                                                            5,
                                                                            5,
                                                                            1,
                                                                            0,
                                                                            5,
                                                                            5,
                                                                            "nw")

        ## 1 2° Frame a droite du 1°
        self.cadre02= tk.Frame(self,
                               width= 500,
                               height= 500,
                               padx= 5,
                               pady= 5,
                               bg= "ivory",
                               bd= 1)

        self.cadre02.grid(column= 1,
                          row= 0,
                          padx= 5,
                          pady= 5,
                          sticky= "nw")
 



le fichier du bouton personnalisé importé c'est a dire
fichier_Btn_Retour_Personnalise_copie.py
importé ds le fichier
fichier_F01R_copie.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-


import tkinter as tk

## Déclarat° de la classe
class Btn_retour():

    ## déclarat° du constructeur
    def __init__(self,
                 oM,
                 libelle,
                 padx,
                 pady,
                 grid_column,
                 grid_row,
                 grid_padx,
                 grid_pady,
                 grid_sticky):
       
        ## déclarat°, initialisat° des attributs d'instance
        ## correspondance avec les parametres du constructeur
        ## les attributs d'instance st propres a chaque instance
        ## ce st des attributs stockant les parametres reçus

        self.oM= oM ## indique ou va se situer l'instance du bouton
        self.libelle= libelle
        self.padx= padx,
        self.pady= pady,
        self.grid_column= grid_column
        self.grid_row= grid_row
        self.grid_padx= grid_padx
        self.grid_pady= grid_pady
        self.grid_sticky= grid_sticky

        ## initialisat° du widget
       
        self.btn_retour= tk.Button(self.oM,
                                   text= self.libelle,
                                   padx= self.padx,
                                   pady= self.pady,
                                   command= self.f1)
   
        self.btn_retour.grid(column= self.grid_column,
                             row= self.grid_row,
                             padx= self.grid_padx,
                             pady= self.grid_pady,
                             sticky= self.grid_sticky)

    ## méthodes de classe

    def f1(self):

        ## print("bjour")
        ##self.destroy()    ## AttributeError, le btn n'a pas
                            ## d'attribut destroy()

        ##self.destroy(self)    ## AttributeError, le btn n'a pas
                                ## d'attribut destroy()

        self.oM.destroy() ## fonctionne ms detruit le Frame
       
        ##self.toplevel.destroy()   ## AttributeError, le btn n'a pas
                                    ## d'attribut toplevel

        ##toplevel.destroy()    ## NameError, le nom toplevel
                                ## n'est pas defini

        ##self.oM.toplevel.destroy()    ## AttributeError, Frame n'a pas
                                        ## d'attribut toplevel

        ##self.parent.destroy()   ## AttributeError, le btn n'a pas
                                  ## d'attribut parent
 



dc, pr en revenir au sujet de ce post,
ds le 1° fichier, j'instancie la classe definie de mon second fichier
c'est a dire
je crée un bouton personnalisé (dt le libellé est: "detruire fr bleu")

ce bouton détruit bien le frame bleu,
hors ce que je souhaite et n'arrive pas a faire c'est détruire la fenetre et faire apparaitre la fenetre principale (celle qui a le mainloop())
j'ai dc 3 niveaux
le fichier de la fenetre principale (mainloop())
---> le fichier de la fenetre secondaire (avec le fr bleu et a l'interieur de ce fr bleu mon bouton_personnalisé)
-------> le fichier de mon bouton personnalisé

voilà,
sinon pr tes remarques:
- forums specifiques: j'y suis aussi ms je me suis fait rembarré (sans doute est-ce ma faute ms comme j'avais pris le temps d'ecrire un code clair et commenté, je l'ai mal pris, j'y retrounerai sans aucun doute car je me rends bien compte qu'ici le sujet du site n'est ni python, ni la prog.
D'ailleurs apres ce post, je pense arreter de poster de tels sujets ici

- tu as remarqué que j'avais pris le temps de détailler, ça fait plaisir (deja dit ms mes efforts st passés inaperçus ou insuffisants ailleurs)

- variables globales, j'essayais justement de ne pas en utiliser, faut que je revoie ma copie sur le sujet(?!)

voili, voilou,
Merci bien pr le temps que tu passes à me lire et surtt a me filer un coup de main.
a+ et bonne journée
tartare


ce n'est pas à la machine de dire à l'homme ce qu'il peut faire, c'est a l'homme de dire a la machine ce qu'elle doit faire

Hors ligne

#4 16-02-2022 11:45:54

tartare
Membre
Inscription : 18-11-2021

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

re,

En fait
j'ai oublié de te mettre le fichier FP_copie.py, celui qui a le mainloop() pr que tu puisses
creer 3 fichiers
executer FP_copie.py qui lancera les 2 autres


#!/usr/bin/python3.9
# -*- coding: utf-8 -*-


import tkinter as tk
from sys import version_info
import fichier_F01R_copie


class FP(tk.Tk):    ## on cree notre applicat° en tant que classe
                    ## qui hérite de la classe tk.Tk

    ## constructeur
    def __init__(self):

        tk.Tk.__init__(self) ## on appelle le constructeur
                             ## de la classe mère pr instancier
                             ## la fenetre Tk ds la variable self.
                             ## super().__init__() est 1
                             ## constructeur identique

        ## appel de la méthode pr initialiser les widgets de la fenetre
        ## l'invocat° de la méthode doit-etre ecrite à la tte fin du
        ## constructeur.
        ## si on invoque la méthode qui initialise les widgets (et dc les
        ## variables qui y st rattachées (comme les variables de ctrl)
        ## AVT la déclarat° de ces variables, alors le programme sort
        ## 1 AttributeError
        self.creer_widgets()

    ## LES methodes

    ## les methodes qui instancient chacune des fenetres

    def creer_F01R(self):
        F01R= fichier_F01R_copie.classe_F01R(self, "F01R")

    ## méthode qui initialise les widgets de la fenetre
    def creer_widgets(self):
       
        ##-----------------------------------------------------------

        ## creat° d'1 1° Frame de fond rouge
        ## ds la fenetre (désignée par "self"
        self.fr01= tk.Frame(self,
                               width= 600,
                               height= 600,
                               padx= 5,
                               pady= 5,
                               bg= "red")

        self.fr01.grid(column= 0,
                          row= 0,
                          padx= 5,
                          pady= 5,
                          sticky= "nw")

        ##-----------------------------------------------------------

        ## creat° d'1 Canvas ds le Frame self.fr01
        ## les dimens° du contenu (ici le Canvas)
        ## prennent le pas sur les dimens° du conteneur
        ## (ici le Frame self.fr01), sauf si le conteneur
        ## dispose de la methode grid_propagate()
        self.fr01.cnv01= tk.Canvas(self.fr01,
                               scrollregion= (0, 0, 1000, 1000),
                               width= 350,
                               height= 500,
                               bd= 1,
                               highlightbackground= "black",
                               bg= "ivory")

        self.fr01.cnv01.grid(column= 0,
                             row= 0,
                             padx= 5,
                             pady= 5,
                             sticky= "nsew")

        ##-----------------------------------------------------------

        ## creat° d'1 Scrollbar verticale a cote (droite)
        ## du Canvas, les 2 widgets st au meme niveau
        ## ds self.fr01
        self.fr01.ysb01= tk.Scrollbar(self.fr01,
                                      orient= "vertical",
                                      command= self.fr01.cnv01.yview)

        self.fr01.ysb01.grid(column= 1,
                             row= 0,
                             padx= 5,
                             pady= 5,
                             sticky= "ns")
       
        ## appairage du Canvas (self.fr01_cnv01)
        ## avec
        ## la Scrollbar (self.fr01_ysb01)
        self.fr01.cnv01.config(yscrollcommand= self.fr01.ysb01.set)

        ##-----------------------------------------------------------
       
        ## creat° d'1 Frame qui sera placé ds le Canvas
        ## grâce à la méthode create_window au-dessous
        self.fr01.cnv01.fr01= tk.Frame(self.fr01.cnv01,
                                      width= 4000,
                                      height= 600,
                                      padx= 5,
                                      pady=5,
                                      bg= "green",
                                      bd= 1)

        ## methode create_window du Canvas
        ## methode qui reçoit la nvelle Frame (crée au-dessus)
        ## ds on attribut "window"
        ## les prochains widgets crées st à placer ds le Frame
        ## contenu ds le Canvas
        self.fr01.cnv01.win01= self.fr01.cnv01.create_window(
                                    5,
                                    5,
                                    anchor= "nw",
                                    window= self.fr01.cnv01.fr01)

        ##-----------------------------------------------------------
       
        ## créat° de boutons ds le Frame contenu ds le Canvas
        self.fr01.cnv01.fr01.btn01= tk.Button(self.fr01.cnv01.fr01,
                                               text= "Quitter",
                                               padx= 5,
                                               pady= 5,
                                               command=self.quit)

        self.fr01.cnv01.fr01.btn01.grid(column= 0,
                           row= 0,
                           padx= 5,
                           pady= 5,
                           sticky= "nw")

        ##-----------------------------------------------------------

        self.fr01.cnv01.fr01.btn19= tk.Button(self.fr01.cnv01.fr01,
                                              text= "F01R: 1 btn personnalisé",
                                              padx= 5,
                                              pady= 5,
                                              command= self.creer_F01R)

        self.fr01.cnv01.fr01.btn19.grid(column= 0,
                                        row= 18,
                                        padx= 5,
                                        pady= 5,
                                        sticky= "nw")

        self.fr01.cnv01.fr01.btn19A= tk.Button(
            self.fr01.cnv01.fr01,
            text= "infos",
            padx= 5,
            pady= 5,
            command= lambda: self.update_explication(self.liste_explication[0]))

        self.fr01.cnv01.fr01.btn19A.grid(column= 1,
                                        row= 18,
                                        padx= 5,
                                        pady= 5,
                                        sticky= "nw")

        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
       
        ## creat° d'1 3° Frame de fond ivoire
        ## ds la fenetre (désignée par "self")
        self.fr03= tk.Frame(self,
                               width= 600,
                               height= 600,
                               padx= 5,
                               pady= 5,
                               bg= "ivory")

        self.fr03.grid(column= 2,
                          row= 0,
                          padx= 5,
                          pady= 5,
                          sticky= "nw")

        ##-----------------------------------------------------------
       
        ## creat° d'1 Canvas ds le Frame self.fr03
        ## les dimens° du contenu (ici le Canvas)
        ## prennent le pas sur les dimens° du conteneur
        ## (ici le Frame self.fr03), sauf si le conteneur
        ## dispose de la methode grid_propagate()
        self.fr03.cnv01= tk.Canvas(self.fr03,
                               scrollregion= (0, 0, 1000, 1000),
                               width= 700,
                               height= 500,
                               bd= 1,
                               highlightbackground= "blue",
                               bg= "ivory")

        self.fr03.cnv01.grid(column= 0,
                             row= 0,
                             padx= 5,
                             pady= 5,
                             sticky= "nsew")

        ##-----------------------------------------------------------

        ## creat° d'1 Scrollbar verticale a cote (droite)
        ## du Canvas, les 2 widgets st au meme niveau
        ## ds self.fr03
        self.fr03.ysb01= tk.Scrollbar(self.fr03,
                                      orient= "vertical",
                                      command= self.fr03.cnv01.yview)

        self.fr03.ysb01.grid(column= 1,
                             row= 0,
                             padx= 5,
                             pady= 5,
                             sticky= "ns")
       
        ## appairage du Canvas (self.fr03_cnv01)
        ## avec
        ## la Scrollbar (self.fr03_ysb01)
        self.fr03.cnv01.config(yscrollcommand= self.fr03.ysb01.set)

        ##-----------------------------------------------------------
       
        ## creat° d'1 Frame qui sera placé ds le Canvas
        ## grâce à la méthode create_window au-dessous
        self.fr03.cnv01.fr01= tk.Frame(self.fr03.cnv01,
                                      width= 4000,
                                      height= 600,
                                      padx= 5,
                                      pady=5,
                                      bg= "ivory",
                                      bd= 1)

        ## methode create_window du Canvas
        ## methode qui reçoit la nvelle Frame (crée au-dessus)
        ## ds on attribut "window"
        ## les prochains widgets crées st à placer ds le Frame
        ## contenu ds le Canvas
        self.fr03.cnv01.win01= self.fr03.cnv01.create_window(
                                    5,
                                    5,
                                    anchor= "nw",
                                    window= self.fr03.cnv01.fr01)

        ##-----------------------------------------------------------

        ## creat° d'1 lbl qui explique le fonctionnement des
        ## differents fichiers
        self.fr03.cnv01.fr01.lbl_explication= tk.Label(self.fr03.cnv01.fr01,
                                        text= "comment fctionne la FP",
                                        padx= 5,
                                        pady= 5,
                                        justify= "left")

        self.fr03.cnv01.fr01.lbl_explication.grid(column= 0,
                                                  row= 0,
                                                  padx= 5,
                                                  pady= 5,
                                                  sticky= "nw")

        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
       
        ## creat° d'1 2° Frame directement ds la FP, juste au-dessous
        ## du 1° Frame (celui de couleur de fond rouge)
        self.fr02= tk.Frame(self,
                            width =200,
                            height= 200,
                            padx= 5,
                            pady= 5,
                            bg= "green",
                            bd= 1)

        self.fr02.grid(column= 0,
                       row= 1,
                       padx= 5,
                       pady= 5,
                       sticky= "nw")

        ##-----------------------------------------------------------
       
        ## créat° de boutons ds le 2° Frame contenu ds FP
        self.fr02.btn01= tk.Button(self.fr02,
                                               text= "Quitter",
                                               padx= 5,
                                               pady= 5,
                                               command=self.quit)

        self.fr02.btn01.grid(column= 0,
                           row= 0,
                           padx= 5,
                           pady= 5,
                           sticky= "nw")

        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
       
        self.liste_explication= [
            "F01R"]
        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
        ##-----------------------------------------------------------
       
        ## creer 1 instance de la classe Btn_retour définie ds
        ## le fichier de classe
        ## /home/laurent/Documents/python/test14/fichier_Btn_Retour_Personnalise.py
        ## son conteneur, la colonne, la ligne, le padx, le pady
        ## self.btn02_retour= fichier_Btn_Retour_Personnalise.Btn_retour(self.parent, "quitter le prog", 5, 5, 1, 0, 5, 5, "nw")

####################################################################
####################################################################

if __name__ == "__main__":
   
    ## creat° de la fenetre
    FP= FP()

    ## definit° des attributs de la fenetre

    ## titre de la barre de titre
    FP.title("Fenetre Principale (FP)")

    ## fenetre en plein ecran, enleve la barre de titre
    FP.wm_attributes("-fullscreen", "True")

    ##
    FP.mainloop()
 


ce n'est pas à la machine de dire à l'homme ce qu'il peut faire, c'est a l'homme de dire a la machine ce qu'elle doit faire

Hors ligne

#5 16-02-2022 19:42:50

David5647
Membre
Distrib. : Debian Sid
Noyau : 5.15.0-2-amd64
(G)UI : i3wm + des bouts de kde
Inscription : 27-08-2017

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

Bon, je vais difficilement pouvoir t'aider sur ce code, entre autre pour les raisons suivantes :

    - c'est encore trop brouillon
    - les noms de sont pas/trop peu explicites (classe_F01R, self.fr01.cnv01, self.cadre01, self.btn01_retour
    - trop de paramètres parasites inutiles (par exemples esthétiques pad_x, pad_y, bg...)
    - des objets (boutons) inutiles pour l'exemple (Scrollbar , frames, bouton info,)
    - des écritures peu compréhensibles (self.fr01.cnv01.fr01.btn01= tk.Button(self.fr01.cnv01.fr01,...)
    - des variables inutiles (créée mais pas utilisée (self.titre, self.width, self.height...))
    - des écritures confuses (self.btn01_retour est un bouton, self.btn02_retour n'est pas un bouton)
    - on peut ajouter : la surcharge de commentaires, la présentation du code...

Il faut que tu fournisses un exemple le plus simplifié possible qui illustre ton problème, là c'est difficile de t'aider dans ces conditions

ce bouton détruit bien le frame bleu,
hors ce que je souhaite et n'arrive pas a faire c'est détruire la fenetre et faire apparaitre la fenetre principale (celle qui a le mainloop())
j'ai dc 3 niveaux
le fichier de la fenetre principale (mainloop())
---> le fichier de la fenetre secondaire (avec le fr bleu et a l'interieur de ce fr bleu mon bouton_personnalisé)
-------> le fichier de mon bouton personnalisé


Il me semble que le code fourni (une fois les fichiers correctement nommés)  réalise cette tâche, à moins que je n'ai pas compris la demande...

variables globales, j'essayais justement de ne pas en utiliser, faut que je revoie ma copie sur le sujet(?!)


Il y en avait dans le premier code fournis (#1), là c'est bon de ce coté là, c'était surtout pour dire qu'il n'y avait pas de différence spécifique entre plusieurs fichier avec des import et un seul fichier

Dernière modification par David5647 (16-02-2022 19:45:15)

Hors ligne

#6 16-02-2022 22:51:34

tartare
Membre
Inscription : 18-11-2021

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

re,

bon je ne px pas alléger + car après ça n'a plus de sens
tjs 3 fichiers


#!/usr/bin/python3.9
# -*- coding: utf-8 -*-

import tkinter as tk
import fichier_F01R_copie

class FP(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.creer_widgets()

    def creer_F01R(self):
        F01R= fichier_F01R_copie.classe_F01R(self)

    def creer_widgets(self):
        self.btn19= tk.Button(self, text= "F01R: 1 btn personnalisé", command= self.creer_F01R)
        self.btn19.grid(column= 0, row= 18, padx= 5, pady= 5, sticky= "nw")

if __name__ == "__main__":
    FP= FP()
    FP.mainloop()
 




#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk
import fichier_Btn_Retour_Personnalise_copie

class classe_F01R(tk.Toplevel):
    def __init__(self, parent):
        super().__init__()
        self.parent= parent
        self.creer_Widgets()

    def on_close(self):
        self.destroy()

    def creer_Widgets(self):

        self.cadre01= tk.Frame(self, bg= "blue")
        self.cadre01.grid(column= 0, row= 0)
        self.btn01_retour= tk.Button(self.cadre01, text= "retour", command= self.on_close)
        self.btn01_retour.grid(column= 0, row= 0)

        ## instance du bouton située ds la fenetre secondaire, ds le fr bleu
        self.btn02_retour= fichier_Btn_Retour_Personnalise_copie.Btn_retour(self.cadre01, "detruire Fr bleu", 0, 1)
 




#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk

class Btn_retour():
    def __init__(self, parent, libelle, grid_column, grid_row):
        self.parent= parent
        self.libelle= libelle
        self.grid_column= grid_column
        self.grid_row= grid_row

        ## initialisat° du widget
       
        self.btn_retour= tk.Button(self.parent, text= self.libelle, command= self.f1)
        self.btn_retour.grid(column= self.grid_column, row= self.grid_row)

    def f1(self):
        ## print("bjour")
        ##self.destroy()    ## AttributeError, le btn n'a pas
                            ## d'attribut destroy()

        ##self.destroy(self)    ## AttributeError, le btn n'a pas
                                ## d'attribut destroy()

        self.parent.destroy() ## fonctionne ms detruit le Frame, ms je vx detruire la fenetre
       
        ##self.toplevel.destroy()   ## AttributeError, le btn n'a pas
                                    ## d'attribut toplevel

        ##toplevel.destroy()    ## NameError, le nom toplevel
                                ## n'est pas defini

        ##self.parent.toplevel.destroy()    ## AttributeError, Frame n'a pas
                                        ## d'attribut toplevel

        ##self.parent.destroy()   ## AttributeError, le btn n'a pas
                                  ## d'attribut parent


 



David5647:

des écritures peu compréhensibles (self.fr01.cnv01.fr01.btn01= tk.Button(self.fr01.cnv01.fr01,...)


ben c'est du ciblage
self= fenetre
fr01= Frame ds la fenetre
cnv01= Canvas ds le Frame
fr01= Frame ds le Canvas
btn01= Button ds le Frame

ca fait beaucoup de niveaux c'est vrai, ms a part tout poser sur la fenetre... je vois pas comment faire.
Tu fais comment toi?

David5647:

des écritures confuses (self.btn01_retour est un bouton, self.btn02_retour n'est pas un bouton)


si tu fais ref a la derniere L du 2° fichier ben... si c'est un bouton, c'est le bouton personnalisé, celui defini ds le 3° fichier

le bouton fait bien ce que je lui demande, detruire le Frame bleu (c'est pr cela qu'il est encore en vie celui-là), ms je vx lui dire de fermer la fenetre, pas de détruire le Frame bleu

Tt laisser ds 1 seul fichier? ben si tu trouves que c'est brouillon avec 3 fichiers, imagine le tt ds 1 seul... et puis là c'est un truc simple
bref je souhaite garder mes 3 fichiers

je te donne raison sur tt le reste (meme si on ne fait pas la compet). Y avait effectivement bcp de bling bling restant ds ce que j'avais écrit et bcp de commentaires (j'ai souvenir que les femmes adorent ms je me rends compte que je suis dvt mon ecran, dc effectivement ca ne sert pas a grd chose)

bon là je ferai pas mx.
si tu ne px pas c'est pas bien grave, je resoudrai bien tôt ou tard.

a+ et bonne soirée.
tartare


ce n'est pas à la machine de dire à l'homme ce qu'il peut faire, c'est a l'homme de dire a la machine ce qu'elle doit faire

Hors ligne

#7 17-02-2022 13:35:03

David5647
Membre
Distrib. : Debian Sid
Noyau : 5.15.0-2-amd64
(G)UI : i3wm + des bouts de kde
Inscription : 27-08-2017

Re : Python: tkinter cibler 1 widget ou fenetre depuis 1 autre widget

Bon le code est bien plus lisible comme ça ;p

---

ben c'est du ciblage
self= fenetre
fr01= Frame ds la fenetre
cnv01= Canvas ds le Frame
fr01= Frame ds le Canvas
btn01= Button ds le Frame

ca fait beaucoup de niveaux c'est vrai, ms a part tout poser sur la fenetre... je vois pas comment faire.
Tu fais comment toi?

Qu'on s'entende bien, faire:

mon_objet.variable = "valeur"

c'est simplement créer une variable dans l'espace de nom de mon_objet. C'est juste une organisation du code.

Il y a plusieurs réponses possibles:

1) A quoi sert une variable?
Une variable sert à stocker un objet pour une utilisation ultérieure.
Il faut créer une variable seulement si tu l'utilise ensuite, là ce n'était pas le cas.
As tu besoin de toutes ces variables? Est-ce important pour ton programme de conserver tout l'arbre?
Si tu veux acceder à un canvas, as tu besoin d'avoir une référence explicite à la frame qui le contient?

Quand tu écris

self.fr01.cnv01.fr01.btn01

as tu besoins de toutes les variables intermédiaires ou as tu besoins uniquement de la référence au bouton?

2) Ciblage (structure du code)

En soit, faire refléter la hiérarchie des objets comme cela, c'est plutôt une bonne idée, pourquoi pas.
Je trouve quand même que les écritures comme

self.var1.var2.var3.var4

Plutôt lourdes, donc à utiliser avec parcimonie.
Si tu veux créer la même structure avec un code (que j'estime) plus lisible, je peux te proposer ça:


##########################################
####### Classes d'un module fictif #######
##########################################
# Soit les classes A, B, C des objets tkinter
# Canvas, Frame, Button... Peu importe
class B:
    pass

class C:
    pass

class D:
    pass


########### ta méthode ################

# Voici ce que ça donne avec la méthode que tu as utilisé
class A1:
    def __init__(self):
        self.bbbbbbb = B()
        self.bbbbbbb.ccccccc = C()
        self.bbbbbbb.ccccccc.ddddddd = D()
        self.bbbbbbb.ccccccc.ddddddd.eeeeeee = "Ta méthode"

########## Proposition 1 ##################
#
# Créer des variables locales,
# faire les opératins avec les variables locales,
# enfin, ratacher et organiser les objets entre-eux
#

class A2:

    def __init__(self):

        bbbbbbb = B()
        ccccccc = C()
        ddddddd = D()
        ddddddd.eeeeeee = "Proposition 1"
        ccccccc.ddddddd = ddddddd
        bbbbbbb.ccccccc = ccccccc

        self.bbbbbbb=bbbbbbb

# (cette solution est une sorte de version "dépliée" de la proposition 2,
# un peu comme si l'on écrivait procéduralement sans faire usage des classes)


######### Proposition 2 ####################
#
# Utiliser l'héritage pour bien séparer les objets
# La structure se construit plus "naturellement"

class A3:
    def __init__(self):
        self.bbbbbbb = B2()

class B2(B):
    def __init__(self):
        self.ccccccc = C2()

class C2(C):
    def __init__(self):
        self.ddddddd = D2()

class D2(D):
    def __init__(self):
        self.eeeeeee = "Proposition 2"

aaaaaaa = A1()
print("aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee :", aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee)
aaaaaaa = A2()
print("aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee :", aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee)
aaaaaaa = A3()
print("aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee :", aaaaaaa.bbbbbbb.ccccccc.ddddddd.eeeeeee)



---

si tu fais ref a la derniere L du 2° fichier ben... si c'est un bouton, c'est le bouton personnalisé, celui defini ds le 3° fichier

Ce n'est pas un bouton, tu peux vérifier avec la fonction type().
La variable contient une classe qui a pour attribut un bouton tkinter,
self.btn01_retour et self.btn02_retour ne pourront pas être utilisés de la même façon puisqu'ils ne représentent pas des objets du même type.
C'est un peu du chipotage, ça ne change rien à l’exécution du code, mais ça rajoute qd même un peu de confusion.

---

Tt laisser ds 1 seul fichier? ben si tu trouves que c'est brouillon avec 3 fichiers, imagine le tt ds 1 seul... et puis là c'est un truc simple
bref je souhaite garder mes 3 fichiers

Je dis juste que ça ne change fondamentalement pas grand chose à condition qu'il n'y ai pas de variables globales, c'est tout.
Et donc, je sous-entends que c'est juste une histoire d'organisation de code (si tes variables sont bien confinées à tes classes).
Bref, ma remarque portait sur les variables globales pas directement sur les fichiers.
Et donc, si 3 fichiers c'est plus lisible, partons sur 3 fichiers, ce n'est pas ce que je questionnais.

---

le bouton fait bien ce que je lui demande, detruire le Frame bleu (c'est pr cela qu'il est encore en vie celui-là), ms je vx lui dire de fermer la fenetre, pas de détruire le Frame bleu


Je n'ai toujours pas compris la demande, quelle fenêtre? la popup c'est bien ça? Fermer = détruire?
Si tu détruis la fenêtre popup qui contient ta frame (ce que le premier bouton fait), où veut tu que ta frame aille? Je comprends pas

J'ai reformulé un peu ton code:

#!/usr/bin/env python3

import tkinter as tk

class MyMainWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.creer_widgets()

    def create_popup_window(self):
        MyPopup(self)

    def creer_widgets(self):
        tk.Button(self, text="open popup", command=self.create_popup_window).pack()


class MyPopup(tk.Toplevel):
    def __init__(self, parent):
        super().__init__()
        self.parent = parent
        self.creer_Widgets()

    def creer_Widgets(self):

        popup_frame = tk.Frame(self, bg="blue")
        popup_frame.grid(column=0, row=0)

        kill_mainw_Btn = BtnKill(popup_frame, "kill mainwindow", self.parent.destroy, 0)
        kill_popup_Btn = BtnKill(popup_frame, "kill popup", self.destroy, 1)
        kill_frame_Btn = BtnKill(popup_frame, "kill blue frame", popup_frame.destroy, 2)

        kill_buttn_Btn = BtnKill(popup_frame, "self kill btn", None, 3)
        kill_buttn_Btn.configure(command=kill_buttn_Btn.destroy)


class BtnKill(tk.Button):
    def __init__(self, parent, text, command, row):

        super().__init__(parent, text=text, command=command)
        self.grid(column=0, row=row)


if __name__ == "__main__":

    window = MyMainWindow()
    window.mainloop()
 



Tu as pour seuls objets:
  - la fenêtre principale : MyMainWindow, désignée par self.parent
  - la popup : MyPopup, désignée par self, enfant de window
  - la frame de la popup, tk.Frame, désignée par popup_frame, enfant de la popup
  - les boutons : BtnKill, désignés par les variables kill_<nom>_Btn, enfants de popup_frame

(les nom de variables soulignés correspondent à l'espace de nom de la fonction Popup.creer_Widgets)
 
Lequel/lesquels veut tu détruire (sachant que détruire le parent détruit l'enfant)?

Hors ligne

Pied de page des forums