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. Style change of own widget in QTableView

Style change of own widget in QTableView

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 2 Posters 1.4k Views 1 Watching
  • 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.
  • thompsonxT Offline
    thompsonxT Offline
    thompsonx
    wrote on last edited by
    #1

    Hello,
    I implemented a descendant of QLineEdit. The descendant has only one difference from QLineEdit - it overrides focusOutEvent which checks if inserted text is valid (I am using 3rd party library for validation which is not suitable to be applied via own QValidator, thus I perform the validation in that method) and if it is not it changes text color to red (setStyleSheet). I am using this widget within QTableView where it is inserted via setItemDelegateForColumn using own derivate of QItemDelegate which returns the widget from createEditor method.

    Problem is that whenever I add invalid value to a cell with that widget the text color does not change. When I use this widget alone, i.e. outside of QTableView as a single element in some layout, it behaves correctly. Does QTableView suppress own style of inner widgets by default? Or, is there a different problem? Thx for help.

    #include <QLineEdit>
    #include <QFocusEvent>
    
    class ExpressionEdit : public QLineEdit
    {
        Q_OBJECT
    
    public:
        ExpressionEdit(QWidget* parent = nullptr);
        ExpressionEdit(const QString& contents, QWidget* parent = nullptr);
    
    signals:
        void validStateChanged(bool isValid);
    
    protected:
        virtual void focusOutEvent(QFocusEvent* e) override;
    
    private:
        bool mValidState;
    };
    
    ExpressionEdit::ExpressionEdit(QWidget* parent) : QLineEdit(parent)
    {
        this->setText("0");
        this->mValidState = true;
    }
    
    ExpressionEdit::ExpressionEdit(const QString& contents, QWidget* parent) :
        QLineEdit(contents, parent)
    {
        this->mValidState = true;
    }
    
    void ExpressionEdit::focusOutEvent(QFocusEvent* e)
    {
        // 3rd party lib for validation
        bool valid = Expression::isValid(this->text().toStdString(), Common::fnVariables());
    
        if (valid)
        {
            this->setStyleSheet("color: black;");
        }
        else
        {
            this->setStyleSheet("color: red; font-weight: bold;");
        }
    
        if ((mValidState == true && valid == false)
                || (mValidState == false && valid == true))
        {
            emit validStateChanged(valid);
            this->mValidState = valid;
        }
    
        QLineEdit::focusOutEvent(e);
    }
    
    #include <QItemDelegate>
    #include "expressionedit.h"
    
    class ExpressionEditDelegate: public QItemDelegate
    {
        Q_OBJECT
    
    public:
        ExpressionEditDelegate(QObject* parent = nullptr);
    
    signals:
        void validStateChanged(bool valid);
    
    private slots:
        void changeValidState(bool valid);
    
    protected:
        QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
        void setEditorData(QWidget * editor, const QModelIndex & index) const override;
        void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
        void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
    };
    
    ExpressionEditDelegate::ExpressionEditDelegate(QObject *parent) : QItemDelegate(parent)
    {
    }
    
    QWidget* ExpressionEditDelegate::createEditor(QWidget *parent,
                                        const QStyleOptionViewItem &option,
                                        const QModelIndex &index) const
    {
        ExpressionEdit *editor = new ExpressionEdit(parent);
    
        connect(editor, &ExpressionEdit::validStateChanged, this, &ExpressionEditDelegate::changeValidState);
    
        return editor;
    }
    
    
    void ExpressionEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        QString value = index.model()->data(index, Qt::EditRole).toString();
        ExpressionEdit *line = static_cast<ExpressionEdit*>(editor);
        line->setText(value);
    }
    
    void ExpressionEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
    {
        ExpressionEdit *line = static_cast<ExpressionEdit*>(editor);
        QString value = line->text();
        model->setData(index, value);
    }
    
    void ExpressionEditDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
        editor->setGeometry(option.rect);
    }
    
    void ExpressionEditDelegate::changeValidState(bool valid)
    {
        emit validStateChanged(valid);
    }
    
    // Delegate assigned to QTableView
    ui->table->setItemDelegateForColumn(2, new ExpressionEditDelegate(this));
    
    raven-worxR 1 Reply Last reply
    0
    • thompsonxT thompsonx

      Hello,
      I implemented a descendant of QLineEdit. The descendant has only one difference from QLineEdit - it overrides focusOutEvent which checks if inserted text is valid (I am using 3rd party library for validation which is not suitable to be applied via own QValidator, thus I perform the validation in that method) and if it is not it changes text color to red (setStyleSheet). I am using this widget within QTableView where it is inserted via setItemDelegateForColumn using own derivate of QItemDelegate which returns the widget from createEditor method.

      Problem is that whenever I add invalid value to a cell with that widget the text color does not change. When I use this widget alone, i.e. outside of QTableView as a single element in some layout, it behaves correctly. Does QTableView suppress own style of inner widgets by default? Or, is there a different problem? Thx for help.

      #include <QLineEdit>
      #include <QFocusEvent>
      
      class ExpressionEdit : public QLineEdit
      {
          Q_OBJECT
      
      public:
          ExpressionEdit(QWidget* parent = nullptr);
          ExpressionEdit(const QString& contents, QWidget* parent = nullptr);
      
      signals:
          void validStateChanged(bool isValid);
      
      protected:
          virtual void focusOutEvent(QFocusEvent* e) override;
      
      private:
          bool mValidState;
      };
      
      ExpressionEdit::ExpressionEdit(QWidget* parent) : QLineEdit(parent)
      {
          this->setText("0");
          this->mValidState = true;
      }
      
      ExpressionEdit::ExpressionEdit(const QString& contents, QWidget* parent) :
          QLineEdit(contents, parent)
      {
          this->mValidState = true;
      }
      
      void ExpressionEdit::focusOutEvent(QFocusEvent* e)
      {
          // 3rd party lib for validation
          bool valid = Expression::isValid(this->text().toStdString(), Common::fnVariables());
      
          if (valid)
          {
              this->setStyleSheet("color: black;");
          }
          else
          {
              this->setStyleSheet("color: red; font-weight: bold;");
          }
      
          if ((mValidState == true && valid == false)
                  || (mValidState == false && valid == true))
          {
              emit validStateChanged(valid);
              this->mValidState = valid;
          }
      
          QLineEdit::focusOutEvent(e);
      }
      
      #include <QItemDelegate>
      #include "expressionedit.h"
      
      class ExpressionEditDelegate: public QItemDelegate
      {
          Q_OBJECT
      
      public:
          ExpressionEditDelegate(QObject* parent = nullptr);
      
      signals:
          void validStateChanged(bool valid);
      
      private slots:
          void changeValidState(bool valid);
      
      protected:
          QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
          void setEditorData(QWidget * editor, const QModelIndex & index) const override;
          void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
          void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
      };
      
      ExpressionEditDelegate::ExpressionEditDelegate(QObject *parent) : QItemDelegate(parent)
      {
      }
      
      QWidget* ExpressionEditDelegate::createEditor(QWidget *parent,
                                          const QStyleOptionViewItem &option,
                                          const QModelIndex &index) const
      {
          ExpressionEdit *editor = new ExpressionEdit(parent);
      
          connect(editor, &ExpressionEdit::validStateChanged, this, &ExpressionEditDelegate::changeValidState);
      
          return editor;
      }
      
      
      void ExpressionEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
      {
          QString value = index.model()->data(index, Qt::EditRole).toString();
          ExpressionEdit *line = static_cast<ExpressionEdit*>(editor);
          line->setText(value);
      }
      
      void ExpressionEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
      {
          ExpressionEdit *line = static_cast<ExpressionEdit*>(editor);
          QString value = line->text();
          model->setData(index, value);
      }
      
      void ExpressionEditDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
      {
          editor->setGeometry(option.rect);
      }
      
      void ExpressionEditDelegate::changeValidState(bool valid)
      {
          emit validStateChanged(valid);
      }
      
      // Delegate assigned to QTableView
      ui->table->setItemDelegateForColumn(2, new ExpressionEditDelegate(this));
      
      raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      @thompsonx
      what is connected to or what happens on the delegate's validStateChanged signal?
      I guess you are just reopening a new editor? Means a new editor widget instance is created and the set stylesheet is lost.

      The short way would be to do the validation also in the delegates createEditor() method (or even in the custom line edit's focusIn event) or also reimplement the delegates's editorEvent() method and emit QAbstractItemDelegate::closeEditor(editor, QAbstractItemDelegate::EndEditHint) accordingly to the received events. Here the QAbstractItemDelegate::EndEditHint is important.

      --- 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
      2
      • thompsonxT Offline
        thompsonxT Offline
        thompsonx
        wrote on last edited by
        #3

        validStateChanged is used to inform connected elements whether an ExpressionEdit's content is valid or not. I have one dialog window where multiple elements are including single ExpressionEdits, tables with ExpressionEdits and other elements used for edit forms. This signal is used to propagate the information about valid state to parent elements. The signal stops at the element controlling dialog window and this dialog cannot be confirmed until all elements are valid.

        Nevertheless, the validation works correctly. I was asking about the style change inside ExpressionEdit::focusOutEvent(). It does not change when an ExpressionEdit is inside the delegate and table. When it is located in a layout or frame it works completely fine. I guess that QTableView blocks own style of inner elements. How should I change the code to make the style change visible within the table?

        raven-worxR 1 Reply Last reply
        0
        • thompsonxT thompsonx

          validStateChanged is used to inform connected elements whether an ExpressionEdit's content is valid or not. I have one dialog window where multiple elements are including single ExpressionEdits, tables with ExpressionEdits and other elements used for edit forms. This signal is used to propagate the information about valid state to parent elements. The signal stops at the element controlling dialog window and this dialog cannot be confirmed until all elements are valid.

          Nevertheless, the validation works correctly. I was asking about the style change inside ExpressionEdit::focusOutEvent(). It does not change when an ExpressionEdit is inside the delegate and table. When it is located in a layout or frame it works completely fine. I guess that QTableView blocks own style of inner elements. How should I change the code to make the style change visible within the table?

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

          @thompsonx
          try a more specific stylesheet:

          this->setStyleSheet("ExpressionEdit { color: red; font-weight: bold; }");
          

          Nevertheless the default behavior for editor widgets is that they are closed on focus out. Thats why i assumed a new editor instance is reopened/created immediately which of course leads to loosing the old stylesheet set.

          --- 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
          2
          • thompsonxT Offline
            thompsonxT Offline
            thompsonx
            wrote on last edited by
            #5

            Your assumption was correct. After focutOutEvent the ExpressionEdit is deleted and its content is copied to QTableView's model, therefore I could not see the style change since the table creates ExpressionEdit only for editting. For displaying the cell's content it uses delegate's method paint.

            So, I solved it by overriding paint method in my delegate. I moved from ExpressionEdit to QLineEdit because it is not necessary. I perform the validation during painting. Problem solved. Now, I just need to figure out how to inform parents about (in)valid state but that is out of this topic (probably, I will add some method which should be called before dialog confirmation).

            @raven-worx Thank you. If there is a better way to do the cell coloring let me know, otherwise this topic is solved.

            There is how I changed my delegate:

            ExpressionEditDelegate::ExpressionEditDelegate(QObject *parent) : QItemDelegate(parent)
            {
            }
            
            QWidget* ExpressionEditDelegate::createEditor(QWidget *parent,
                                                const QStyleOptionViewItem &option,
                                                const QModelIndex &index) const
            {
                QLineEdit *editor = new QLineEdit(parent);
            
                return editor;
            }
            
            
            void ExpressionEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
            {
                QString value = index.model()->data(index, Qt::EditRole).toString();
                QLineEdit *line = static_cast<QLineEdit*>(editor);
                line->setText(value);
            }
            
            
            void ExpressionEditDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
            {
                QLineEdit *line = static_cast<QLineEdit*>(editor);
                QString value = line->text();
                model->setData(index, value);
            }
            
            
            void ExpressionEditDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
            {
                editor->setGeometry(option.rect);
            }
            
            void ExpressionEditDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
            {
                QVariant data = index.data();
                QString text = data.toString();
                QStyleOptionViewItem newOption(option);
                if (!ExpressionEdit::isValid(text))
                {
                    newOption.font.setBold(true);
                    newOption.palette.setColor(QPalette::Text, Qt::red);
                }
            
                QItemDelegate::paint(painter, newOption, index);
            }
            
            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