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 :

Python 3.3 Tkinter Entrées contrôlées


Sujet :

Tkinter Python

  1. #1
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut Python 3.3 Tkinter Entrées contrôlées
    Bonjour à tous,

    L'extrait de code (Python 3.3 et tkinter) que je vous soumets ci-aprés remplit correctement la
    fonction que je lui demande (filtrer la saisie de 3 'Entry' ou plus, ici: float>0 ou rien) tout en essayant
    d'éviter si possible, les risques de 'plantage' par des saisies inappropriées.

    Je souhaiterais le simplifier (le rendre un peu plus 'académique').

    J'ai dû me résoudre à inhiber le click-G de souris car il provoquait une boucle sans fin en cas
    de click sur une entrée déjà validée. Idem pour Shift+Tab mais je ne sais pas l'invalider.

    J'ai essayé sans succès de regrouper en une seule les 3 fonctions de 'validation' et idem pour
    'invalidation' mais je n'ai pas réussi à passer en paramètre le nom de l'Entry.

    Si vous avez un moment ...
    Ce n'est pas urgent, c'est juste pour apprendre.

    Merci
    L.P.

    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    from tkinter import *
     
    class Saisie(object):
        "recup des saisies"
        def __init__(self,x=0):
            self.x=x
     
    def affiche():
        etq1.config(text=str(s1.x+s2.x+s3.x))
     
    def valid_e1():
        print('valid_e1') # pour trace
        if e1.get()=='':
            s1.x=0 ; affiche()
            e2.focus_set()
            return 1
        else:
            try:
                if (float(e1.get())>0) == True:
                    s1.x=float(e1Var.get())
                    print('yes e1',e1.get()) # pour trace 
                    e2.focus_set() ; e2.selection_range(0,END)
                    affiche()
                    return 1
                else:
                    return 0
            except:
                return 0
     
    def invalid_e1():
        print('invalid_e1') # pour trace
        e1.focus_set()
        e1.selection_range(0,END) # re-select saisie
        return 1    
     
    def valid_e2():
        print('valid_e2') # pour trace 
        if e2.get()=='':
            s2.x=0 ; affiche()
            e3.focus_set()
            return 1
        else:
            try:
                if (float(e2.get())>0) == True:
                    s2.x=float(e2Var.get())
                    print('yes e2',e2.get()) # pour trace 
                    e3.focus_set() ; e3.selection_range(0,END)
                    affiche()                
                    return 1
                else:
                    return0
            except:
                return 0
     
    def invalid_e2():
        print('invalid_e2') # pour trace
        e2.focus_set()
        e2.selection_range(0,END) # re-select saisie
        return 1
     
    def valid_e3():
        print('valid_e3') # pour trace
        if e3.get()=='':
            s3.x=0 ; affiche()
            e1.focus_set()
            return 1
        else:
            try:
                if (float(e3.get())>0) == True:
                    s3.x=float(e3Var.get())
                    print('yes e3',e3.get()) # pour trace 
                    e1.focus_set() ; e1.selection_range(0,END)
                    affiche()                
                    return 1
                else:
                    return 0
            except:
                return 0   
     
    def invalid_e3():
        print('invalid_e3') # pour trace
        e3.focus_set()
        e3.selection_range(0,END) # re-select saisie
        return 1
     
    def tabul(event):
        fen.event_generate('<Tab>')
     
    def onClick(event):
        return 'break'
     
    def quitter(event=None):
        fen.quit()
     
    # fenêtre principale ***************************************
    fen = Tk()
    fen.title("Exo Entrées controlées")
    fen.geometry ("600x500+150+100")
    ###----
    e1Var=StringVar(); e2Var=StringVar(); e3Var=StringVar() # StringVar()
    ###----
    s1=Saisie() ; s2=Saisie() ; s3=Saisie() ## valeurs des saisies
    ###----
    etq1=Label(fen, text='', width=10, bg='ivory')  #=================
    etq1.place(x=80, y=120)
    ###----
    e1=Entry(fen, width=10, textvariable=e1Var, validate='focusout',
            validatecommand=valid_e1, invalidcommand=invalid_e1)
    e1.place(x=80, y=80) ; e1.focus_set()
    e1.bind("<Return>",tabul) # ------------------
    e1.bind("<Button-1>", onClick ) 
    ###----
    e2=Entry(fen, width=10, textvariable=e2Var, validate='focusout',
            validatecommand=valid_e2, invalidcommand=invalid_e2)
    e2.place(x=180, y=80)
    e2.bind("<Return>",tabul) # ------------------
    e2.bind("<Button-1>", onClick )
    ###----
    e3=Entry(fen, width=10, textvariable=e3Var, validate='focusout',
            validatecommand=valid_e3, invalidcommand=invalid_e3)
    e3.place(x=280, y=80)
    e3.bind("<Return>",tabul) # ------------------
    e3.bind("<Button-1>", onClick ) 
    ###----
    btQuitter = Button(fen, text="QUITTER", font=("Arial",-12),
        takefocus=True, bg='bisque', fg='navy')
    btQuitter.place(x=500, y=465)
    btQuitter.bind("<Return>", quitter)
    btQuitter.bind("<Button>", quitter)
    ###----
    fen.protocol("WM_DELETE_WINDOW", quitter)
    fen.mainloop()
    fen.destroy()

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Quand je lis votre code, je constate 2 soucis majeurs.
    Le premier est la multiplication des fonctions valid_e{1,2,3} et invalid_e{1,2,3}.
    Une des causes est que si eN est "valide", il devra passer le focus a eN.next si N < 3 ou a e1 sinon.
    Sans entrer dans des considérations Tk, vous vous simplifieriez grandement la vie en stockant les entry dans une liste.
    Ce qui permet une première "factorisation" du code:
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    from tkinter import *
     
    # class Saisie(object):
    #     "recup des saisies"
    #     def __init__(self,x=0):
    #         self.x=x
    #  
    def affiche():
        value = 0
        for e in entries:
            s = e.get()
            if s:
                try:
                    v = float(s)
                except:
                    return
            else:
                v = 0
            value += v    
     
        etq1['text'] = str(value)
     
    def do_validate(entry):
        print ('do_validate:', entry)
     
        ix = entries.index(entry)
        ix = (ix + 1) % len(entries)
        next_entry = entries[ix]
        print ('do_validate, next:', next_entry)
     
        if entry.get()=='':
            affiche()
            next_entry.focus_set()
            return True
        else:
            try:
                if (float(entry.get())>0) == True:
                    #s1.x=float(e1Var.get())
                    print('yes: ', entry, entry.get()) # pour trace 
                    next_entry.focus_set() ; next_entry.selection_range(0,END)
                    affiche()
                    return 1
                else:
                    return 0
            except:
                return 0
     
    def do_invalidate(entry):
        print('do_invalidate:', entry) # pour trace
        entry.focus_set()
        entry.selection_range(0,END) # re-select saisie
        return 1    
     
    def valid_e1():
        print('valid_e1') # pour trace
        return do_validate(e1)
     
    #     if e1.get()=='':
    #         s1.x=0 ; affiche()
    #         e2.focus_set()
    #         return 1
    #     else:
    #         try:
    #             if (float(e1.get())>0) == True:
    #                 s1.x=float(e1Var.get())
    #                 print('yes e1',e1.get()) # pour trace 
    #                 e2.focus_set() ; e2.selection_range(0,END)
    #                 affiche()
    #                 return 1
    #             else:
    #                 return 0
    #         except:
    #             return 0
     
    def invalid_e1():
        print('invalid_e1') # pour trace
        return do_invalidate(e1)
    #     e1.focus_set()
    #     e1.selection_range(0,END) # re-select saisie
    #     return 1    
     
    def valid_e2():
        print('valid_e2') # pour trace 
        return do_validate(e2)
    #     if e2.get()=='':
    #         s2.x=0 ; affiche()
    #         e3.focus_set()
    #         return 1
    #     else:
    #         try:
    #             if (float(e2.get())>0) == True:
    #                 s2.x=float(e2Var.get())
    #                 print('yes e2',e2.get()) # pour trace 
    #                 e3.focus_set() ; e3.selection_range(0,END)
    #                 affiche()                
    #                 return 1
    #             else:
    #                 return 0
    #         except:
    #             return 0
     
    def invalid_e2():
        print('invalid_e2') # pour trace
        return do_invalidate(e2)
    #     
    #     e2.focus_set()
    #     e2.selection_range(0,END) # re-select saisie
    #     return 1
    #  
    def valid_e3():
        print('valid_e3') # pour trace
        return do_validate(e3)
    #     if e3.get()=='':
    #         s3.x=0 ; affiche()
    #         e1.focus_set()
    #         return 1
    #     else:
    #         try:
    #             if (float(e3.get())>0) == True:
    #                 s3.x=float(e3Var.get())
    #                 print('yes e3',e3.get()) # pour trace 
    #                 e1.focus_set() ; e1.selection_range(0,END)
    #                 affiche()                
    #                 return 1
    #             else:
    #                 return 0
    #         except:
    #             return 0   
     
    def invalid_e3():
        print('invalid_e3') # pour trace
        return do_invalidate(e3)
    #     e3.focus_set()
    #     e3.selection_range(0,END) # re-select saisie
    #     return 1
     
    def tabul(event):
        fen.event_generate('<Tab>')
     
    def onClick(event):
        return 'break'
     
    def quitter(event=None):
        fen.quit()
     
    if __name__ == '__main__': 
        # fenêtre principale ***************************************
        fen = Tk()
        fen.title("Exo Entrées controlées")
        fen.geometry ("600x500+150+100")
    #     ###----
    #     e1Var=StringVar(); e2Var=StringVar(); e3Var=StringVar() # StringVar()
    #     ###----
    #     s1=Saisie() ; s2=Saisie() ; s3=Saisie() ## valeurs des saisies
    #     ###----
        etq1=Label(fen, text='', width=10, bg='ivory')  #=================
        etq1.place(x=80, y=120)
        ###----
        e1=Entry(fen, width=10, textvariable=e1Var, validate='focusout',
                validatecommand=valid_e1, invalidcommand=invalid_e1, name="e1")
        e1.place(x=80, y=80) ; e1.focus_set()
        e1.bind("<Return>",tabul) # ------------------
        e1.bind("<Button-1>", onClick ) 
        ###----
        e2=Entry(fen, width=10, textvariable=e2Var, validate='focusout',
                validatecommand=valid_e2, invalidcommand=invalid_e2, name='e2')
        e2.place(x=180, y=80)
        e2.bind("<Return>",tabul) # ------------------
        e2.bind("<Button-1>", onClick )
        ###----
        e3=Entry(fen, width=10, textvariable=e3Var, validate='focusout',
                validatecommand=valid_e3, invalidcommand=invalid_e3, name='e3')
        e3.place(x=280, y=80)
        e3.bind("<Return>",tabul) # ------------------
        e3.bind("<Button-1>", onClick ) 
    #++
        entries = [ e1, e2, e3 ]
    #--
        ###----
        btQuitter = Button(fen, text="QUITTER", font=("Arial",-12),
            takefocus=True, bg='bisque', fg='navy')
        btQuitter.place(x=500, y=465)
        btQuitter.bind("<Return>", quitter)
        btQuitter.bind("<Button>", quitter)
        ###----
        fen.protocol("WM_DELETE_WINDOW", quitter)
        fen.mainloop()
        fen.destroy()
    note: je dis souvent qu'informatique c'est mettre le problème "en forme".
    Vous constatez que les propriétés d'une liste simple aide grandement à réduire les répétitions.

    Vous voyez que mes fonctions do_validate, do_invalidate prennent maintenant en paramètre "entry".
    Pour "factoriser" la création (et la fabrication du paramètre), on a 3 solutions:
    - lambda (closure simple)
    - classes,
    - fonctions de fonctions (closure plus compliquées)
    Avec les lambda... on peut écrire:
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    from tkinter import *
     
    def affiche():
        value = 0
        for e in entries:
            s = e.get()
            if s:
                try:
                    v = float(s)
                except:
                    return
            else:
                v = 0
            value += v    
     
        etq1['text'] = str(value)
     
    def do_validate(entry):
        print ('do_validate:', entry)
     
        ix = entries.index(entry)
        ix = (ix + 1) % len(entries)
        next_entry = entries[ix]
        print ('do_validate, next:', next_entry)
     
        if entry.get()=='':
            affiche()
            next_entry.focus_set()
            return True
        else:
            try:
                if (float(entry.get())>0) == True:
                    #s1.x=float(e1Var.get())
                    print('yes: ', entry, entry.get()) # pour trace 
                    next_entry.focus_set() ; next_entry.selection_range(0,END)
                    affiche()
                    return 1
                else:
                    return 0
            except:
                return 0
     
    def do_invalidate(entry):
        print('do_invalidate:', entry) # pour trace
        entry.focus_set()
        entry.selection_range(0,END) # re-select saisie
        return 1    
     
    # def valid_e1():
    #     print('valid_e1') # pour trace
    #     return do_validate(e1)
     
     
    # def invalid_e1():
    #     print('invalid_e1') # pour trace
    #     return do_invalidate(e1)
     
     
    # def valid_e2():
    #     print('valid_e2') # pour trace 
    #     return do_validate(e2)
     
    # def invalid_e2():
    #     print('invalid_e2') # pour trace
    #     return do_invalidate(e2)
     
    # def valid_e3():
    #     print('valid_e3') # pour trace
    #     return do_validate(e3)
     
    # def invalid_e3():
    #     print('invalid_e3') # pour trace
    #     return do_invalidate(e3)
     
    # def tabul(event):
    #    fen.event_generate('<Tab>')
     
    # def onClick(event):
    #    return 'break'
     
    def quitter(event=None):
        fen.quit()
     
    if __name__ == '__main__': 
        # fenêtre principale ***************************************
        fen = Tk()
        fen.title("Exo Entrées controlées")
        fen.geometry ("600x500+150+100")
     
        etq1=Label(fen, text='', width=10, bg='ivory')  #=================
        etq1.place(x=80, y=120)
        ###----
        x, y = 80, 80
        entries = []
        for z in range(3):
            entry = Entry(fen, width=10, name="e%d" % (z+1))
            entry.configure(
                validate='focusout',
                validatecommand=lambda e=entry: do_validate(e), 
                invalidcommand=lambda e=entry: do_invalidate(e),
                )
     
            entry.place(x=x+100*z, y=80) ; 
            entry.bind("<Return>", lambda e: entry.event_generate('<Tab>'))# ------------------
            entry.bind("<Button-1>", lambda e: 'break' ) 
            entries.append(entry)
        entries[0].focus_set()
    #     ###----
    #     e2=Entry(fen, width=10, textvariable=e2Var, validate='focusout',
    #             validatecommand=valid_e2, invalidcommand=invalid_e2, name='e2')
    #     e2.place(x=180, y=80)
    #     e2.bind("<Return>",tabul) # ------------------
    #     e2.bind("<Button-1>", onClick )
    #     ###----
    #     e3=Entry(fen, width=10, textvariable=e3Var, validate='focusout',
    #             validatecommand=valid_e3, invalidcommand=invalid_e3, name='e3')
    #     e3.place(x=280, y=80)
    #     e3.bind("<Return>",tabul) # ------------------
    #     e3.bind("<Button-1>", onClick ) 
    # #++
    #     entries = [ e1, e2, e3 ]
    #--
        ###----
        btQuitter = Button(fen, text="QUITTER", font=("Arial",-12),
            takefocus=True, bg='bisque', fg='navy')
        btQuitter.place(x=500, y=465)
        btQuitter.bind("<Return>", quitter)
        btQuitter.bind("<Button>", quitter)
        ###----
        fen.protocol("WM_DELETE_WINDOW", quitter)
        fen.mainloop()
        fen.destroy()
    note: on peut aussi remplacer "tabul" et "onClick" par des "lambda".

    Avec des "class":
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    import tkinter as tk
     
    from tkinter import *
     
    def affiche():
        value = 0
        for e in entries:
            s = e.get()
            if s:
                try:
                    v = float(s)
                except:
                    return
            else:
                v = 0
            value += v    
     
        etq1['text'] = str(value)
     
    def do_validate(entry):
        print ('do_validate:', entry)
     
        ix = entries.index(entry)
        ix = (ix + 1) % len(entries)
        next_entry = entries[ix]
        print ('do_validate, next:', next_entry)
     
        if entry.get()=='':
            affiche()
            next_entry.focus_set()
            return True
        else:
            try:
                if (float(entry.get())>0) == True:
                    #s1.x=float(e1Var.get())
                    print('yes: ', entry, entry.get()) # pour trace 
                    next_entry.focus_set() ; next_entry.selection_range(0,END)
                    affiche()
                    return 1
                else:
                    return 0
            except:
                return 0
     
    def do_invalidate(entry):
        print('do_invalidate:', entry) # pour trace
        entry.focus_set()
        entry.selection_range(0,END) # re-select saisie
        return 1    
     
     
     
    def quitter(event=None):
        fen.quit()
     
    class Entry(tk.Entry):
        do_validate = do_validate
        do_invalidate = do_invalidate
     
        def __init__(self, *args, **kwds):
            super().__init__(*args, **kwds)
            self.configure(
                validate='focusout',
                validatecommand=self.do_validate, 
                invalidcommand=self.do_invalidate,
                )
     
            self.bind("<Return>", lambda e: self.event_generate('<Tab>'))
            self.bind("<Button-1>", lambda e: 'break' ) 
     
    if __name__ == '__main__': 
        # fenêtre principale ***************************************
        fen = Tk()
        fen.title("Exo Entrées controlées")
        fen.geometry ("600x500+150+100")
     
        etq1=Label(fen, text='', width=10, bg='ivory')  #=================
        etq1.place(x=80, y=120)
        ###----
        x, y = 80, 80
        entries = []
        for z in range(3):
            entry = Entry(fen, width=10, name="e%d" % (z+1))
    #         entry.configure(
    #             validate='focusout',
    #             validatecommand=lambda e=entry: do_validate(e), 
    #             invalidcommand=lambda e=entry: do_invalidate(e),
    #             )
     
            entry.place(x=x+100*z, y=80) ; 
    #         entry.bind("<Return>",tabul) # ------------------
    #         entry.bind("<Button-1>", onClick ) 
            entries.append(entry)
        entries[0].focus_set()
     
        ###----
        btQuitter = Button(fen, text="QUITTER", font=("Arial",-12),
            takefocus=True, bg='bisque', fg='navy')
        btQuitter.place(x=500, y=465)
        btQuitter.bind("<Return>", quitter)
        btQuitter.bind("<Button>", quitter)
        ###----
        fen.protocol("WM_DELETE_WINDOW", quitter)
        fen.mainloop()
        fen.destroy()
    note: plutôt que de fabriquer une fonction collée à entry via lambda, on utilise la méthode do_validate associée à l'objet Entry.
    Les do_validate = do_validate dans la definition de la classe sont la pour montrer qu'entre une fonction qui prend entry en premier paramètre et une methode, il n'y a qu'un petit pas.

    fonctions de fonctions:
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    import tkinter as tk
     
    from tkinter import *
     
    def affiche():
        value = 0
        for e in entries:
            s = e.get()
            if s:
                try:
                    v = float(s)
                except:
                    return
            else:
                v = 0
            value += v    
     
        etq1['text'] = str(value)
     
     
     
     
    def quitter(event=None):
        fen.quit()
     
     
     
    def create_entry(*args, **kwds):
     
        def do_validate():
            print ('do_validate:', entry)
     
            ix = entries.index(entry)
            ix = (ix + 1) % len(entries)
            next_entry = entries[ix]
            print ('do_validate, next:', next_entry)
     
            if entry.get()=='':
                affiche()
                next_entry.focus_set()
                return True
            else:
                try:
                    if (float(entry.get())>0) == True:
                        #s1.x=float(e1Var.get())
                        print('yes: ', entry, entry.get()) # pour trace 
                        next_entry.focus_set() ; next_entry.selection_range(0,END)
                        affiche()
                        return 1
                    else:
                        return 0
                except:
                    return 0
     
        def do_invalidate():
            print('do_invalidate:', entry) # pour trace
            entry.focus_set()
            entry.selection_range(0,END) # re-select saisie
            return 1    
     
     
        entry = Entry(*args, **kwds)
        entry.configure(
            validate='focusout',
            validatecommand=do_validate, 
            invalidcommand=do_invalidate,
            )
     
        entry.bind("<Return>", lambda e: entry.event_generate('<Tab>'))
        entry.bind("<Button-1>", lambda e: 'break' ) 
        return entry
     
    if __name__ == '__main__': 
        # fenêtre principale ***************************************
        fen = Tk()
        fen.title("Exo Entrées controlées")
        fen.geometry ("600x500+150+100")
     
        etq1=Label(fen, text='', width=10, bg='ivory')  #=================
        etq1.place(x=80, y=120)
        ###----
        x, y = 80, 80
        entries = []
        for z in range(3):
            entry = create_entry(fen, width=10, name="e%d" % (z+1))
     
            entry.place(x=x+100*z, y=80) ; 
            entries.append(entry)
        entries[0].focus_set()
     
        ###----
        btQuitter = Button(fen, text="QUITTER", font=("Arial",-12),
            takefocus=True, bg='bisque', fg='navy')
        btQuitter.place(x=500, y=465)
        btQuitter.bind("<Return>", quitter)
        btQuitter.bind("<Button>", quitter)
        ###----
        fen.protocol("WM_DELETE_WINDOW", quitter)
        fen.mainloop()
        fen.destroy()
    ici create_entry construit l'entry mais aussi un espace de noms dans lequel pourront taper les fonctions "nested" do_validate et do_invalidate.
    plus besoin de passer entry en paramètre.

    En guise de conclusion, la programmation des GUI est intéressante car on se retrouve avec pleins de widgets qui auront parfois des comportements semblables.
    Comment gérer tout çà sans répliquer le code?
    Python vous offre beaucoup de choix.

    - W

    PS: Vous aviez posté vos premières tribulations avec Entry, il y a quelques mois. Mais je n'ai pas eu le temps de construire une réponse satisfaisante à vos questions.
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut Python 3.3 Tkinter Entrées contrôlées
    Bonjour et merci pour cette prompte réponse qui répond parfaitement à ma question sur la 'factorisation des fonctions'.
    Je vais essayer de comprendre au moins 2 méthodes sur les 3..

    Par ailleurs vous parlez de 2 soucis dans mon code..
    C'est flateur. J'aurais parié sur beaucoup plus. C'est encourageant et je le dis sans rire.

    C'est quoi le 2° souci ?

    Salut et encore merci.

    L.P.


    P.S: Je ne sais pas invalider Shift+Tab qui déclanche une boucle sans fin.
    Si vous avez un moment..

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,
    Le premier soucis était la répétition des lignes de codes des fonctions pour savoir qui est l'entry suivante (c'est ici que la liste est intéressante).
    On avait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def valid_e1():
         ... code avec next_entry=e2
    def valid_e2():
         ... code avec next_entry=e3
    def valid_e3():
         ... code avec next_entry=e1
    J'ai poussé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def do_valid(entry):
          ...on calcule next_entry
          ...code commun
    a ce moment là, le corps des fonction valid_eX se réduit à appeler do_valid avec l'entry qui va bien.

    Le deuxième soucis est de se libérer des valid_eX avec les 3 solutions lambda, classes, fonctions de fonctions,...

    Citation Envoyé par luc pic
    Par ailleurs vous parlez de 2 soucis dans mon code..
    C'est flatteur. J'aurais parié sur beaucoup plus. C'est encourageant et je le dis sans rire.
    Si je devais coder un truc comme çà, j'aurais des approches différentes.
    Ici, je n'ai abordé que les soucis de "structure" i.e. on prend comme à priori que votre approche est bonne et on regarde ce qui est "souffreteux".
    çàd les concepts qu'il serait intéressant de travailler pour être un peu plus "confortable".

    Citation Envoyé par luc pic
    P.S: Je ne sais pas invalider Shift+Tab qui déclanche une boucle sans fin.
    Si vous avez un moment..
    Si vous ne touchez à rien "Shift+Tab" et "Tab" permettent de changer le widget de saisie qui a le "focus" suivant une liste interne à Tk.
    "Tab" réalise "Next" alors que "Shift+Tab" réalise "Prev".
    Problème: tous deux génèrent "focusout" et provoque l'appel de do_validate qui n'est codé que pour supporter "next".
    => Si do_validate ne sait pas dans quel contexte il a été appelé, çà boucle lorsqu'on le prend à rebrousse poil.

    Je regarderai çà mais pas avant ce soir, il fait beau dehors: autant en profiter.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    La liste d'entry permettait de "factoriser" votre code...
    Mais <Tab> et <Shift-Tab> montrent que Tk a déjà sa petite idée de qui sera "suivant".
    L'exemple ci dessous montre comment çà fonctionne:
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    import tkinter as tk
     
    def on_focusout(entry):
     
        print ('focusout:', entry)
        if entry.ignore_focusout: 
            return False
     
        return (entry.get() == '1')
     
    def on_invalidate(entry):
        print ('on_invalidate: ', entry, end='')
     
        if entry.ignore_focusout:
            print (' toggle')
            entry.ignore_focusout = False
            return False
        print(' stick on entry')
        next_entry = app.focus_get()
        next_entry.ignore_focusout = True
        entry.focus_set()
        return False
     
     
     
    if __name__ == '__main__':
     
        app = tk.Tk()
     
        frame = tk.Frame(name='form')
     
        for x in range(3):
            entry = tk.Entry(frame, name='entry-%d' % x)
            entry.ignore_focusout = False
            entry.configure(
                validate='focusout',
                vcmd=lambda w=entry: on_focusout(w),
                invcmd=lambda w=entry: on_invalidate(w),
                )
            entry.grid(row=x, column=0)
     
     
        frame.pack(fill='x', side='top')
     
        tk.mainloop()
    Si vous testez un peu, vous constaterez que lorsque "on_focusout" est appelé, le suivant - quelqu'il soit - est le widget que retourne app.focus_get().
    - * - Problème - * -
    Si la saisie est invalide, on va "changer" le focus...
    Et comme on n'a peut être encore rien saisi dans le "suivant", on va boucler dans les "focusout".
    C'est ce que "résout" l'attribut (dynamique) ignore_focusout...

    - W
    edit: et j'ai changé la répartition du boulot entre ce que fait "validate" et ce que fait "invalidate".
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut
    Bonjour,
    Sur mon poste (W8; Python 3.3) le dernier bout de code ne fonctionne pas.
    Quelque soit la saisie (correcte ou non) impossible de passer à l'entrée suivante.
    En 'gros' je comprends l'idée mais je ne trouve pas l'erreur.
    L.P.

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par luc pic Voir le message
    Quelque soit la saisie (correcte ou non) impossible de passer à l'entrée suivante.
    Dans cet exemple, la saisie est "correcte" si (et seulement si) entry.get() retourne "1".

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  8. #8
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut
    Bonjour,

    Merci pour le coup de main mais je me vois contraint d'abandonner l'idée de validation par 'focusout'. Trop compliqué pour mon niveau d'apprenti.

    Dans le dernier exemple, j'ai tenté de modifier le return du 'on_focusout' par

    " return (entry.get() in "0123456789-.") "

    mais impossible de saisir 2 fois le même chiffre ni le signe moins ni le point.

    Il me semble qu'une approche de la validation par 'key' sera plus facile.

    Encore merci, j'ai retenu en tout cas la 'factorisation' des procedures et ce n'est pas rien.

    A + sans doute..

    L.P.

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Dans le dernier exemple, j'ai tenté de modifier le return du 'on_focusout' par

    " return (entry.get() in "0123456789-.") "
    Lorsque focus est "out", vous supposez que l'utilisateur à saisi des informations et qu'il veut passer à la saisie d'une autre entrée.
    Si l'entrée n'est pas valide, on scotche le focus sur l'entrée en cours.
    A ce moment là, entry.get() retourne tout ce qui a été saisi et s'il y a plus de 2 caractères "entry.get() in "0123456789-." sera toujours "False".

    Dans votre code, l'entrée était valide si et seulement si float(...) se passait normalement.
    Mon code se concentre sur le "focus traversal" et montrer comment çà fonctionne pour éviter (ici) d'avoir une liste.
    Si vous voulez que l'entrée soit valide lorsqu'on a saisi un float, il faut écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
           try:
                 float(entry.get())
           except:
                 return False
           else:
                 return True
    Ceci dit, on a seulement besoin d'une fonction qui retourne un booléen en fonction du contenu de l'entrée: dommage de faire cela que dans un cas particulier (sinon tester).

    Il me semble qu'une approche de la validation par 'key' sera plus facile.
    La logique est différente et l'interface Tk plus compliquée voir le post.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  10. #10
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut
    Bonjour,

    Effectivement par 'key' ce n'est pas aussi simple que j'aurais pu le penser...!

    J'ai lu avec intérêt le 'post' de "PauseKawa" qui teste 'isdigit()' pour valider des entiers mais pour des 'float' ?

    Donc jusqu'à plus avancé dans mon apprentissage, je procéderai à une saisie en deux parties. Une pour les Entiers et l'autre pour les décimales. L'addition des 2 entrées fera le float.

    En tout cas ce 'tour d'horizon' a été très intéressant.

    Encore merci pour votre patience.

    L.P.

    J'ai trouvé à l'adresse: http://tkinter.unpythonic.net/wiki/ValidateEntry
    une exemple complet que personnellement, compte tenu de mon niveau actuel, je n'ai pas complètement compris et encore moins su adapter.

    Moi, c'est pour le plaisir et je ne suis pas pressé mais ça peut en intéresser d'autres.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par luc pic Voir le message
    Effectivement par 'key' ce n'est pas aussi simple que j'aurais pu le penser...!
    En fait, le callback associé à "key" étant déclenché à la saisie de chaque caractère il permet une interaction "fine" avec l'utilisateur.
    Mais votre code est plus orienté à scotcher le focus sur l'entry courante tant que l'utilisateur n'a pas saisi le "float" attendu.

    J'ai lu avec intérêt le 'post' de "PauseKawa" qui teste 'isdigit()' pour valider des entiers mais pour des 'float' ?
    isdigit() permet d'éviter le try...except...
    Pour "float", dommage de s'en passer.

    J'ai trouvé à l'adresse: http://tkinter.unpythonic.net/wiki/ValidateEntry
    une exemple complet que personnellement, compte tenu de mon niveau actuel, je n'ai pas complètement compris et encore moins su adapter.

    Moi, c'est pour le plaisir et je ne suis pas pressé mais ça peut en intéresser d'autres.
    Cet exemple utilise la deuxième méthode mentionnée par PauseKawa (trace sur la variable Tk associée) pour tester la validité de l'Entry (à chaque saisie).
    Après on peut "spécialiser" les Entry pour qu'elles acceptent un "int", un "float",... en sous classant et en surchargeant des méthodes de classe.
    note: on n'est plus à comprendre ce que fait Entry mais à organiser du code et offrir une interface cohérente pour un développeur d'application tkinter.
    L'important n'est pas "comment cela se construit" mais "comment cela s'utilise".

    Pour ce qui est de "scotcher le focus sur l'entry courante", cela n'apporte pas grand chose.

    Bon courage,
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre régulier
    Homme Profil pro
    retraité
    Inscrit en
    Septembre 2010
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Septembre 2010
    Messages : 102
    Points : 71
    Points
    71
    Par défaut
    Bonjour à tous,

    J'ai finalement renoncé à renoncer de poursuivre le codage des saisies 'float' par le filtrage par 'key'.

    Les conseils et exemples des uns et des autres (Notamment 'Wiztricks' qui reconnaîtra ici ses leçons sur la factorisation des fonctions) ont étés profitables et le code ci-après permet la saisie contrôlée de 'float' dans cet exemple basique de calcul de delta en pourcentages.
    Le résultat est impeccable.

    Toutefois, n'ayant pas su factoriser, dans la construction des entrées, les 'StringVar', j'ai utilisé 2 variables globales et je ne pense pas que ce soit la bonne solution.

    Je n'ai pas su faire autrement.

    Serait-il possible de mettre ce code de saisie 'float' dans une classe enregistrée à part pour des utilisations ultérieures ?

    Si oui, comment ?

    L.P.
    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    from tkinter import *
     
    NUM = "1234567890." # filtrage nécessaire à cause du point
     
    def tabul(event):
        w=event.widget
        fen.event_generate('<Tab>')
     
    def affiche(event):
        etq3['text']=''
        w=event.widget #  ; print(event.widget, w.get())
     
        global e1, e2 # ; print(e1, e2)
     
        if w == entries[0] and w.get() != '':
            e1 = float(w.get()) # ; print('Saisie.e1',e1)
     
        if w == entries[1] and w.get() != '':
            e2 = float(w.get()) # ; print('Saisie.e2',e2)
     
        if (entries[0].get() !='' and entries[0].get() !='0' \
           and entries[0].get() !=0) and entries[1].get() !='':
     
            r = (e2 / e1 - 1) * 100 # delta en % entre s1 et s2
            etq3['text']=str("delta:  {:8.4f} %".format(r))
     
    def touche(event):
        w=event.widget # ; print(event.widget)
        w.config(validate="key")
        w.car = event.char
     
    def chiffres(e):
    #    print(e) # -- trace
        try:
            float(e.get() + e. car)
            return e.car in NUM
        except:
            return 0
    # --------
    fen= Tk()
    fen.title("validation FLOAT des Entry")
    fen.protocol("WM_DELETE_WINDOW", fen.quit)
    maFonte = "Comic_Sans_MS -14 bold"
    # ---------------------------
    entries = []
    for z in range(2):
        s=Entry(fen, font=maFonte, name="s%d" %(z+1))
        s.config(vcmd=lambda e=s:chiffres(e), validate = "key")
        s.car=""
        s.bind("<Return>", tabul)
        s.bind("<Key>", touche)
        s.bind("<FocusOut>", affiche)
        s.pack(pady=20, padx=20)
        entries.append(s)
    entries[0].focus_set()
    e1=1 ; e2=0
    # ----------
    etq3=Label(fen, width=20, text='', bg='light blue'); etq3.pack(pady=10)
    # ----------
    Button(fen, font=maFonte, text="QUITTER",
    command=fen.quit).pack(pady=20)
    # ------------ boucle 
    fen.mainloop()
    fen.destroy() 
    # fichier validation_float_key_ok_033.py

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 09/09/2013, 10h22
  2. Problème Python 2.7 Tkinter
    Par nicolivier dans le forum Général Python
    Réponses: 4
    Dernier message: 23/03/2013, 19h32
  3. Espacement entre contrôles
    Par Neuromancien2 dans le forum wxPython
    Réponses: 1
    Dernier message: 12/03/2011, 22h30
  4. différence entre contrôle utilisateur et contôle composite
    Par abdallah2007 dans le forum Windows Forms
    Réponses: 1
    Dernier message: 27/05/2009, 00h04
  5. [AC-2003] Comment retrouver toutes les liaisons entre contrôles
    Par amelien dans le forum VBA Access
    Réponses: 1
    Dernier message: 03/10/2008, 21h36

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