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. QStyledItemDelegate inside a QTableView?

QStyledItemDelegate inside a QTableView?

Scheduled Pinned Locked Moved Unsolved General and Desktop
20 Posts 4 Posters 2.7k 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.
  • mzimmersM mzimmers

    So, here's the signature of my delegate's paint() method:

    void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                             const QModelIndex &index) const
    
    1. how do I derive my model from the index? The model will dictate what I draw.
    2. can I position my paint object (in this case, a rectangle) within a cell in the view? Currently, it shows up like this:
      timezone.PNG
      I want it in the Widget column. How do I designate which row/column the paint takes place?

    Thanks...

    EDIT: to make these questions a bit more concrete, let's say I wanted to check a different column in my current row. If the value in that column is odd, I'd make my rectangle green; if even, red.

    JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by JonB
    #6

    @mzimmers said in QStyledItemDelegate inside a QTableView?:

    how do I derive my model from the index? The model will dictate what I draw.

    What do you mean by this? index is an index into the model being drawn. const QAbstractItemModel *QModelIndex::model() const gives you the model, i.e. from index.model() in the paint() event. And index.data() gives you the data value from the model.

    This paint() method is called for every cell to draw, one at a time, as necessary. You look at index.row/column() if that is relevant to your painting. If you want to do something special in certain rows/columns use an if/switch statement. Just call the base class paint() for the default painting. So you do not "position the object into/designate which row/column the paint takes place", you wait till it's called with index.column() == 2 for your Widget column.

    If you used QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate) to set your delegate on the tableview it is set/will be called for every cell drawn. However, as a convenience to save you having to use an if/switch for the Widget column, you could instead use QAbstractItemView::setItemDelegateForColumn(int column, QAbstractItemDelegate *delegate) to set a delegate for an individual column.

    1 Reply Last reply
    3
    • mzimmersM Offline
      mzimmersM Offline
      mzimmers
      wrote on last edited by
      #7

      So, I guess the correct way to get a new index is something like this:

      void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
      {
          QModelIndex qmi;
          if (index.column() == COLUMN_WIDGET) {
              qmi = index.model()->index(index.row(), COLUMN_CURRENTTIME);
              QString qs = qmi.data().toString();
              etc...
      

      And then process qs as necessary to influence how I do my painting.

      And, thanks for the reference to setItemDelegateForColumn(). That is indeed very convenient.

      JonBJ Christian EhrlicherC 2 Replies Last reply
      0
      • mzimmersM mzimmers

        So, I guess the correct way to get a new index is something like this:

        void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
            QModelIndex qmi;
            if (index.column() == COLUMN_WIDGET) {
                qmi = index.model()->index(index.row(), COLUMN_CURRENTTIME);
                QString qs = qmi.data().toString();
                etc...
        

        And then process qs as necessary to influence how I do my painting.

        And, thanks for the reference to setItemDelegateForColumn(). That is indeed very convenient.

        JonBJ Offline
        JonBJ Offline
        JonB
        wrote on last edited by
        #8

        @mzimmers said in QStyledItemDelegate inside a QTableView?:

        index.model()->index(index.row(), COLUMN_CURRENTTIME);

        That can be written as index.siblingAtColumn(COLUMN_CURRENTTIME).

        1 Reply Last reply
        2
        • mzimmersM mzimmers

          So, I guess the correct way to get a new index is something like this:

          void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
          {
              QModelIndex qmi;
              if (index.column() == COLUMN_WIDGET) {
                  qmi = index.model()->index(index.row(), COLUMN_CURRENTTIME);
                  QString qs = qmi.data().toString();
                  etc...
          

          And then process qs as necessary to influence how I do my painting.

          And, thanks for the reference to setItemDelegateForColumn(). That is indeed very convenient.

          Christian EhrlicherC Online
          Christian EhrlicherC Online
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #9

          @mzimmers said in QStyledItemDelegate inside a QTableView?:

          And then process qs as necessary to influence how I do my painting.

          why? You already have the correct index - it's the one which is passed to your paint function... why would you paint something from another cell in the one you have to paint?

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          mzimmersM 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            @mzimmers said in QStyledItemDelegate inside a QTableView?:

            And then process qs as necessary to influence how I do my painting.

            why? You already have the correct index - it's the one which is passed to your paint function... why would you paint something from another cell in the one you have to paint?

            mzimmersM Offline
            mzimmersM Offline
            mzimmers
            wrote on last edited by mzimmers
            #10

            @Christian-Ehrlicher I need the contents of another cell in order to know what to paint. The other cell in question contains a QString with digits; I need to know those digits in order to know what to paint. (I'm sort of doing a manual implementation of the Qt LCD class.)

            EDIT: this brings me to another issue, though -- the delegate can access the data from within its paint() method, but there doesn't appear to be a way to pass that to the object that actually does the painting. I'm using the StarDelegate example, and the delegate looks like this:

            void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                     const QModelIndex &index) const
            {
                QModelIndex qmi;// = new QModelIndex(m_model->index(index.row(), COLUMN_CURRENTTIME));
            
                if (index.column() == COLUMN_WIDGET) {
                    // qmi = index.model()->index(index.row(), COLUMN_CURRENTTIME);
                    qmi = index.siblingAtColumn(COLUMN_CURRENTTIME);
                    
                    QString qs = qmi.data().toString();
            
                    StarRating starRating = qvariant_cast<StarRating>(index.data());
                    starRating.paint(painter, option.rect, option.palette,
                                     StarRating::EditMode::ReadOnly);
            

            My starRating object needs to know the data in order to paint. I don't know how to pass that information to that object. Or...am I doing this wrong?

            EDIT 2: I see that I can modify the signature of my starRating to include the value I want to paint, so disregard my last question.

            JonBJ 1 Reply Last reply
            0
            • mzimmersM mzimmers

              @Christian-Ehrlicher I need the contents of another cell in order to know what to paint. The other cell in question contains a QString with digits; I need to know those digits in order to know what to paint. (I'm sort of doing a manual implementation of the Qt LCD class.)

              EDIT: this brings me to another issue, though -- the delegate can access the data from within its paint() method, but there doesn't appear to be a way to pass that to the object that actually does the painting. I'm using the StarDelegate example, and the delegate looks like this:

              void TimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                       const QModelIndex &index) const
              {
                  QModelIndex qmi;// = new QModelIndex(m_model->index(index.row(), COLUMN_CURRENTTIME));
              
                  if (index.column() == COLUMN_WIDGET) {
                      // qmi = index.model()->index(index.row(), COLUMN_CURRENTTIME);
                      qmi = index.siblingAtColumn(COLUMN_CURRENTTIME);
                      
                      QString qs = qmi.data().toString();
              
                      StarRating starRating = qvariant_cast<StarRating>(index.data());
                      starRating.paint(painter, option.rect, option.palette,
                                       StarRating::EditMode::ReadOnly);
              

              My starRating object needs to know the data in order to paint. I don't know how to pass that information to that object. Or...am I doing this wrong?

              EDIT 2: I see that I can modify the signature of my starRating to include the value I want to paint, so disregard my last question.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by
              #11

              @mzimmers
              A different way of abstracting sounds like doing the work/mapping for the Widgets column in a QAbstractProxyModel instead of in the delegate. A QIdentityProxyModel with an extra Widgets column. The delegate then just draws the rectangle or whatever without doing the logic of calculating anything. Just a possibility.

              1 Reply Last reply
              0
              • mzimmersM Offline
                mzimmersM Offline
                mzimmers
                wrote on last edited by
                #12

                Thanks to everyone's help, I think I'm very close to having this functional. I'm able to paint the data in the manner I want, and it displays correctly.

                One remaining issue: when the data changes, the display doesn't automatically repaint. (It does repaint if I defocus, or resize or much of anything else, just not on its own.) What is the recommended way of getting this to update? I do have a one second timer in my worker thread that updates the model and signals the display thread, but that doesn't provoke a repaint (I guess because I have the delegate).

                Thanks...

                JonBJ 1 Reply Last reply
                0
                • mzimmersM mzimmers

                  Thanks to everyone's help, I think I'm very close to having this functional. I'm able to paint the data in the manner I want, and it displays correctly.

                  One remaining issue: when the data changes, the display doesn't automatically repaint. (It does repaint if I defocus, or resize or much of anything else, just not on its own.) What is the recommended way of getting this to update? I do have a one second timer in my worker thread that updates the model and signals the display thread, but that doesn't provoke a repaint (I guess because I have the delegate).

                  Thanks...

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by
                  #13

                  @mzimmers
                  The fact that you have your own delegate should not be at issue here. The fact that it does repaint when resized (calls for a complete redraw) but not when data changes indicates the right dataChanged() signal is not being emitted. Put in qDebug() statements to see when that does/does not occur. If you are still computing one column's value/need to redraw from another column's value change, you would need the dataChanged() signal parameters to include both columns, or multiple signals.

                  mzimmersM 1 Reply Last reply
                  1
                  • JonBJ JonB

                    @mzimmers
                    The fact that you have your own delegate should not be at issue here. The fact that it does repaint when resized (calls for a complete redraw) but not when data changes indicates the right dataChanged() signal is not being emitted. Put in qDebug() statements to see when that does/does not occur. If you are still computing one column's value/need to redraw from another column's value change, you would need the dataChanged() signal parameters to include both columns, or multiple signals.

                    mzimmersM Offline
                    mzimmersM Offline
                    mzimmers
                    wrote on last edited by
                    #14

                    @JonB I have this connection in my Widget:

                        QObject::connect(m_model, &QStandardItemModel::dataChanged, this, &Widget::processChange);
                    void Widget::processChange(QModelIndex qmi1, QModelIndex qmi2) {
                        Q_UNUSED(qmi1)
                        Q_UNUSED(qmi2)
                        ui->tableView->repaint();
                    }
                    

                    The slot is called, but the repaint() doesn't seem to do anything until I click on the Widget.

                    Could this somehow be related to the use of an incorrect model class? I'm using QStandardItemView, but my display widget is a QTableView.

                    Thanks...

                    1 Reply Last reply
                    0
                    • Christian EhrlicherC Online
                      Christian EhrlicherC Online
                      Christian Ehrlicher
                      Lifetime Qt Champion
                      wrote on last edited by
                      #15

                      When a model changes you don't have to call update on the view - this is done automatically when the dataChanged() emits with the correct indexes.

                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                      Visit the Qt Academy at https://academy.qt.io/catalog

                      mzimmersM 1 Reply Last reply
                      1
                      • Christian EhrlicherC Christian Ehrlicher

                        When a model changes you don't have to call update on the view - this is done automatically when the dataChanged() emits with the correct indexes.

                        mzimmersM Offline
                        mzimmersM Offline
                        mzimmers
                        wrote on last edited by
                        #16

                        @Christian-Ehrlicher well then, I'm doing something wrong. My model has the following columns:

                        enum MODEL_COLUMNS {
                            COLUMN_LABEL,
                            COLUMN_OFFSET,
                            COLUMN_TEXT_TIME,
                            COLUMN_PAINT_TIME,
                            NBR_COLS
                        } ;
                        

                        In my Widget:

                            ui->tableView->setModel(model);
                            timeDelegate = new TimeDelegate();
                            ui->tableView->setItemDelegateForColumn(COLUMN_PAINT_TIME, timeDelegate);
                        

                        The idea is that when something in column COLUMN_TEXT_TIME changes, I want to alter the corresponding entry in COLUMN_PAINT_TIME (in the view). I'm getting the signal from the model, and I do repaint, but the repaint only becomes visible when I click on the window (or try to resize it, or try to move it). These actions also cause the signal to be sent again.

                        Am I missing something here?

                        Thanks...

                        Christian EhrlicherC 1 Reply Last reply
                        0
                        • mzimmersM mzimmers

                          @Christian-Ehrlicher well then, I'm doing something wrong. My model has the following columns:

                          enum MODEL_COLUMNS {
                              COLUMN_LABEL,
                              COLUMN_OFFSET,
                              COLUMN_TEXT_TIME,
                              COLUMN_PAINT_TIME,
                              NBR_COLS
                          } ;
                          

                          In my Widget:

                              ui->tableView->setModel(model);
                              timeDelegate = new TimeDelegate();
                              ui->tableView->setItemDelegateForColumn(COLUMN_PAINT_TIME, timeDelegate);
                          

                          The idea is that when something in column COLUMN_TEXT_TIME changes, I want to alter the corresponding entry in COLUMN_PAINT_TIME (in the view). I'm getting the signal from the model, and I do repaint, but the repaint only becomes visible when I click on the window (or try to resize it, or try to move it). These actions also cause the signal to be sent again.

                          Am I missing something here?

                          Thanks...

                          Christian EhrlicherC Online
                          Christian EhrlicherC Online
                          Christian Ehrlicher
                          Lifetime Qt Champion
                          wrote on last edited by
                          #17

                          @mzimmers said in QStyledItemDelegate inside a QTableView?:

                          Am I missing something here?

                          As I said - you may be calling dataChanged() with wrong indexes or roles but you don't show us the code. Did you disabled updates for your widgets somehow?
                          Also you should rather use update() instead repaint()

                          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                          Visit the Qt Academy at https://academy.qt.io/catalog

                          mzimmersM 1 Reply Last reply
                          0
                          • Christian EhrlicherC Christian Ehrlicher

                            @mzimmers said in QStyledItemDelegate inside a QTableView?:

                            Am I missing something here?

                            As I said - you may be calling dataChanged() with wrong indexes or roles but you don't show us the code. Did you disabled updates for your widgets somehow?
                            Also you should rather use update() instead repaint()

                            mzimmersM Offline
                            mzimmersM Offline
                            mzimmers
                            wrote on last edited by mzimmers
                            #18

                            @Christian-Ehrlicher I don't understand why columns or roles play a factor here. The dataChanged() signal is getting delivered; I've verified that in the debugger. And when it does, I unconditionally call:

                            ui->tableView->repaint();
                            

                            (I'll change that to update().)

                            Shouldn't this be sufficient without regard to roles or columns?

                            I thought I'd posted all the relevant code; what else would be helpful?

                            Thanks...

                            EDIT: I verified that updates are indeed enabled on the widget.

                            Christian EhrlicherC 1 Reply Last reply
                            0
                            • mzimmersM mzimmers

                              @Christian-Ehrlicher I don't understand why columns or roles play a factor here. The dataChanged() signal is getting delivered; I've verified that in the debugger. And when it does, I unconditionally call:

                              ui->tableView->repaint();
                              

                              (I'll change that to update().)

                              Shouldn't this be sufficient without regard to roles or columns?

                              I thought I'd posted all the relevant code; what else would be helpful?

                              Thanks...

                              EDIT: I verified that updates are indeed enabled on the widget.

                              Christian EhrlicherC Online
                              Christian EhrlicherC Online
                              Christian Ehrlicher
                              Lifetime Qt Champion
                              wrote on last edited by
                              #19

                              @mzimmers said in QStyledItemDelegate inside a QTableView?:

                              The dataChanged() signal is getting delivered

                              When you use the wrong indexes it will not help. But do what you want.

                              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                              Visit the Qt Academy at https://academy.qt.io/catalog

                              mzimmersM 1 Reply Last reply
                              0
                              • Christian EhrlicherC Christian Ehrlicher

                                @mzimmers said in QStyledItemDelegate inside a QTableView?:

                                The dataChanged() signal is getting delivered

                                When you use the wrong indexes it will not help. But do what you want.

                                mzimmersM Offline
                                mzimmersM Offline
                                mzimmers
                                wrote on last edited by mzimmers
                                #20

                                @Christian-Ehrlicher I'm sorry, but I'm just not understanding something here. If I get a signal from the model, and respond to that signal by repainting/updating/whatever my QTableView, is that not sufficient to trigger my delegate's paint() event (which will update the COLUMN_PAINT_TIME column)?

                                Thanks....

                                EDIT: well, what @Christian-Ehrlicher said was 100% right, but...I still don't understand the mechanism. I added a line in my worker thread to update the COLUMN_PAINT_TIME in the model, and now it seems to be working. (The date of model update in this case seems to be irrelevant, as the display is changed by the delegate.)

                                What I don't understand is, if the COLUMN_PAINT_TIME in the view is under control of a delegate, why does the model have to be updated? If my widget slot is calling repaint/update/whatever unconditionally, why isn't that sufficient irrespective of the arguments in the signal/slot mechanism?

                                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