Partage
  • Partager sur Facebook
  • Partager sur Twitter

Empecher la validation d'un QDialog

Sujet résolu
    13 mai 2022 à 6:29:51

    Salut

    J'ai repris un projet que j'avais commencé il y'a maintenant 02 ans . J'ai une classe definie comme suit CustomDialogBox:

    #ifndef CUSTOMDIALOGBOX_H
    #define CUSTOMDIALOGBOX_H
    
    #include <QFormLayout>
    #include <QLabel>
    #include <QLineEdit>
    #include <QObject>
    #include <QPushButton>
    #include <QString>
    #include <QWidget>
    
    class CustomDialogBox : public QWidget
    {
        Q_OBJECT
    public:
        CustomDialogBox(QWidget* parent = 0);
        ~CustomDialogBox();
    
    private:
        QFormLayout* layout;
        QLabel* l_adr;
        QLabel* l_nom;
        QLabel* l_num;
        QLineEdit* adr;
        QLineEdit* nom;
        QLineEdit* num;
        QPushButton* valid;
    
    private slots:
        void saveOnFile();
    };
    
    #endif // CUSTOMDIALOGBOX_H
    

    Mon constructeur:

    CustomDialogBox::CustomDialogBox(QWidget *parent)
        : QWidget(parent)
    {
        l_nom =  new QLabel(tr("Nom du Client : "));
        l_num =  new QLabel(tr("Numero de Chambre reservée : "));;
        l_adr = new QLabel(tr("Adresse du Client : "));
    
        nom = new QLineEdit;
        num = new QLineEdit;
        adr = new QLineEdit;
        num->setInputMask("009");   // 'ddd' pour ne recevoir que des chiffres, au trop 03
        adr->setInputMask("999 999 999");
        nom->setMinimumWidth(50);
        num->setMinimumWidth(50);
        adr->setMinimumWidth(50);
    
        valid = new QPushButton(tr("Ajouter"));
    
        layout = new QFormLayout;
        layout->addRow(l_nom, nom);
        layout->addRow(l_num, num);
        layout->addRow(l_adr, adr);
        layout->addRow(valid);
    
        setLayout(layout);
        setWindowTitle(tr("Ajout d'une reservation"));
        setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint);
    
        connect(valid, SIGNAL(clicked()), this, SLOT(saveOnFile()));
    }

    Et ma fonction svaeOnFile():

    void CustomDialogBox::saveOnFile()
    {
        QFile fichier("./Data/reserveChm.txt");
        if (fichier.open(QIODevice::WriteOnly | QIODevice::Text | QFile::Append)) {
            QTextStream out(&fichier);
            out << nom->text() << "|"
                << num->text() << "|"
                << QDateTime::currentDateTime().toString() << "|"
                << adr->text() << "\n";
        }
        else{
            QMessageBox::critical(this, tr("Problème rencontré"), tr("Une erreur est survenu lors de l'ouverture du fichier"));
        }
    }

    Je tiens a rappeler que le code est tel qu'il etait il y'a deux ans, je suis vraiment ouvert a des moyens pour son amelioration.

    Je veux que la fonction saveOnFile() s'execute uniquement si les QLineEdit sont tous remplis. Je voulais mettre une condition avant l'enregistrement directement dans saveOnFile qui sera du genre:

    if (nom->text()->length() == 0 || num->text()->length() == 0 || ...)
        QMessageBox::critical(this, tr("Enregistrement impossible"), tr("Veuillez remplir tous les champs de ce formulaire");
    

    Mais je veux que ce soit fait dans le CustomWidget et non dans ma fonction. Et si possible que l'application affiche elle meme le message mais pas que moi je le fasse. Vous avez donc une idee pour obliger ce remplissage avant que le bouton ne soit meme disponible (donc il sera grisé au debut) ?

    Je suis pas de cours en Qt, l'IDE meme QtCreator a une riche documentation et je me sers juste de ca. Si vous avez des cours en "francais" dessus je suis aussi partant.

    Merci.



    • Partager sur Facebook
    • Partager sur Twitter
      13 mai 2022 à 15:49:55

      Salut,

      Ben, selon le bon vieux principe du SRP (Single Responsability Principle), tu devrais ajouter une fonction membre à ta boite de dialogque custom.

      Tu pourrais l'appeler, pour que les gens puissent comprendre ce qu'elle fait, par exemple checkIt ou pouquoi pas checkFields, voire même isDialogReadyToSave et il serait sans doute pas mal qu'elle renvoie un booléen dont la valeur serait à true si tous les champs sont remplis et à false dans le cas contraire.

      Elle pourrait donc tout à  fait ressembler à quelque chose comme

      bool CustomDialogBox::checkIt(){
          /* je vais, volontairement, passer toutes les étapes en
           * revue, pour qu'il ne puisse y avoir aucun doute sur ce
           * qui est fait 
           */
          bool result{true}; // on considère de base que tout est correct
          if(adr.text().empty()) // si l'adresse est vide
              result = false; // ca va pas le faire
          if(nom.text().empty()) // si le nom est vide
              result = false; // ca le fait pas non plus
          if(num.tex().empty()) // et enfin, si le numéro est vide
              result = false;   // ca n'ira pas d'avantage
          return result;
      }

      Tu peux parfaitement déclarer cette fonction dans l'accessibilité privée, car il n'y a clairement que ton dialogue custom (et plus précisément la fonction saveOnFile) qui devra y accéder... Il n'y a donc pas besoin de l'exposer "au tout venant" ;)

      L'idée de cette fonction est toute simple dans le principe: on part du principe que tout est correct ... jusqu'à preuve du contraire.

      Si l'on croise la preuve que "quelque chose n'est pas correct", alors, result vaudra automatiquement faux, et nous saurons que l'enregistrement ne peut pas se faire ;)

      Puis, une fois que tu as cette fantastique manière de savoir si tous les champs sont prêts à être enregistrer, tu  pourra modifier ta fonction saveOnFile de manière à ce qu'elle tienne compte de te "check", sous une forme proche de

      oid CustomDialogBox::saveOnFile()
      {
          if(checkIt()){ // si le check est validé
              QFile fichier("./Data/reserveChm.txt");
              if (fichier.open(QIODevice::WriteOnly | 
                  QIODevice::Text | QFile::Append)) {
                  QTextStream out(&fichier);
                  out << nom->text() << "|"
                      << num->text() << "|"
                      << QDateTime::currentDateTime().toString() << "|"
                      << adr->text() << "\n";
              }
              else{
                  QMessageBox::critical(this, tr("Problème rencontré"), tr("Une erreur est survenu lors de l'ouverture du fichier"));
              }
          } 
      // Si tu y tiens, tu peux rajouter un message d'erreur indiquant qu'il faut remplir tous les champs< e:se{ QMessageBox::critical(this, tr("Données manquantes"),tr("L'enregistrement n'a pas été fait car des données sont manquantes")); }

      Et le tour sera joué ;)

      NOTA : nous pourrions même aller plus loin dans le respect du SRP en nous disant que la fonction saveOnFile ne fait que mettre le check (la fonction checkIt) et la possibilité de réellement effectuer la sauvegarde (qui prendrait la forme d'une fonction nommée reallySaveOnFile, par exemple) pour que cette dernière n'ai vraiment à s'inquiéter de la possibilité "physique" d'enregistrement.

      Ainsi, le code d'origine de ta fonction saveOnFile deviendrait le code de la fonction reallySaveOnFile (qui pourrait elle aussi être déclarée dans l'accessibilité privée), sous la forme de

      void CustomDialogBox::reallySaveOnFile()
      {
          QFile fichier("./Data/reserveChm.txt");
          if (fichier.open(QIODevice::WriteOnly | QIODevice::Text | QFile::Append)) {
              QTextStream out(&fichier);
              out << nom->text() << "|"
                  << num->text() << "|"
                  << QDateTime::currentDateTime().toString() << "|"
                  << adr->text() << "\n";
          }
          else{
              QMessageBox::critical(this, tr("Problème rencontré"), tr("Une erreur est survenu lors de l'ouverture du fichier"));
          }
      }

      et le code de ta fonction saveOnFile prendrait la forme de

      void CustomDialogBox::saveOnFile()
      {
          if(checkIt())  // si le check et concluant
              reallySaveOnFile(); // on essaye effectivement 
                                  // d'enregistrer les données
          else  // tu ajoute ce message ... ou non, selon tes gouts 
              QMessageBox::critical(this, tr("Données manquantes"),tr("L'enregistrement n'a pas été fait car des données sont manquantes"));
      }

      L'idée derrière tout cet exercice est de prendre simplement l'habitude de ne t'inquiéter d'un seul problème à la fois.  Tu peux te le permettre ici car tout sera de toutes manières "piloté" par l'interface graphique, et que, même s'il devait y avoir une légère perte de performances du fait des différents appels de fonctions (ce qui devrait encore être prouvé ;) ) le temps de réaction de l'utilisateur ferait que cela ne change au final absolument rien , car on a quoi? peut être 0.0003 secondes de délais du à l'appel d'une fonction supplémentaire? Comparés au 0.2 secondes qui seront de toutes manières indispensable pour que le disque dur trouve l'endroit où l'écriture pourra se faire et pour qu'il la fasse effectivement, cela ne peut pas avoir d'influence néfaste ;)

      D'un autre coté, je dois avouer que cette deuxième approche est peut-être un peu exagérée et "jusqu'au bouliste", car elle n'apporte pas grand chose à la première solution présentée... Quoi que ... ? :D

      EDIT:

      Note au passage que l'on pourrait améliorer la logique de la fonction checkIt en lui donnant la forme de

      bool CustomDialogBox::checkIt(){
          return (! adr.text().empty() &&
                  ! nom.text().empty() &&
                  ! num.text().empty());

      car l'opérateur && (ainsi que l'opérateur || ) est ce que l'on appelle un opérateur "optimisé", c'est à dire qu'il ne va évaluer l'opérande de droite que si le résultat final peut encore être modifié.

      Cela signifie que, dans le cas présent, le test va s'assurer que adr n'est pas vide et que nom ne sera testé que... si adr n'est pas vide (car, à ce moment là, le "status" de nom pourra encore changer la donne) et que, de même, num ne sera testé que si nom (et donc, forcément, adr) n'est pas vide.

      Autrement dit, cela permet de s'assurer qu'il n'y aura -- au pire -- jamais qu'un seul champs vide de testé, alors que la première version de la fonction les vérifiera tous.

      Mais, encore une fois, dans ce cadre bien précis, tu ne verras sans doute absolument aucun avantage à utiliser cette notation, car le temps du test est tellement faible par rapport au reste que tu n'en verra pas la différence ;)

      -
      Edité par koala01 13 mai 2022 à 16:11:28

      • Partager sur Facebook
      • Partager sur Twitter
      Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
        13 mai 2022 à 17:18:14

        Merci.

        Et pour "Et si possible que l'application affiche elle meme le message mais pas que moi je le fasse." je compte utiliser QToolTip, a force de fouiller la doc j'ai trouvé. J'aurai qu'a définir un pour chaque champ et l'afficher sur les champs vides a la "soumission" sans faire appel a QMessageBox.

        Bon je ferais différents test, merci encore.

        • Partager sur Facebook
        • Partager sur Twitter
          13 mai 2022 à 17:29:36

          Un dialog qui ouvre un autre dialog, je dirais que c'est bof en termes d'UI.

          Mettre en rouge les champs qui ne sont pas remplis + un message pour dire que leur remplissage est obligatoire me semble mieux.

          Autre chose, je pense que c'est pas au dialog de faire l'enregistrement, c'est au code appellant.

          CustomDialogBox box;
          box.open();
          if (box.answer() == ok)
              save(box.infos());

          CustomDialogBox est un nom bof bof, pas tres explicite.

          Et a mon avis, si tu fais ca, cela justifie de créer un custom widget pour le texte + le line edit + le message d'erreur.

          -
          Edité par gbdivers 13 mai 2022 à 17:33:20

          • Partager sur Facebook
          • Partager sur Twitter
            13 mai 2022 à 20:54:04

            gbdivers a écrit:

            Autre chose, je pense que c'est pas au dialog de faire l'enregistrement, c'est au code appellant.

            CustomDialogBox box;
            box.open();
            if (box.answer() == ok)
                save(box.infos());

            CustomDialogBox est un nom bof bof, pas tres explicite.

            Et a mon avis, si tu fais ca, cela justifie de créer un custom widget pour le texte + le line edit + le message d'erreur.

            -
            Edité par gbdivers il y a environ 3 heures

            Je connais pas vraiment cette syntaxe, je precise que c'etait pas vraiment une QDialog. Je vais donc modifier sa structure pour cela.

            gbdivers a écrit:

            Un dialog qui ouvre un autre dialog, je dirais que c'est bof en termes d'UI.

            Mettre en rouge les champs qui ne sont pas remplis + un message pour dire que leur remplissage est obligatoire me semble mieux.


            j'ai aucune idee de comment on fait. Mais je vais chercher apres etre passé de QWidget a QDialog.

            Edit:

            J'ai lu un peu et voila:

                if (fieldsAreValid()){ // C'est pareil checkIt() qui est plus haut
                }
                else {
                    //QMessageBox::critical(this, tr("Enregistrement impossible"), tr("Tous les champs doivent etre remplis"));
                    for (auto &var : getInvalidFields()) { // cette fonction renvoie les champs non remplis
                        var->setStyleSheet("border: 2px solid red");
                        var->setToolTip("Veuillez remplir ce champ"); // Je voulais afficher un petit texte avec mais je comprends toujours pas le fonctionnement de ToolTip, rien ne s'affiche
                    }
                }

            -
            Edité par Asmitta 13 mai 2022 à 22:05:12

            • Partager sur Facebook
            • Partager sur Twitter

            Empecher la validation d'un QDialog

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            • Editeur
            • Markdown