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. Set Delegate for each cell in a QTableWidget?
Forum Updated to NodeBB v4.3 + New Features

Set Delegate for each cell in a QTableWidget?

Scheduled Pinned Locked Moved Solved General and Desktop
23 Posts 4 Posters 8.3k 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.
  • VRoninV Offline
    VRoninV Offline
    VRonin
    wrote on last edited by
    #13

    The shape of the play/pause button should depend on the data in the model. Coan you show us the slot where you you change the "In Queue" to "Paused"?

    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
    ~Napoleon Bonaparte

    On a crusade to banish setIndexWidget() from the holy land of Qt

    H 1 Reply Last reply
    0
    • VRoninV VRonin

      The shape of the play/pause button should depend on the data in the model. Coan you show us the slot where you you change the "In Queue" to "Paused"?

      H Offline
      H Offline
      hbatalha
      wrote on last edited by
      #14

      @VRonin Here are the connect's with QPushButton:

      connect(pause_button, &QPushButton::clicked, record, &Record::pauseResume); // `pause_button` is `button1` in the code above
      connect(record, &Record::progressUpdated, this, &MainWindow::onProgressUpdated);
      

      The slot:

      void MainWindow::onProgressUpdated(const QString& command, QString status)
      {
          const int row = commands_rows.key(command); // QMap
      
          QTableWidgetItem* item = ui->table->item(row, 2);
      
          bool isPercentage = false;
      
          int progressPercentInt = status.toInt(&isPercentage);
      
          if (progressPercentInt < 0)
          {
              status = tr("Error");
              isPercentage = false;
          }
      
          if(isPercentage)
          {
              item->setText(status + "%");
          }
          else
          {
              item->setText(status);
          }
      }
      
      VRoninV 1 Reply Last reply
      0
      • H hbatalha

        @VRonin Here are the connect's with QPushButton:

        connect(pause_button, &QPushButton::clicked, record, &Record::pauseResume); // `pause_button` is `button1` in the code above
        connect(record, &Record::progressUpdated, this, &MainWindow::onProgressUpdated);
        

        The slot:

        void MainWindow::onProgressUpdated(const QString& command, QString status)
        {
            const int row = commands_rows.key(command); // QMap
        
            QTableWidgetItem* item = ui->table->item(row, 2);
        
            bool isPercentage = false;
        
            int progressPercentInt = status.toInt(&isPercentage);
        
            if (progressPercentInt < 0)
            {
                status = tr("Error");
                isPercentage = false;
            }
        
            if(isPercentage)
            {
                item->setText(status + "%");
            }
            else
            {
                item->setText(status);
            }
        }
        
        VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #15

        @hbatalha said in Set Delegate for each cell in a QTableWidget?:

        &Record::pauseResume

        Can you show this slot please?

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        H 1 Reply Last reply
        0
        • VRoninV VRonin

          @hbatalha said in Set Delegate for each cell in a QTableWidget?:

          &Record::pauseResume

          Can you show this slot please?

          H Offline
          H Offline
          hbatalha
          wrote on last edited by hbatalha
          #16

          @VRonin

          void Recod::pauseResume()
          { 
              if(isInQueue() || isToResume())
              {
                  emit pogressUpdated(command, tr("Paused"));
          
                  if(hasStarted)
                  {
                      process->kill();
                  }
          
                  status_ = Status::PAUSED;
                  hasStarted = false;
              }
              else
              {
                  emit progressUpdated(command, tr("In Queue"));
                  status_ = Status::TO_RESUME;
                  emit statusChanged(getArgsStr(), status());
              }
          }
          
          VRoninV 1 Reply Last reply
          0
          • H hbatalha

            @VRonin

            void Recod::pauseResume()
            { 
                if(isInQueue() || isToResume())
                {
                    emit pogressUpdated(command, tr("Paused"));
            
                    if(hasStarted)
                    {
                        process->kill();
                    }
            
                    status_ = Status::PAUSED;
                    hasStarted = false;
                }
                else
                {
                    emit progressUpdated(command, tr("In Queue"));
                    status_ = Status::TO_RESUME;
                    emit statusChanged(getArgsStr(), status());
                }
            }
            
            VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by VRonin
            #17

            @hbatalha said in Set Delegate for each cell in a QTableWidget?:

            status_ = Status::PAUSED;

            Yeah, see, you are using a global state to manage data that should live in the model. pauseResume should look more like:

            enum{
            StatusRole = Qt::UserRole
            };
            void Recod::pauseResume(const QModelIndex& index){
            if (index.data(StatusRole).toInt() == Status::PAUSED)
            model->setData(index,Status::TO_RESUME,StatusRole);
            else
            model->setData(index,Status::PAUSED,StatusRole);
            }
            

            This stores the state of the single item into the model. Signaling the change is handled by the model. You can then retrieve that state inside the delegate and paint accordingly

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            H 1 Reply Last reply
            2
            • VRoninV VRonin

              @hbatalha said in Set Delegate for each cell in a QTableWidget?:

              status_ = Status::PAUSED;

              Yeah, see, you are using a global state to manage data that should live in the model. pauseResume should look more like:

              enum{
              StatusRole = Qt::UserRole
              };
              void Recod::pauseResume(const QModelIndex& index){
              if (index.data(StatusRole).toInt() == Status::PAUSED)
              model->setData(index,Status::TO_RESUME,StatusRole);
              else
              model->setData(index,Status::PAUSED,StatusRole);
              }
              

              This stores the state of the single item into the model. Signaling the change is handled by the model. You can then retrieve that state inside the delegate and paint accordingly

              H Offline
              H Offline
              hbatalha
              wrote on last edited by hbatalha
              #18

              @VRonin How I get this part, but since I will be having multiple rows how will I differentiate each delegate button with its row? In my for loop when I insert rows if I call setItemDelegateForColumn only once still all the rows that are inserted after will have the delegate buttons. Shouldn't I be using setItemDelegateForRow instead? Each record takes its buttons but only the last delegate buttons created in the for loop will be set into the columns. This is the part I am not getting.

              Sorry it's taking so much time but it's proving to be trickier for me to understand delegates fully. This is my first hands-on experience with them.

              VRoninV 1 Reply Last reply
              0
              • H hbatalha

                @VRonin How I get this part, but since I will be having multiple rows how will I differentiate each delegate button with its row? In my for loop when I insert rows if I call setItemDelegateForColumn only once still all the rows that are inserted after will have the delegate buttons. Shouldn't I be using setItemDelegateForRow instead? Each record takes its buttons but only the last delegate buttons created in the for loop will be set into the columns. This is the part I am not getting.

                Sorry it's taking so much time but it's proving to be trickier for me to understand delegates fully. This is my first hands-on experience with them.

                VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #19

                @hbatalha said in Set Delegate for each cell in a QTableWidget?:

                Each record takes its buttons but only the last delegate buttons created in the for loop will be set into the columns.

                Not sure I understand what you mean.

                If showing/not showing the buttons depend on a state of the item then we are back to the case above. Save such state in a role of the model and use it in the delegate.

                Maybe if you can formulate a simple concrete example we can get to the root of the problem

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                H 1 Reply Last reply
                0
                • VRoninV VRonin

                  @hbatalha said in Set Delegate for each cell in a QTableWidget?:

                  Each record takes its buttons but only the last delegate buttons created in the for loop will be set into the columns.

                  Not sure I understand what you mean.

                  If showing/not showing the buttons depend on a state of the item then we are back to the case above. Save such state in a role of the model and use it in the delegate.

                  Maybe if you can formulate a simple concrete example we can get to the root of the problem

                  H Offline
                  H Offline
                  hbatalha
                  wrote on last edited by
                  #20

                  @VRonin said in Set Delegate for each cell in a QTableWidget?:

                  Not sure I understand what you mean.

                  Ok, try having a QTableWidget with multiple rows and use setItemDelegateForColumn to add the delegate buttons like in my code above, then click a button in a row and have only that delegate button in that row be repainted (change icon). This is what I am unable to do.

                  If you see the image below I click one button and all the other buttons change icon as well. I want to click one button and only that button to change its icon.
                  5sm60k.gif

                  Maybe if you can formulate a simple concrete example we can get to the root of the problem

                  I will try to create one.

                  1 Reply Last reply
                  0
                  • VRoninV Offline
                    VRoninV Offline
                    VRonin
                    wrote on last edited by
                    #21

                    Simplified example:

                    buttondelegate.h

                    #ifndef BUTTONDELEGATE_H
                    #define BUTTONDELEGATE_H
                    
                    #include <QApplication>
                    #include <QStyledItemDelegate>
                    #include <QPushButton>
                    #include <QPainter>
                    
                    class ButtonDelegate : public QStyledItemDelegate
                    {
                        Q_OBJECT
                    #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
                        Q_DISABLE_COPY_MOVE(ButtonDelegate)
                    #else
                        Q_DISABLE_COPY(ButtonDelegate)
                    #endif
                    public:
                        explicit ButtonDelegate(QObject* parent)
                            :QStyledItemDelegate(parent)
                        {
                    
                        }
                        void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                        {
                            Q_ASSERT(index.isValid());
                            QStyleOptionViewItem opt = option;
                            initStyleOption(&opt, index);
                            const QWidget *widget = option.widget;
                            QStyle *style = widget ? widget->style() : QApplication::style();
                            QStyleOptionButton buttonOption = buttonOptions(opt,index);
                            style->drawControl(QStyle::CE_PushButton, &buttonOption, painter, widget);
                        }
                        QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                        {
                            QStyleOptionViewItem opt = option;
                            initStyleOption(&opt, index);
                            return buttonRect(opt).size();
                        }
                        QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const Q_DECL_OVERRIDE
                        {
                            return new QWidget(parent);
                        }
                        void setEditorData(QWidget *, const QModelIndex &) const Q_DECL_OVERRIDE
                        {
                    
                        }
                        void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const Q_DECL_OVERRIDE
                        {
                    
                        }
                        void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                        {
                            QStyleOptionViewItem opt = option;
                            initStyleOption(&opt, index);
                            editor->setGeometry(opt.rect);
                            const QRect butRect = buttonRect(opt);
                            QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor");
                            Q_ASSERT(baseEditor);
                            baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height());
                            QWidget* myButton = editor->findChild<QWidget*>("myButton");
                            Q_ASSERT(myButton);
                            myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height());
                        }
                    Q_SIGNALS:
                        void clicked(const QModelIndex &index);
                    protected:
                        bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
                        {
                            Q_ASSERT(event);
                            Q_ASSERT(model);
                            Qt::ItemFlags flags = model->flags(index);
                            if ((option.state & QStyle::State_Enabled) && (flags & Qt::ItemIsEnabled))
                            {
                                switch (event->type()){
                                case QEvent::MouseButtonRelease:{
                                    QStyleOptionViewItem viewOpt(option);
                                    initStyleOption(&viewOpt, index);
                                    QMouseEvent *me = static_cast<QMouseEvent*>(event);
                                    if (me->button() == Qt::LeftButton)
                                    {
                                        currentIndex = index;
                                        clickedHelper();
                                    }
                                }
                                    break;
                                default:
                                    break;
                                }
                            }
                            return QStyledItemDelegate::editorEvent(event,model,option,index);
                        }
                        virtual QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, const QModelIndex &index, bool skipRct=false) const
                        {
                            QStyleOptionButton buttonOption;
                            buttonOption.icon = m_buttonIcon;
                            if(index.data(Qt::UserRole).toBool())
                                buttonOption.text = QString(QChar(0x23F8)); //pause button emoji
                            else
                                buttonOption.text = QString(QChar(0x25B6)); //play button emoji
                            buttonOption.rect = skipRct ? QRect() : buttonRect(option);
                            buttonOption.features = QStyleOptionButton::None;
                            buttonOption.direction = option.direction;
                            buttonOption.fontMetrics = option.fontMetrics;
                            buttonOption.palette = option.palette;
                            buttonOption.styleObject = option.styleObject;
                            return buttonOption;
                        }
                        virtual QRect buttonRect(const QStyleOptionViewItem &option) const
                        {
                            return option.rect;
                        }
                    
                    private:
                        mutable QModelIndex currentIndex;
                        QString m_buttonText;
                        QIcon m_buttonIcon;
                        void clickedHelper()
                        {
                            clicked(currentIndex);
                        }
                    
                    };
                    #endif // BUTTONDELEGATE_H
                    

                    main.cpp

                    #include <QApplication>
                    #include <QTableWidget>
                    #include "buttondelegate.h"
                    
                    int main(int argc, char *argv[])
                    {
                        QApplication app(argc,argv);
                        QTableWidget wid(10,2);
                        for(int i=0;i<10;++i){
                            wid.setItem(i,0,new QTableWidgetItem(QStringLiteral("Item %1").arg(i+1)));
                        }
                        ButtonDelegate* buttonDelegate = new ButtonDelegate(&wid);
                        QObject::connect(buttonDelegate,&ButtonDelegate::clicked,&wid,[&wid](const QModelIndex& index){
                            Q_ASSERT(index.model()==wid.model());
                            wid.model()->setData(index,index.data(Qt::UserRole).toBool() ? QVariant() : true,Qt::UserRole);
                        });
                        wid.setItemDelegateForColumn(1, buttonDelegate);
                        wid.show();
                        return app.exec();
                    }
                    

                    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                    ~Napoleon Bonaparte

                    On a crusade to banish setIndexWidget() from the holy land of Qt

                    H 1 Reply Last reply
                    3
                    • VRoninV VRonin

                      Simplified example:

                      buttondelegate.h

                      #ifndef BUTTONDELEGATE_H
                      #define BUTTONDELEGATE_H
                      
                      #include <QApplication>
                      #include <QStyledItemDelegate>
                      #include <QPushButton>
                      #include <QPainter>
                      
                      class ButtonDelegate : public QStyledItemDelegate
                      {
                          Q_OBJECT
                      #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
                          Q_DISABLE_COPY_MOVE(ButtonDelegate)
                      #else
                          Q_DISABLE_COPY(ButtonDelegate)
                      #endif
                      public:
                          explicit ButtonDelegate(QObject* parent)
                              :QStyledItemDelegate(parent)
                          {
                      
                          }
                          void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                          {
                              Q_ASSERT(index.isValid());
                              QStyleOptionViewItem opt = option;
                              initStyleOption(&opt, index);
                              const QWidget *widget = option.widget;
                              QStyle *style = widget ? widget->style() : QApplication::style();
                              QStyleOptionButton buttonOption = buttonOptions(opt,index);
                              style->drawControl(QStyle::CE_PushButton, &buttonOption, painter, widget);
                          }
                          QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                          {
                              QStyleOptionViewItem opt = option;
                              initStyleOption(&opt, index);
                              return buttonRect(opt).size();
                          }
                          QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const Q_DECL_OVERRIDE
                          {
                              return new QWidget(parent);
                          }
                          void setEditorData(QWidget *, const QModelIndex &) const Q_DECL_OVERRIDE
                          {
                      
                          }
                          void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const Q_DECL_OVERRIDE
                          {
                      
                          }
                          void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE
                          {
                              QStyleOptionViewItem opt = option;
                              initStyleOption(&opt, index);
                              editor->setGeometry(opt.rect);
                              const QRect butRect = buttonRect(opt);
                              QWidget* baseEditor = editor->findChild<QWidget*>("baseEditor");
                              Q_ASSERT(baseEditor);
                              baseEditor->setGeometry(0,0,opt.rect.width()-butRect.width(),opt.rect.height());
                              QWidget* myButton = editor->findChild<QWidget*>("myButton");
                              Q_ASSERT(myButton);
                              myButton->setGeometry(opt.rect.width()-butRect.width(), 0, butRect.width(),butRect.height());
                          }
                      Q_SIGNALS:
                          void clicked(const QModelIndex &index);
                      protected:
                          bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
                          {
                              Q_ASSERT(event);
                              Q_ASSERT(model);
                              Qt::ItemFlags flags = model->flags(index);
                              if ((option.state & QStyle::State_Enabled) && (flags & Qt::ItemIsEnabled))
                              {
                                  switch (event->type()){
                                  case QEvent::MouseButtonRelease:{
                                      QStyleOptionViewItem viewOpt(option);
                                      initStyleOption(&viewOpt, index);
                                      QMouseEvent *me = static_cast<QMouseEvent*>(event);
                                      if (me->button() == Qt::LeftButton)
                                      {
                                          currentIndex = index;
                                          clickedHelper();
                                      }
                                  }
                                      break;
                                  default:
                                      break;
                                  }
                              }
                              return QStyledItemDelegate::editorEvent(event,model,option,index);
                          }
                          virtual QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, const QModelIndex &index, bool skipRct=false) const
                          {
                              QStyleOptionButton buttonOption;
                              buttonOption.icon = m_buttonIcon;
                              if(index.data(Qt::UserRole).toBool())
                                  buttonOption.text = QString(QChar(0x23F8)); //pause button emoji
                              else
                                  buttonOption.text = QString(QChar(0x25B6)); //play button emoji
                              buttonOption.rect = skipRct ? QRect() : buttonRect(option);
                              buttonOption.features = QStyleOptionButton::None;
                              buttonOption.direction = option.direction;
                              buttonOption.fontMetrics = option.fontMetrics;
                              buttonOption.palette = option.palette;
                              buttonOption.styleObject = option.styleObject;
                              return buttonOption;
                          }
                          virtual QRect buttonRect(const QStyleOptionViewItem &option) const
                          {
                              return option.rect;
                          }
                      
                      private:
                          mutable QModelIndex currentIndex;
                          QString m_buttonText;
                          QIcon m_buttonIcon;
                          void clickedHelper()
                          {
                              clicked(currentIndex);
                          }
                      
                      };
                      #endif // BUTTONDELEGATE_H
                      

                      main.cpp

                      #include <QApplication>
                      #include <QTableWidget>
                      #include "buttondelegate.h"
                      
                      int main(int argc, char *argv[])
                      {
                          QApplication app(argc,argv);
                          QTableWidget wid(10,2);
                          for(int i=0;i<10;++i){
                              wid.setItem(i,0,new QTableWidgetItem(QStringLiteral("Item %1").arg(i+1)));
                          }
                          ButtonDelegate* buttonDelegate = new ButtonDelegate(&wid);
                          QObject::connect(buttonDelegate,&ButtonDelegate::clicked,&wid,[&wid](const QModelIndex& index){
                              Q_ASSERT(index.model()==wid.model());
                              wid.model()->setData(index,index.data(Qt::UserRole).toBool() ? QVariant() : true,Qt::UserRole);
                          });
                          wid.setItemDelegateForColumn(1, buttonDelegate);
                          wid.show();
                          return app.exec();
                      }
                      
                      H Offline
                      H Offline
                      hbatalha
                      wrote on last edited by hbatalha
                      #22

                      @VRonin This one seems to be the solution I have been looking for. I will, however, have to hack my current code to using this solution. In case of any question I will reach out to you.

                      Thank you for taking so much time and having so much patience with me. I appreciate it.

                      1 Reply Last reply
                      1
                      • H Offline
                        H Offline
                        hbatalha
                        wrote on last edited by hbatalha
                        #23

                        @VRonin hi, I have finished modifying the delegate button so I can use it in my code and it works fine.
                        However, I am wondering how can I set a stylesheet for it. With my old QPushButton I had it like this:

                        QPushButton* cancel_button = new QPushButton(this);
                        cancel_button->setIcon(QIcon(QPixmap(":/Icons/cancel.png")));
                        cancel_button->setStyleSheet("QPushButton{background-color: transparent; background-repeat: none; border: none;}  QPushButton:pressed{ background-color: transparent; }");
                        

                        Can I do it with the delegate button? The reason I want to do this is because I want the the button to display only the icon I set for it.

                        Edit: I think I have solved it by going in the QStyleOptionButton buttonOptions(const QStyleOptionViewItem &option, bool skipRct=false) const function and change the QStyleOptionButton features to flat:

                                QStyleOptionButton buttonOption;
                                buttonOption.features = QStyleOptionButton::Flat;
                        
                        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