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. paint custom QTableView row colors
QtWS25 Last Chance

paint custom QTableView row colors

Scheduled Pinned Locked Moved Unsolved General and Desktop
table viewitem delegate
5 Posts 3 Posters 621 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.
  • J Offline
    J Offline
    johnco3
    wrote on 3 Mar 2023, 06:43 last edited by
    #1

    I have an application with a QTableView that displays log entries (colored by thread ID). The code below shows how did this by assigning colors to each row using a thread ID. This thread coloring is optional (defaulting to normal black text on a white background or vise-versa if dark mode is enabled), so when I toggle it, I need to repaint the QTableView rows (at least the visible ones). I think the way to optionally achieve this through the QT Model/View is to subclass of QStyledItemDelegate and do something with roles, but I am really not sure and would greatly appreciate some simple example.

    void
    MainWindow::handleAddLogEntry(
        const level::level_enum aLevel,
        const QString& rLogEntry,
        const Qt::HANDLE aThreadID) const
    {
        // foreground colors for default main thread text
        const static auto gFGBrushDark = QBrush(QColorConstants::White);
        const static auto gFGBrushLight = QBrush(QColorConstants::Black);
    
        static const auto gBrushes = std::array {
            QBrush(QColorConstants::Red),
            QBrush(QColorConstants::Green),
            QBrush(QColorConstants::Blue),
            QBrush(QColorConstants::Magenta)
        };
    
        // do not make static as we need to adjust for theme
        const auto gDefaultBrushColor = (mThemeStyle ==
            ThemeStyle::Light) ? gFGBrushLight : gFGBrushDark;
        static const auto gMainThreadID = QThread::currentThreadId();
        static std::map<Qt::HANDLE, QBrush> gBrushInfo;
        // reset default brush color each time as it is theme dependent
        // and the theme may have been changed since the last log entry
        gBrushInfo[gMainThreadID] = gDefaultBrushColor;
    
        QList<QStandardItem*> row;
        QStandardItem* item = nullptr;
    
        auto brushColor = gDefaultBrushColor;
        // lookup thread ID for coloring
        if (const auto it = gBrushInfo.find(
            aThreadID); it != gBrushInfo.cend()) {
            if (mColorLog) {
                brushColor = it->second;
            }
        } else { // update gBrushInfo with thread color
            static auto gNextColor = 0;
            // insert the new color in gBrushInfo
            const auto& brush = gBrushes[
                gNextColor % gBrushes.size()];
            gBrushInfo[aThreadID] = brush;
            if (mColorLog) {
                brushColor = brush;
            }
            ++gNextColor;
        }
    
        // Col1: Timestamp
        item = new QStandardItem(getTimeStampStr());
        item->setForeground(brushColor);
        row.emplaceBack(item);
    
        // Col2: Thread ID - 32 bit hex integer with leading 0x
        item = new QStandardItem(QString("0x%1").arg(
            reinterpret_cast<const uint64_t>(aThreadID),
            8, 16, QChar('0')));
        item->setForeground(brushColor);
        row.emplaceBack(item);
    
        // Col3: Level
        item = new QStandardItem(std::string(
            enum_name(aLevel)).c_str());
        item->setForeground(brushColor);
        row.emplaceBack(item);
    
        // Col4: Description.
        item = new QStandardItem(rLogEntry);
        item->setForeground(brushColor);
        row.emplaceBack(item);
    
        mLogViewModel->appendRow(row);
    }
    
    C 1 Reply Last reply 3 Mar 2023, 07:02
    0
    • J johnco3
      3 Mar 2023, 06:43

      I have an application with a QTableView that displays log entries (colored by thread ID). The code below shows how did this by assigning colors to each row using a thread ID. This thread coloring is optional (defaulting to normal black text on a white background or vise-versa if dark mode is enabled), so when I toggle it, I need to repaint the QTableView rows (at least the visible ones). I think the way to optionally achieve this through the QT Model/View is to subclass of QStyledItemDelegate and do something with roles, but I am really not sure and would greatly appreciate some simple example.

      void
      MainWindow::handleAddLogEntry(
          const level::level_enum aLevel,
          const QString& rLogEntry,
          const Qt::HANDLE aThreadID) const
      {
          // foreground colors for default main thread text
          const static auto gFGBrushDark = QBrush(QColorConstants::White);
          const static auto gFGBrushLight = QBrush(QColorConstants::Black);
      
          static const auto gBrushes = std::array {
              QBrush(QColorConstants::Red),
              QBrush(QColorConstants::Green),
              QBrush(QColorConstants::Blue),
              QBrush(QColorConstants::Magenta)
          };
      
          // do not make static as we need to adjust for theme
          const auto gDefaultBrushColor = (mThemeStyle ==
              ThemeStyle::Light) ? gFGBrushLight : gFGBrushDark;
          static const auto gMainThreadID = QThread::currentThreadId();
          static std::map<Qt::HANDLE, QBrush> gBrushInfo;
          // reset default brush color each time as it is theme dependent
          // and the theme may have been changed since the last log entry
          gBrushInfo[gMainThreadID] = gDefaultBrushColor;
      
          QList<QStandardItem*> row;
          QStandardItem* item = nullptr;
      
          auto brushColor = gDefaultBrushColor;
          // lookup thread ID for coloring
          if (const auto it = gBrushInfo.find(
              aThreadID); it != gBrushInfo.cend()) {
              if (mColorLog) {
                  brushColor = it->second;
              }
          } else { // update gBrushInfo with thread color
              static auto gNextColor = 0;
              // insert the new color in gBrushInfo
              const auto& brush = gBrushes[
                  gNextColor % gBrushes.size()];
              gBrushInfo[aThreadID] = brush;
              if (mColorLog) {
                  brushColor = brush;
              }
              ++gNextColor;
          }
      
          // Col1: Timestamp
          item = new QStandardItem(getTimeStampStr());
          item->setForeground(brushColor);
          row.emplaceBack(item);
      
          // Col2: Thread ID - 32 bit hex integer with leading 0x
          item = new QStandardItem(QString("0x%1").arg(
              reinterpret_cast<const uint64_t>(aThreadID),
              8, 16, QChar('0')));
          item->setForeground(brushColor);
          row.emplaceBack(item);
      
          // Col3: Level
          item = new QStandardItem(std::string(
              enum_name(aLevel)).c_str());
          item->setForeground(brushColor);
          row.emplaceBack(item);
      
          // Col4: Description.
          item = new QStandardItem(rLogEntry);
          item->setForeground(brushColor);
          row.emplaceBack(item);
      
          mLogViewModel->appendRow(row);
      }
      
      C Online
      C Online
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on 3 Mar 2023, 07:02 last edited by
      #2

      Simply set the new colors when you toggle it. The view is then updated automatically. Even though using QStandardItemModel would be slow for this when there a lot of items.

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

      J 1 Reply Last reply 3 Mar 2023, 08:49
      0
      • C Christian Ehrlicher
        3 Mar 2023, 07:02

        Simply set the new colors when you toggle it. The view is then updated automatically. Even though using QStandardItemModel would be slow for this when there a lot of items.

        J Offline
        J Offline
        johnco3
        wrote on 3 Mar 2023, 08:49 last edited by johnco3 3 Mar 2023, 08:56
        #3

        @Christian-Ehrlicher Thanks but for some strange reason the table doesn't refresh itself when I toggle the colors. To do so I would need to erase the entire table and re-insert the entries - but I cannot as it is a log file. Whenever I turn the thread coloring on, future rows are added with the expected color, then when I turn it off the log entries following the colored entries are back to the usual black/white. I thought that the QStyledItemDelegate or some subclass thereof was the trick to automatically handle the repainting of rows.

        I also tried to force an updated as follows and it did not work either.

        const auto& topLeft =
            mLogViewModel->index(0, 0);
        const auto& bottomRight =
            mLogViewModel->index(
                mLogViewModel->rowCount() - 1,
                mLogViewModel->columnCount() - 1);
        Q_EMIT mLogViewModel->dataChanged(topLeft, bottomRight);
        
        
        JonBJ 1 Reply Last reply 3 Mar 2023, 08:55
        0
        • J johnco3
          3 Mar 2023, 08:49

          @Christian-Ehrlicher Thanks but for some strange reason the table doesn't refresh itself when I toggle the colors. To do so I would need to erase the entire table and re-insert the entries - but I cannot as it is a log file. Whenever I turn the thread coloring on, future rows are added with the expected color, then when I turn it off the log entries following the colored entries are back to the usual black/white. I thought that the QStyledItemDelegate or some subclass thereof was the trick to automatically handle the repainting of rows.

          I also tried to force an updated as follows and it did not work either.

          const auto& topLeft =
              mLogViewModel->index(0, 0);
          const auto& bottomRight =
              mLogViewModel->index(
                  mLogViewModel->rowCount() - 1,
                  mLogViewModel->columnCount() - 1);
          Q_EMIT mLogViewModel->dataChanged(topLeft, bottomRight);
          
          
          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on 3 Mar 2023, 08:55 last edited by JonB 3 Mar 2023, 09:04
          #4

          @johnco3
          If I understand right. You have a model with existing rows. You want to change the color of those rows. So you need QTableView to redraw those rows. So you need to emit dataChanged() to cover all rows to tell the view they need updating. Is that your situation?

          EDIT
          Oh, lol, I have only just noticed you have that in your final code!

          Well, you have already explicitly called QStandardItem::setForeground() on existing cells, right? So nothing is going to change that unless you do so yourself on those items? If you cannot calculate desired color to return from data(), so that you do not have to set it, then you will have to revisit existing items changing their color.

          J 1 Reply Last reply 3 Mar 2023, 19:03
          0
          • JonBJ JonB
            3 Mar 2023, 08:55

            @johnco3
            If I understand right. You have a model with existing rows. You want to change the color of those rows. So you need QTableView to redraw those rows. So you need to emit dataChanged() to cover all rows to tell the view they need updating. Is that your situation?

            EDIT
            Oh, lol, I have only just noticed you have that in your final code!

            Well, you have already explicitly called QStandardItem::setForeground() on existing cells, right? So nothing is going to change that unless you do so yourself on those items? If you cannot calculate desired color to return from data(), so that you do not have to set it, then you will have to revisit existing items changing their color.

            J Offline
            J Offline
            johnco3
            wrote on 3 Mar 2023, 19:03 last edited by
            #5

            @JonB I'm still wondering if the right approach would be to use a QItemViewDelegate subclass. I need custom painting of the rows when they come into view based on a global UI setting (colored or normal display) and the content of one of the cells in the row (the threadID).

            The Star Delegate example seems to indicate that custom cell painting is done via these view delegates.

            I thought that the way these delegates are supposed to work is that they get called for painting or editing on demand (i.e. when their cells are visible via the scrollbars or double clicked for editing).

            This way the change from normal to colored rows would refresh the visible view (I don't really need to store the background with each item in the table as it is just for visibility purposes.

            1 Reply Last reply
            0

            1/5

            3 Mar 2023, 06:43

            • Login

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