Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. SubClassing QLineEdit & QDialog
Forum Updated to NodeBB v4.3 + New Features

SubClassing QLineEdit & QDialog

Scheduled Pinned Locked Moved Solved General and Desktop
12 Posts 2 Posters 2.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ModelTechM Offline
    ModelTechM Offline
    ModelTech
    wrote on last edited by
    #1

    I am trying to make a generic name editor/dialog for my application. I want to do this by subclassing QLineEdit and QDialog, where the text entered by the user should satisfy a QRegularExpression and certain existing other names are not allowed. I thought to do this with the following code, but there is not any effect on the Ok button being enabled or not / the color of the text. Any ideas of what is wrong? I would guess that something is not right with the reimplemented methods, but I actually have no clue at all...

    QRegularExpression NameExp("^_?([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_|:|.|\\-)*$");
    
    class NameEditor : public QLineEdit {
    
    public:
        NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
        void textChanged(const QString &);
        bool hasAcceptableInput();
    
    private:
        QStringList Excludes;
        bool Valid;
    };
    
    NameEditor::NameEditor(const QString &Name, QStringList &E, QWidget *Parent) : QLineEdit(Name, Parent) {
    
        Excludes = E;
        Valid = true;
        setValidator(new QRegularExpressionValidator(NameExp));
    }
    
    void NameEditor::textChanged(const QString &Text) {
    
        QPalette *Palette = new QPalette;
        Palette->setColor(QPalette::Text, Qt::black);
        setPalette(*Palette);
        Valid = true;
    
        if (QLineEdit::hasAcceptableInput())
            for (int i = 0; Valid && (i != Excludes.size()); i++)
                if (Excludes[i] == Text) {
                    QPalette *Palette = new QPalette;
                    Palette->setColor(QPalette::Text, Qt::red);
                    setPalette(*Palette);
                    Valid = false;
                }
    
        QLineEdit::textChanged(Text);
    }
    
    bool NameEditor::hasAcceptableInput() { return Valid; }
    
    raven-worxR 1 Reply Last reply
    0
    • ModelTechM ModelTech

      I am trying to make a generic name editor/dialog for my application. I want to do this by subclassing QLineEdit and QDialog, where the text entered by the user should satisfy a QRegularExpression and certain existing other names are not allowed. I thought to do this with the following code, but there is not any effect on the Ok button being enabled or not / the color of the text. Any ideas of what is wrong? I would guess that something is not right with the reimplemented methods, but I actually have no clue at all...

      QRegularExpression NameExp("^_?([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_|:|.|\\-)*$");
      
      class NameEditor : public QLineEdit {
      
      public:
          NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
          void textChanged(const QString &);
          bool hasAcceptableInput();
      
      private:
          QStringList Excludes;
          bool Valid;
      };
      
      NameEditor::NameEditor(const QString &Name, QStringList &E, QWidget *Parent) : QLineEdit(Name, Parent) {
      
          Excludes = E;
          Valid = true;
          setValidator(new QRegularExpressionValidator(NameExp));
      }
      
      void NameEditor::textChanged(const QString &Text) {
      
          QPalette *Palette = new QPalette;
          Palette->setColor(QPalette::Text, Qt::black);
          setPalette(*Palette);
          Valid = true;
      
          if (QLineEdit::hasAcceptableInput())
              for (int i = 0; Valid && (i != Excludes.size()); i++)
                  if (Excludes[i] == Text) {
                      QPalette *Palette = new QPalette;
                      Palette->setColor(QPalette::Text, Qt::red);
                      setPalette(*Palette);
                      Valid = false;
                  }
      
          QLineEdit::textChanged(Text);
      }
      
      bool NameEditor::hasAcceptableInput() { return Valid; }
      
      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @ModelTech
      you are calling QLineEdit::hasAcceptableInput() (in the base class).
      You reimplemented this method in your derived class, but this method isn't virtual, so side effects can be expected.
      Rename the method and call it directly in your derived implementation.

      Also it may be confusing to call it with the base class namespace. Call it with this->hasAcceptableInput() instead. So it will refer to the validator you set on the line edit.

      You mixing up 2 different things in your implementation.

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      1
      • ModelTechM Offline
        ModelTechM Offline
        ModelTech
        wrote on last edited by
        #3

        Thanks for your hint! I will take make the changes you propose.

        1 Reply Last reply
        0
        • ModelTechM Offline
          ModelTechM Offline
          ModelTech
          wrote on last edited by ModelTech
          #4

          I think I have followed your suggestion (plus storing of the two possible palettes) by changing the code as shown below and using isValid in my application instead of hasAcceptableInput. However, there is no difference in behavior... Other ideas of what might be wrong?

          QRegularExpression NameExp("^_?([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_|:|.|\\-)*$");
          
          class NameEditor : public QLineEdit {
          
          public:
              NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
              void textChanged(const QString &);
              bool isValid();
          
          private:
              QStringList Excludes;
              QPalette *Black;
              QPalette *Red;
              bool Valid;
          };
          
          NameEditor::NameEditor(const QString &Name, QStringList &E, QWidget *Parent) : QLineEdit(Name, Parent) {
          
              Excludes = E;
              Black = new QPalette;
              Black->setColor(QPalette::Text, Qt::black);
              Red = new QPalette;
              Red->setColor(QPalette::Text, Qt::black);
              Valid = true;
              setValidator(new QRegularExpressionValidator(NameExp));
          }
          
          void NameEditor::textChanged(const QString &Text) {
          
              setPalette(*Black);
              Valid = true;
          
              if (hasAcceptableInput())
                  for (int i = 0; Valid && (i != Excludes.size()); i++)
                      if (Excludes[i] == Text) {
                          setPalette(*Red);
                          Valid = false;
                      }
          }
          
          bool NameEditor::isValid() { return Valid; }
          
          raven-worxR 1 Reply Last reply
          0
          • ModelTechM ModelTech

            I think I have followed your suggestion (plus storing of the two possible palettes) by changing the code as shown below and using isValid in my application instead of hasAcceptableInput. However, there is no difference in behavior... Other ideas of what might be wrong?

            QRegularExpression NameExp("^_?([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_|:|.|\\-)*$");
            
            class NameEditor : public QLineEdit {
            
            public:
                NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                void textChanged(const QString &);
                bool isValid();
            
            private:
                QStringList Excludes;
                QPalette *Black;
                QPalette *Red;
                bool Valid;
            };
            
            NameEditor::NameEditor(const QString &Name, QStringList &E, QWidget *Parent) : QLineEdit(Name, Parent) {
            
                Excludes = E;
                Black = new QPalette;
                Black->setColor(QPalette::Text, Qt::black);
                Red = new QPalette;
                Red->setColor(QPalette::Text, Qt::black);
                Valid = true;
                setValidator(new QRegularExpressionValidator(NameExp));
            }
            
            void NameEditor::textChanged(const QString &Text) {
            
                setPalette(*Black);
                Valid = true;
            
                if (hasAcceptableInput())
                    for (int i = 0; Valid && (i != Excludes.size()); i++)
                        if (Excludes[i] == Text) {
                            setPalette(*Red);
                            Valid = false;
                        }
            }
            
            bool NameEditor::isValid() { return Valid; }
            
            raven-worxR Offline
            raven-worxR Offline
            raven-worx
            Moderators
            wrote on last edited by
            #5

            @ModelTech
            there is no appearance of a button in the code you've posted?
            How do you try to en-/disable the button.

            Also you should rename your textChanged() to something like onTextChanged() and declare it as slot. Since you overwriting QLineEdit's signal.

            In the constructor then do this:

            connect( this, SIGNAL(textChanged(QString)), this, SLOT(onTextChanged(QString)) );
            

            I assume this is what you want to do.

            --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
            If you have a question please use the forum so others can benefit from the solution in the future

            1 Reply Last reply
            0
            • ModelTechM Offline
              ModelTechM Offline
              ModelTech
              wrote on last edited by
              #6

              Of course, I want to use my NameEditor as Wiget in a bigger context, where such slots will exist. I want to use my NameEditor in various other widgets, where there may not be a button to take over the input (in such cases, it should be taken over on text change or on focus out) or there may indeed be a button. To exemplify the latter, I also created a NameDialog with Ok & Cancel buttons using the NameEditor. The code for it is shown below, but it does not behave as I hoped...

              The behavior of this NameDialog should be as follows. The NameEditor validator should ensure that the input satisfies the NameExp and change the color of the text to red if a string is entered that is included in the Excludes list. The Ok button of the NameDialog should only be enabled if the NameEditor has valid input (i.e., satisfying the NameExp and not being in the Excludes list).

              class NameDialog : public QDialog {
              
                  Q_OBJECT
              
              public:
                  NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                  QString name() const;
              
              private slots:
                  void textChanged();
              
              private:
                  NameEditor *Editor;
                  QDialogButtonBox *Buttons;
              };
              
              NameDialog::NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent) : QDialog(Parent) {
              
                  QFormLayout *Top = new QFormLayout;
                  Editor = new NameEditor(Name, Excludes, this);
                  connect(Editor, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
                  Top->addRow(tr("%1 Name:").arg(ItemName), Editor);
                  Buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
                  connect(Buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept()));
                  connect(Buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
                  QVBoxLayout *Layout = new QVBoxLayout;
                  Layout->addLayout(Top);
                  Layout->addWidget(Buttons);
                  setLayout(Layout);
                  setWindowTitle(tr("Rename %1").arg(ItemName));
              }
              
              QString NameDialog::name() const { return Editor->text(); }
              
              void NameDialog::textChanged() {
              
                  Buttons->button(QDialogButtonBox::Ok)->setEnabled(Editor->isValid());
              }
              
              raven-worxR 1 Reply Last reply
              0
              • ModelTechM ModelTech

                Of course, I want to use my NameEditor as Wiget in a bigger context, where such slots will exist. I want to use my NameEditor in various other widgets, where there may not be a button to take over the input (in such cases, it should be taken over on text change or on focus out) or there may indeed be a button. To exemplify the latter, I also created a NameDialog with Ok & Cancel buttons using the NameEditor. The code for it is shown below, but it does not behave as I hoped...

                The behavior of this NameDialog should be as follows. The NameEditor validator should ensure that the input satisfies the NameExp and change the color of the text to red if a string is entered that is included in the Excludes list. The Ok button of the NameDialog should only be enabled if the NameEditor has valid input (i.e., satisfying the NameExp and not being in the Excludes list).

                class NameDialog : public QDialog {
                
                    Q_OBJECT
                
                public:
                    NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                    QString name() const;
                
                private slots:
                    void textChanged();
                
                private:
                    NameEditor *Editor;
                    QDialogButtonBox *Buttons;
                };
                
                NameDialog::NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent) : QDialog(Parent) {
                
                    QFormLayout *Top = new QFormLayout;
                    Editor = new NameEditor(Name, Excludes, this);
                    connect(Editor, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
                    Top->addRow(tr("%1 Name:").arg(ItemName), Editor);
                    Buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
                    connect(Buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept()));
                    connect(Buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
                    QVBoxLayout *Layout = new QVBoxLayout;
                    Layout->addLayout(Top);
                    Layout->addWidget(Buttons);
                    setLayout(Layout);
                    setWindowTitle(tr("Rename %1").arg(ItemName));
                }
                
                QString NameDialog::name() const { return Editor->text(); }
                
                void NameDialog::textChanged() {
                
                    Buttons->button(QDialogButtonBox::Ok)->setEnabled(Editor->isValid());
                }
                
                raven-worxR Offline
                raven-worxR Offline
                raven-worx
                Moderators
                wrote on last edited by raven-worx
                #7

                @ModelTech
                but as i said. Rename it to onTextChanged() since you are overwriting the signal with the same name from the base class.
                Currently you are preventing the class' MetaObject emitting the QLineEdit's textChanged() signal.

                --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                If you have a question please use the forum so others can benefit from the solution in the future

                1 Reply Last reply
                1
                • ModelTechM Offline
                  ModelTechM Offline
                  ModelTech
                  wrote on last edited by
                  #8

                  Oh, of course. Apologies, I overlooked that... So, I made the changes below, but no difference in behavior. I feel quite stupid by now...

                  Changes to NameEditor:

                  class NameEditor : public QLineEdit {
                  
                      Q_OBJECT
                  
                  signals:
                      void onTextChanged(const QString &);
                  
                  public:
                      NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                      void textChanged(const QString &);
                      bool isValid();
                  
                  private:
                      QStringList Excludes;
                      QPalette *Black;
                      QPalette *Red;
                      bool Valid;
                  };
                  
                  void NameEditor::textChanged(const QString &Text) {
                  
                      setPalette(*Black);
                      Valid = true;
                  
                      if (hasAcceptableInput())
                          for (int i = 0; Valid && (i != Excludes.size()); i++)
                              if (Excludes[i] == Text) {
                                  setPalette(*Red);
                                  Valid = false;
                              }
                  
                      emit onTextChanged(Text);
                  }
                  

                  Changes to NameDialog:

                  NameDialog::NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent) : QDialog(Parent) {
                  
                      QFormLayout *Top = new QFormLayout;
                      Editor = new NameEditor(Name, Excludes, this);
                      connect(Editor, SIGNAL(onTextChanged(QString)), this, SLOT(textChanged()));
                      Top->addRow(tr("%1 Name:").arg(ItemName), Editor);
                      Buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
                      connect(Buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept()));
                      connect(Buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
                      QVBoxLayout *Layout = new QVBoxLayout;
                      Layout->addLayout(Top);
                      Layout->addWidget(Buttons);
                      setLayout(Layout);
                      setWindowTitle(tr("Rename %1").arg(ItemName));
                  }
                  
                  raven-worxR 1 Reply Last reply
                  0
                  • ModelTechM ModelTech

                    Oh, of course. Apologies, I overlooked that... So, I made the changes below, but no difference in behavior. I feel quite stupid by now...

                    Changes to NameEditor:

                    class NameEditor : public QLineEdit {
                    
                        Q_OBJECT
                    
                    signals:
                        void onTextChanged(const QString &);
                    
                    public:
                        NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                        void textChanged(const QString &);
                        bool isValid();
                    
                    private:
                        QStringList Excludes;
                        QPalette *Black;
                        QPalette *Red;
                        bool Valid;
                    };
                    
                    void NameEditor::textChanged(const QString &Text) {
                    
                        setPalette(*Black);
                        Valid = true;
                    
                        if (hasAcceptableInput())
                            for (int i = 0; Valid && (i != Excludes.size()); i++)
                                if (Excludes[i] == Text) {
                                    setPalette(*Red);
                                    Valid = false;
                                }
                    
                        emit onTextChanged(Text);
                    }
                    

                    Changes to NameDialog:

                    NameDialog::NameDialog(const QString &ItemName, const QString &Name, QStringList &Excludes, QWidget *Parent) : QDialog(Parent) {
                    
                        QFormLayout *Top = new QFormLayout;
                        Editor = new NameEditor(Name, Excludes, this);
                        connect(Editor, SIGNAL(onTextChanged(QString)), this, SLOT(textChanged()));
                        Top->addRow(tr("%1 Name:").arg(ItemName), Editor);
                        Buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
                        connect(Buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept()));
                        connect(Buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject()));
                        QVBoxLayout *Layout = new QVBoxLayout;
                        Layout->addLayout(Top);
                        Layout->addWidget(Buttons);
                        setLayout(Layout);
                        setWindowTitle(tr("Rename %1").arg(ItemName));
                    }
                    
                    raven-worxR Offline
                    raven-worxR Offline
                    raven-worx
                    Moderators
                    wrote on last edited by
                    #9

                    @ModelTech
                    Still not correct. It seems you have problems understanding the difference between SINGALS and SLOTS, since you mix them up in your code.

                    signals:
                        void onTextChanged(const QString &);
                    
                    public:
                        NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                        void textChanged(const QString &);
                        bool isValid();
                    

                    i said RENAME your textChanged() to onTextChanged(). And make sure its a SLOT.
                    You introduced a new signal onTextChanged(). And still left the old textChanged() ... which still overrides QLineEdit's same named signal.

                    Ok i (exceptionally) do it for you (including some small enhancements):

                    class NameEditor : public QLineEdit {
                    
                        Q_OBJECT
                    
                    public:
                        NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                        bool isValid();
                    
                    signals:
                         void validChanged(bool);
                    
                    protected slots:
                        void onTextChanged(const QString &);
                    
                    private:
                        QStringList Excludes;
                        QPalette *Black;
                        QPalette *Red;
                        bool Valid;
                    };
                    
                    void NameEditor::onTextChanged(const QString &Text)
                    {
                        bool validityCheck = true;
                    
                        if (hasAcceptableInput())
                        {
                            for (int i = 0; Valid && (i != Excludes.size()); i++)
                                if (Excludes[i] == Text) {
                                    validityCheck = false;
                                    break;
                                }
                        }
                        else
                            validityCheck = false;
                    
                        if( Valid != validityCheck )
                        {
                              Valid = validityCheck;
                              emit validChanged(Valid);
                    
                              if( Valid )
                                     setPalette(*Black);
                              else
                                     setPalette(*Red);
                        }
                    }
                    

                    Also you should replace the line from

                    connect(Editor, SIGNAL(onTextChanged(QString)), this, SLOT(textChanged()));
                    

                    to

                    connect(Editor, SIGNAL(validChanged(bool)), this, SLOT(textChanged()));  // you may want to rename the slot to something like "onValidChanged(bool)"
                    

                    or even to this (direct approach) and remove your current textChanged() slot in the dialog implementation:

                    connect(Editor, SIGNAL(validChanged(bool)), Buttons->button(QDialogButtonBox::Ok), SLOT(setEnabled(bool)));
                    

                    --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                    If you have a question please use the forum so others can benefit from the solution in the future

                    1 Reply Last reply
                    1
                    • ModelTechM Offline
                      ModelTechM Offline
                      ModelTech
                      wrote on last edited by ModelTech
                      #10

                      That shows what a Qt novice I am... A big thank you for pointing me to my error!! A also appreciate the additional enhancements as they gave me some inspiration for improvements elsewhere in my code.

                      Based on your example code, I have been able to fix my situation (in a slightly different way).

                      raven-worxR 1 Reply Last reply
                      0
                      • ModelTechM ModelTech

                        That shows what a Qt novice I am... A big thank you for pointing me to my error!! A also appreciate the additional enhancements as they gave me some inspiration for improvements elsewhere in my code.

                        Based on your example code, I have been able to fix my situation (in a slightly different way).

                        raven-worxR Offline
                        raven-worxR Offline
                        raven-worx
                        Moderators
                        wrote on last edited by
                        #11

                        @ModelTech
                        some more hints for the future:

                        • it's good practice to name slots onXXX()
                        • while naming members make sure they are not already defined in the base class to avoid unexpected behaviors (unless they are virtual and you intent to do so)

                        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                        If you have a question please use the forum so others can benefit from the solution in the future

                        1 Reply Last reply
                        0
                        • ModelTechM Offline
                          ModelTechM Offline
                          ModelTech
                          wrote on last edited by ModelTech
                          #12

                          Thanks, that is a very useful advice. Perhaps for completeness, my solution:

                          class NameEditor : public QLineEdit {
                          
                              Q_OBJECT
                          
                          signals:
                              void validChange(bool);
                          
                          public:
                              NameEditor(const QString &Name, QStringList &Excludes, QWidget *Parent = 0);
                          
                          private slots:
                              void onChange(const QString &);
                          
                          private:
                              QStringList Excludes;
                              QPalette *Black;
                              QPalette *Red;
                              bool Valid;
                          };
                          
                          NameEditor::NameEditor(const QString &Name, QStringList &E, QWidget *Parent) : QLineEdit(Name, Parent) {
                          
                              Excludes = E;
                              Black = new QPalette;
                              Black->setColor(QPalette::Text, Qt::black);
                              Red = new QPalette;
                              Red->setColor(QPalette::Text, Qt::red);
                              Valid = true;
                              setValidator(new QRegularExpressionValidator(NameExp));
                              connect(this, SIGNAL(textChanged(QString)), this, SLOT(onChange(QString)));
                          }
                          
                          void NameEditor::onChange(const QString &Text) {
                          
                              Valid = hasAcceptableInput();
                          
                              for (int i = 0; Valid && (i != Excludes.size()); i++)
                                  Valid = Excludes[i] != Text;
                          
                              if (Valid)
                                  setPalette(*Black);
                              else
                                  setPalette(*Red);
                          
                              emit validChange(Valid);
                          }
                          
                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved