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

PyQt Python Discussion :

limiter la saisie d'une cellule dans un QtTableWidget [QtGui]


Sujet :

PyQt Python

  1. #1
    Membre du Club
    Homme Profil pro
    débutant
    Inscrit en
    Février 2012
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : débutant
    Secteur : Alimentation

    Informations forums :
    Inscription : Février 2012
    Messages : 88
    Points : 56
    Points
    56
    Par défaut limiter la saisie d'une cellule dans un QtTableWidget
    Bonjour à tous,
    voici mon problème :
    j'ai un QtTableWidget qui me sert à la saisie de prix (donc en euro), et je voudrais limiter cette saisie a des chiffres de type float.
    En clair lorsque l'utilisateur tape une lettre il n'y ai pas de saisie ?
    Sur mon exemple j'ai connecté la vérification, mais elle s'effectue seulement quand je quitte la cellule.
    je pense qu'il faut implanter un keyPressEvent seulement sur le tableur et non sur tous les autres objets, comment faire ?
    merci d'avance de votre aide

    Voici un petit bout de code pour formuler mon problème :
    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
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3.4
     
    from __future__ import division
    import os,sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
     
    ###################################################################################################
    class Feuille( QWidget) :
        def __init__(self, parent = None) :
            super(Feuille, self).__init__( parent)
            self.setObjectName("Feuille")
            self.parent = parent
     
            self.tableur = QTableWidget()
            self.tableur.setRowCount(4)
            self.tableur.setColumnCount(2)
            tuple_entete = ("Prix", "Unité")
            self.tableur.setHorizontalHeaderLabels(tuple_entete)
            tuple_produit = ("Produit 1", "Produit 2", "Produit 3","Produit 4")
            self.tableur.setVerticalHeaderLabels(tuple_produit)
     
            for cpt in range(self.tableur.rowCount()):
                prx = QTableWidgetItem(str(float(cpt * 10)))
                prx.setTextAlignment(Qt.AlignCenter)
                prx.setForeground(Qt.red)
                self.tableur.setItem(cpt, 0, prx)
     
                texte = QTableWidgetItem("€")
                texte.setTextAlignment(Qt.AlignCenter)
                texte.setFlags(Qt.NoItemFlags) # non modifiable
                self.tableur.setItem(cpt, 1, texte)
     
            self.tableur.cellChanged.connect(self.control_saisie)
     
            layout = QVBoxLayout()
            layout.addWidget(self.tableur)
            self.setLayout(layout)
     
        def control_saisie(self):
            caractere_autorise = (0,1,2,3,4,5,6,7,8,9,".")
            txt = self.tableur.currentItem().text()
            if txt in caractere_autorise :
                pass
            else :
                txt = txt[0:-1]
                self.tableur.setCurrentItem(float(txt))
     
    #############################################################################
    if __name__ == "__main__":
        app =  QApplication (sys.argv)
        fp = Feuille()
        fp.show()
        sys.exit(app.exec_())
        app.MainLoop()

  2. #2
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    salut,

    pour implanter uniquement un keyPressEvent :
    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
    class SuperTableur(QTableWidget):
        def __init__(self, Parent=None):
            super().__init__(Parent)
     
            self.setRowCount(4)
            self.setColumnCount(2)
            tuple_entete = ("Prix", "Unité")
            self.setHorizontalHeaderLabels(tuple_entete)
            tuple_produit = ("Produit 1", "Produit 2", "Produit 3","Produit 4")
            self.setVerticalHeaderLabels(tuple_produit)
     
        def keyPressEvent(self, event):
           ......
     
     
    class Feuille( QWidget) :
        def __init__(self, parent = None) :
           .....
           self.tableur = SuperTableur()
           ....
    Il est également possible de surveiller tous les évènements qui se passe dans la fenêtre via un eventFilter() puis de ne traiter que le QTableWidget et de ne prendre en compte que les keyPress.
    Mais au final c'est bien plus lourd et inutile dans le cas présent.

    Si jamais tu utilises des fichiers ui pour l'interface tu peux également faire ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class SuperTableur(QTableWidget):
        def keyPressEvent(self, event):
           ......
     
    class Feuille( QWidget) :
        def __init__(self, parent = None) :
           .....
           self.tableur.__class__ = SuperTableur
           ....
    J’espère ne pas avoir dis de bêtise mais je sais que des gens très compétents me le signaleront si c'est le cas.

  3. #3
    Membre du Club
    Homme Profil pro
    débutant
    Inscrit en
    Février 2012
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : débutant
    Secteur : Alimentation

    Informations forums :
    Inscription : Février 2012
    Messages : 88
    Points : 56
    Points
    56
    Par défaut
    Merci hizoka, j'étudie tout ça demain.

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 478
    Points : 9 278
    Points
    9 278
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Une autre solution que je préfère: QValidator, QDoubleValidator ou QRegExpValidator mis en place dans un QLineEdit avec sa méthode setValidator.

    Voilà un petit code avec QDoubleValidator:

    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
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3, PyQt5
     
    import sys
    from PyQt5 import (QtWidgets, QtGui, QtCore)
     
    #############################################################################
    class Fenetre(QtWidgets.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super().__init__(parent)
            self.resize(300, 200)
     
            self.edit = QtWidgets.QLineEdit(self)
            self.edit.editingFinished.connect(self.saisienb)
     
            # met en place le validateur qui n'accepte que les flottants
            validator = QtGui.QDoubleValidator()
            self.edit.setValidator(validator)
     
            # place le widget dans la fenêtre
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.edit, 0, 0)
            self.setLayout(posit)
     
        #========================================================================
        def saisienb(self):
            """exécuté en fin de saisie, seulement si la validation est ok!
            """
            print(self.edit.text())
     
    #############################################################################
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
    L'inconvénient, qui peut aussi être un avantage, c'est que le format dépend de la langue de l'OS: ça veut dire que pour un OS français, c'est la virgule qui marche et non le point décimal.

    Et, bien sûr, il faut intégrer le QLineEdit en question dans la bonne case du QTableWidget (setCellItem?) à la place de l'habituel QTableWidgetItem.

    Si on veut une validation plus spécifique, on peut utiliser QRegExpValidator avec le motif regex qui va bien.

    Voilà un autre exemple qui permet de saisir 2 nb flottants séparés par une virgule (type 12.3,56.8):

    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
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3, PyQt4
     
    import sys
    from PyQt5 import (QtWidgets, QtGui, QtCore)
     
    #############################################################################
    class Fenetre(QtWidgets.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre, self).__init__(parent)
     
            self.resize(300, 200)
     
            self.edit = QtWidgets.QLineEdit(self)
            self.edit.editingFinished.connect(self.saisienb)
     
            motif = r"^[0-9]+\.[0-9]+,[0-9]+\.[0-9]+$"  #  exemple: 674097.85,6813.25
            regex = QtCore.QRegExp(motif)
            validator = QtGui.QRegExpValidator(regex, self.edit)
            self.edit.setValidator(validator)
     
            # placer les widgets dans la fenêtre
            posit = QtWidgets.QGridLayout()
            posit.addWidget(self.edit, 0, 0)
            self.setLayout(posit)
     
        #========================================================================
        def saisienb(self):
            """exécuté en fin de saisie, seulement si la validation est ok!
            """
            print(self.edit.text())
     
    #############################################################################
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
    Dans les cas encore plus bizarre, on peut sous-classer QValidator et faire tous les tests de validations qu'on veut et renvoyer son résultat au QLineEdit.

    J'utilise ça pour savoir si le mot qu'on est en train de taper se trouve dans une liste de mots donnés (en fait une liste de pays).

  5. #5
    Membre du Club
    Homme Profil pro
    débutant
    Inscrit en
    Février 2012
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : débutant
    Secteur : Alimentation

    Informations forums :
    Inscription : Février 2012
    Messages : 88
    Points : 56
    Points
    56
    Par défaut
    merci Tyrtamos je touche presque au but !
    J'ai modifié mon code, en ajoutant un tableau de QLineEdit défini par le nombre d'entrée de mon tableur.
    Ensuite j'implante un eventFilter pour déterminer le QLineEdit en cours.
    J'ai préféré ta technique avec QRegExp car tous les valeurs saisie jusqu'à présent ont comme séparateur décimal le point.
    Reste 2 problèmes :
    - Lorsque la saisie n'est pas valide (exemple rien de saisi dans QLineEdit en cours) la méthode "saisienb" n'est pas lancée.
    Est il possible d'activer un message d'erreur ou de mettre la valeur 0.0 par défaut ?

    - Existe il un moyen plus sympa pour bloquer la saisie d'une colonne autre que "Qt.NoItemFlags" qui vaut un enabled = False ?

    Je mets mon code, toutes suggestions sont les biens venues.

    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
     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    # Python 3.4
     
    from __future__ import division
    import os,sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
     
    ###################################################################################################
    class Feuille( QWidget) :
        def __init__(self, parent = None) :
            super(Feuille, self).__init__( parent)
            self.setObjectName("Feuille")
            self.parent = parent
     
            self.edit_en_cours = 0
     
            self.tableur = QTableWidget()
            self.tableur.setRowCount(4)
            self.tableur.setColumnCount(2)
            tuple_entete = ("Prix", "Unité")
            self.tableur.setHorizontalHeaderLabels(tuple_entete)
            tuple_produit = ("Produit 1", "Produit 2", "Produit 3","Produit 4")
            self.tableur.setVerticalHeaderLabels(tuple_produit)
     
            self.edit = [0] * self.tableur.rowCount()
     
            motif = r"^[0-9]+\.[0-9]+$"
            regex = QRegExp(motif)
     
            for cpt in range(self.tableur.rowCount()):
                self.edit[cpt] = QLineEdit()
                self.edit[cpt].installEventFilter(self)
                self.edit[cpt].editingFinished.connect(self.saisienb)
                validator = QRegExpValidator(regex, self.edit[cpt])
                self.edit[cpt].setValidator(validator)
                self.edit[cpt].setText(str(float(cpt * 10)))
                self.tableur.setCellWidget(cpt, 0, self.edit[cpt])
     
                texte = QTableWidgetItem("€")
                texte.setTextAlignment(Qt.AlignCenter)
                texte.setFlags(Qt.NoItemFlags) # non modifiable
                self.tableur.setItem(cpt, 1, texte)
     
            layout = QVBoxLayout()
            layout.addWidget(self.tableur)
            self.setLayout(layout)
     
        def eventFilter(self, obj, event):
            """detecte quel lineEdit a le focus et en recupere l'index"""
            if event.type() ==  QEvent.FocusIn:
                if obj in self.edit:
                    self.edit_en_cours = self.edit.index(obj)
            return False
     
        def saisienb(self):
            print(self.edit[self.edit_en_cours].text())
     
    #############################################################################
    if __name__ == "__main__":
        app =  QApplication (sys.argv)
        fp = Feuille()
        fp.show()
        sys.exit(app.exec_())
        app.MainLoop()

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 478
    Points : 9 278
    Points
    9 278
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Par défaut, la fin d'édition du QLineEdit avec un QValidator ne déclenche le signal editingFinished que si la validation a dit Ok, et ça parait logique.

    Pour gérer la fin d'édition avec une case vide, je pense qu'il faut gérer la perte de focus (tabulation ou souris) avec le "focusOutEvent" du QLineEdit.

    Pour empêcher la saisie d'une colonne, je pense qu'un ".setReadOnly(True)" appliqué aux QLineEdit de la colonne devrait marcher.

    [edit] autres suggestions pour ton code:
    - "from __future__ import division" est inutile avec Python 3
    - les importations avec "import *" sont à éviter
    - avec PyQt5, les arguments de super sont inutiles: super().__init__( parent) suffit
    - je ne comprends pas "app.MainLoop()": la boucle de traitement des évènements est "app.exec_()"

  7. #7
    Membre du Club
    Homme Profil pro
    débutant
    Inscrit en
    Février 2012
    Messages
    88
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : débutant
    Secteur : Alimentation

    Informations forums :
    Inscription : Février 2012
    Messages : 88
    Points : 56
    Points
    56
    Par défaut
    Très bien, j'ai résolu mes petits problèmes.
    J'ai bien pris note de tes conseils, et je vais modifier mon code.
    Encore un grand merci.

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

Discussions similaires

  1. Saisie d'une date dans une cellule a l'aide d'un Calendrier
    Par jfontaine dans le forum Contribuez
    Réponses: 4
    Dernier message: 04/03/2013, 18h45
  2. VBA - Limiter la saisie d'une seule virgule ou point dans une TextBox
    Par natab dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 09/08/2011, 13h00
  3. lire une cellule dans une DBGrid
    Par flo74 dans le forum Bases de données
    Réponses: 10
    Dernier message: 30/01/2006, 08h52
  4. Réponses: 8
    Dernier message: 15/07/2005, 14h23
  5. Modifier Font d'une cellule dans Excel
    Par nmathon dans le forum API, COM et SDKs
    Réponses: 2
    Dernier message: 27/05/2005, 14h42

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