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. Highlight matched substrings in QStyledItemDelegate
QtWS25 Last Chance

Highlight matched substrings in QStyledItemDelegate

Scheduled Pinned Locked Moved Solved General and Desktop
qstyleditemdelepaint
8 Posts 2 Posters 3.4k 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.
  • P Offline
    P Offline
    panosk
    wrote on 2 Dec 2015, 10:57 last edited by
    #1

    I'm using a custom QStyledItemDelegate and I'd like to highlight or mark with different font color specific words that match a regular expression. How should I implement the paint method to highlight these words?

    P 1 Reply Last reply 3 Dec 2015, 10:31
    0
    • P panosk
      2 Dec 2015, 10:57

      I'm using a custom QStyledItemDelegate and I'd like to highlight or mark with different font color specific words that match a regular expression. How should I implement the paint method to highlight these words?

      P Offline
      P Offline
      panosk
      wrote on 3 Dec 2015, 10:31 last edited by
      #2

      A gentle bump with a little more information.

      It seems I have trouble setting correctly the rectangle of the matched strings in relation to the whole text. Some early experiments:

      void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
      {
          QStyleOptionViewItemV4 optionV4 = option;
          initStyleOption(&optionV4, index);
          if (optionV4.state & QStyle::State_Enabled) {
              const QRect rect(...); 
              //Here I'm drawing the whole text
              painter->drawText(rect,Qt::TextWordWrap,index.data(Qt::DisplayRole).toString()); 
      
              /*QTextDocument doc(optionV4.text);*/ // This could probably be a better option...
      
              QRegularExpression reg(MYREG);
              QRegularExpressionMatchIterator it = reg.globalMatch(optionV4.text);
              while(it.hasNext()) {
                  QRegularExpressionMatch match = it.next();
                  QString found = match.captured(1);
                  painter->save();
                  QFontMetrics fmt(optionV4.font);
                  QRect foundRect = fmt.boundingRect(found);
                  QBrush brush = optionV4.palette.background();
                  brush.setColor(Qt::yellow);
                  painter->fillRect(foundRect,brush);
                  painter->restore();
              }
      

      Currently something like this will cover the text and won't highlight it, but I hope that gives an idea of what I want to achieve.

      Basically, I want to mark some words in some way and treat them differently (kind of placeholders) when editing the cell text, so the formatting I'll do in the delegate has to be also available in the editor (QTextEdit). The underlying model is a subclassed QSqlRelationalTableModel so I'm not sure if it's possible to move this logic to the data() method of my model and return these strings with a different role other than Qt::DisplayRole. Ideas are of course welcome.

      1 Reply Last reply
      0
      • K Offline
        K Offline
        kshegunov
        Moderators
        wrote on 3 Dec 2015, 10:56 last edited by kshegunov 12 Mar 2015, 11:05
        #3

        Hello,
        It seems you almost got it. As I see it, you only need to move the rectangle-filling part before drawing the text and it should light up.

        Kind regards.

        EDIT:
        Do you wish the font to change color or it's background? If the former, then probably you'll need to draw the text in parts.

        Read and abide by the Qt Code of Conduct

        P 1 Reply Last reply 3 Dec 2015, 11:33
        0
        • K kshegunov
          3 Dec 2015, 10:56

          Hello,
          It seems you almost got it. As I see it, you only need to move the rectangle-filling part before drawing the text and it should light up.

          Kind regards.

          EDIT:
          Do you wish the font to change color or it's background? If the former, then probably you'll need to draw the text in parts.

          P Offline
          P Offline
          panosk
          wrote on 3 Dec 2015, 11:33 last edited by
          #4

          @kshegunov
          Hello,

          Thanks for the tips. Changing font color would be probably better. In any case, the problem is the wrong offsets of the boundingRect. The width and height are correct, but the x and y points are the same (0,-14) no matter where the substring is found, which is expected as it's mentioned in the documentation:

          Returns the rectangle that is covered by ink if character ch were to be drawn at the origin of the coordinate system.
          

          So, I probably need to use the translate() method to move the substring rectangle to the right position in relation to the enclosing option.rect but it seems I have exhausted all my brain cells and can't figure out the calculation...:)

          Also, if you could give any general clues how I should draw the text in parts I would appreciate it.

          K 1 Reply Last reply 3 Dec 2015, 11:50
          0
          • P panosk
            3 Dec 2015, 11:33

            @kshegunov
            Hello,

            Thanks for the tips. Changing font color would be probably better. In any case, the problem is the wrong offsets of the boundingRect. The width and height are correct, but the x and y points are the same (0,-14) no matter where the substring is found, which is expected as it's mentioned in the documentation:

            Returns the rectangle that is covered by ink if character ch were to be drawn at the origin of the coordinate system.
            

            So, I probably need to use the translate() method to move the substring rectangle to the right position in relation to the enclosing option.rect but it seems I have exhausted all my brain cells and can't figure out the calculation...:)

            Also, if you could give any general clues how I should draw the text in parts I would appreciate it.

            K Offline
            K Offline
            kshegunov
            Moderators
            wrote on 3 Dec 2015, 11:50 last edited by
            #5

            @panosk
            Hello,
            It might get pretty complex seeing that you've enabled word wrapping and gave up on QTextDocument. The calculation could be done like this (I'm speculating here, have not tried it):
            Split your text into words (in an array), get the bounding rectangle for each word (in an array).
            Then repeat until you've exhausted all words:

            1. Calculate the most words per line.
            2. Draw the words one by one (with appropriate style). Draw a word and then move your current position right.
            3. Go to next line (move your current position down).

            It's certainly is not ideal, but will probably work. One problem I see is that this way is a bit clumsy and inflexible, and probably QTextDocument and it's support classes will give you better performance/possibilities.

            Kind regards.

            Read and abide by the Qt Code of Conduct

            P 1 Reply Last reply 3 Dec 2015, 12:03
            0
            • K kshegunov
              3 Dec 2015, 11:50

              @panosk
              Hello,
              It might get pretty complex seeing that you've enabled word wrapping and gave up on QTextDocument. The calculation could be done like this (I'm speculating here, have not tried it):
              Split your text into words (in an array), get the bounding rectangle for each word (in an array).
              Then repeat until you've exhausted all words:

              1. Calculate the most words per line.
              2. Draw the words one by one (with appropriate style). Draw a word and then move your current position right.
              3. Go to next line (move your current position down).

              It's certainly is not ideal, but will probably work. One problem I see is that this way is a bit clumsy and inflexible, and probably QTextDocument and it's support classes will give you better performance/possibilities.

              Kind regards.

              P Offline
              P Offline
              panosk
              wrote on 3 Dec 2015, 12:03 last edited by
              #6

              @kshegunov
              Well, I haven't given up on QTextDocument, actually I have a comment in my code posting that this could probably be a better option. I agree that things will get too complicated and considering that the model/view could contain thousand of rows, performance could be an issue. I'll go the QTextDocument route.

              Thanks a lot for your feedback!

              1 Reply Last reply
              0
              • K Offline
                K Offline
                kshegunov
                Moderators
                wrote on 3 Dec 2015, 12:06 last edited by kshegunov 12 Mar 2015, 12:09
                #7

                Something like this should be sufficient ...

                const QRect rect(...); // The item rectangle
                qint32 newLineOffset = 0;
                QRect spaceSize = fmt.boundingRect(QStringLiteral(" "));
                QPoint position;   // This would be our current position
                QStringList words = index.data(Qt::DisplayRole).toString().split(QStringLiteral(" "), QString::SkipEmptyParts); // Words array
                for (qint32 i = 0, size = words.size(); i < size && position.y() < rect.height(); i++)  {
                     QRect wordSize = fmt.boundingRect(words[i]);
                     if (position.x() + wordSize.width() > rect.width())  {
                          // We go to a newline
                          position.x() = 0;
                          position.y() += newLineOffset;
                          newLineOffset = 0;
                     }
                     // Draw the word (you could insert your own style here)
                     painter->drawText(position, words[i]);
                     // Move right + space size
                     position.x() += wordSize.width() + spaceSize.width();
                     // Update line height
                     newLineOffset = qMax(newLineOffset, qMax(wordSize.height(), spaceSize.height()));
                }
                

                EDIT:
                Right I saw that you wrote a post. Yes, probably QTextDocument will be the more flexible way. Do consider the code sample I give you here though.

                Kind regards.

                Read and abide by the Qt Code of Conduct

                P 1 Reply Last reply 3 Dec 2015, 12:12
                1
                • K kshegunov
                  3 Dec 2015, 12:06

                  Something like this should be sufficient ...

                  const QRect rect(...); // The item rectangle
                  qint32 newLineOffset = 0;
                  QRect spaceSize = fmt.boundingRect(QStringLiteral(" "));
                  QPoint position;   // This would be our current position
                  QStringList words = index.data(Qt::DisplayRole).toString().split(QStringLiteral(" "), QString::SkipEmptyParts); // Words array
                  for (qint32 i = 0, size = words.size(); i < size && position.y() < rect.height(); i++)  {
                       QRect wordSize = fmt.boundingRect(words[i]);
                       if (position.x() + wordSize.width() > rect.width())  {
                            // We go to a newline
                            position.x() = 0;
                            position.y() += newLineOffset;
                            newLineOffset = 0;
                       }
                       // Draw the word (you could insert your own style here)
                       painter->drawText(position, words[i]);
                       // Move right + space size
                       position.x() += wordSize.width() + spaceSize.width();
                       // Update line height
                       newLineOffset = qMax(newLineOffset, qMax(wordSize.height(), spaceSize.height()));
                  }
                  

                  EDIT:
                  Right I saw that you wrote a post. Yes, probably QTextDocument will be the more flexible way. Do consider the code sample I give you here though.

                  Kind regards.

                  P Offline
                  P Offline
                  panosk
                  wrote on 3 Dec 2015, 12:12 last edited by
                  #8

                  @kshegunov said:

                  EDIT:
                  Right I saw that you wrote a post. Yes, probably QTextDocument will be the more flexible way. Do consider the code sample I give you here though.

                  Kind regards.

                  Thank you very much for this, I will definitely try it!

                  1 Reply Last reply
                  0

                  7/8

                  3 Dec 2015, 12:06

                  • Login

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