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. How to change the text background in QTableView
QtWS25 Last Chance

How to change the text background in QTableView

Scheduled Pinned Locked Moved Unsolved General and Desktop
11 Posts 4 Posters 1.1k 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.
  • D deleted385

    I've a QLineEdit to filter items in the QTableView:

    Window::Window(QWidget *parent) : QWidget(parent){
        aModel = new QStandardItemModel();
        aModel->setColumnCount(2);
        for (int i = 0; i < 3; i++) {
            auto col1 = new QStandardItem("Left " + QString::number(i + 1));
            auto col2 = new QStandardItem("Right " + QString::number(i + 1));
            col2->setTextAlignment(Qt::AlignVCenter|Qt::AlignRight);
            aModel->appendRow(QList<QStandardItem*>() << col1 << col2);
            col1 = new QStandardItem("Right " + QString::number(i + 1));
            col2 = new QStandardItem("Left " + QString::number(i + 1));
            col2->setTextAlignment(Qt::AlignVCenter|Qt::AlignRight);
            aModel->appendRow(QList<QStandardItem*>() << col1 << col2);
        }
        auto lay = new QVBoxLayout(this);
        auto combo = new CustomCombo(aModel);
        auto line = new QLineEdit(this);
        line->setPlaceholderText("Filter");
        line->setClearButtonEnabled(true);
        auto table = new QTableView(this);
        auto tableProxy = new QSortFilterProxyModel(this);
        tableProxy->setSourceModel(aModel);
        tableProxy->setFilterKeyColumn(0);
        tableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
        table->setModel(tableProxy);
        table->horizontalHeader()->hide();
        table->verticalHeader()->hide();
        lay->addWidget(combo);
        lay->addWidget(line);
        lay->addWidget(table);
        setLayout(lay);
        connect(line, &QLineEdit::textChanged, [=]{tableProxy->setFilterFixedString(line->text());});
    }
    

    and it does the filter correctly:

    x1.gif

    What I want is highlight the matching string in the QTableView. Something like this:

    x2.gif

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

    @Emon-Haque
    You would need to write your own QStyledItemDelegate to do whatever highlighting and attach it to the QTableView via QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate).

    You will then need to persuade the view to redraw every time you type a new character causing the view to want to change its highlighting. Normally a QTableView only redraws in response to a model data change.

    D 1 Reply Last reply
    4
    • JonBJ JonB

      @Emon-Haque
      You would need to write your own QStyledItemDelegate to do whatever highlighting and attach it to the QTableView via QAbstractItemView::setItemDelegate(QAbstractItemDelegate *delegate).

      You will then need to persuade the view to redraw every time you type a new character causing the view to want to change its highlighting. Normally a QTableView only redraws in response to a model data change.

      D Offline
      D Offline
      deleted385
      wrote on last edited by
      #3

      @JonB, sounds complicated. Could you please provide an example to help me understand it better?

      1 Reply Last reply
      0
      • D Offline
        D Offline
        deleted385
        wrote on last edited by deleted385
        #4

        found a solution in other forum that works with this delegate:

        Delegate::Delegate(){ selection.setBackground(Qt::green); /*QTextCharFormat*/}
        void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
            if(index.column() == 0){
                QTextDocument doc(index.model()->data(index, Qt::DisplayRole).toString());
                int position = 0;
                QTextCursor cur;
                do {
                    cur = doc.find(query, position); // query is private QString
                    cur.setCharFormat(selection);
                    cur.movePosition(QTextCursor::Right);
                    position = cur.position();
                } while (!cur.isNull());
                painter->save();
                painter->translate(option.rect.x(), option.rect.y());
                doc.drawContents(painter);
                painter->restore();
                if(option.state == QStyle::State_Selected){
                    qDebug() << "here";
                    painter->fillRect(option.rect, Qt::red);
                }
            }
            else QStyledItemDelegate::paint(painter, option, index);
        }
        

        and these in mainwindow:

        Window::Window(QWidget *parent) : QWidget(parent){
            auto lay = new QVBoxLayout(this);
            auto hlay = new QHBoxLayout;
            auto textBox = new QLineEdit(this);
            auto label = new QLabel(this);
            label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
            hlay->addWidget(textBox);
            hlay->addWidget(label);
            auto table = new QTableView(this);
            lay->addLayout(hlay);
            lay->addWidget(table);
            auto model = new QStandardItemModel;
            for (int i = 0; i < 50000; i++) {
                auto col1 = new QStandardItem("Column 1 : " + QString::number(i));
                auto col2 = new QStandardItem("Column 2 : " + QString::number(i));
                model->appendRow(QList<QStandardItem*>() << col1 << col2);
            }
            proxy = new QSortFilterProxyModel(table);
            proxy->setSourceModel(model);
            proxy->setFilterKeyColumn(0);
            table->setModel(proxy);
            delegate = new Delegate();
            table->setItemDelegate(delegate);
            table->horizontalHeader()->hide();
            table->verticalHeader()->hide();
            label->setText(locale().toString(proxy->rowCount()));
            connect(textBox, &QLineEdit::textChanged, [=]{
                auto query = textBox->text();
                delegate->setQuery(query);
                proxy->setFilterFixedString(query);
                proxy->invalidate();
                label->setText(locale().toString(proxy->rowCount()));
            });
        }
        

        x1.gif

        Now, when I click on an item in column 1, it doesn't highlight and doesn't print qDebug() << "here";.

        mrjjM 1 Reply Last reply
        0
        • D Offline
          D Offline
          deleted385
          wrote on last edited by deleted385
          #5

          Another problem is when I've long text in the column I want to highlight. Here's what I wanted to achieve:

          x2.gif

          When I typed the word heard in search textbox it found 752 occurrences in this WPF app. Now here's the table without delegate:

          x3.gif

          No idea why does it get 750 matches instead of 752! If I use delegate, I don't see any content in 7th column:

          x4.gif

          the delegate is same as before except the first line in paint instead of if(index.column() == 0), I, now have, if(index.column() == 6) and here's the content of Window:

          Window::Window(QWidget *parent) : QWidget(parent){
              auto lay = new QVBoxLayout(this);
              auto hlay = new QHBoxLayout;
              auto textBox = new QLineEdit(this);
              auto label = new QLabel(this);
              label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
              hlay->addWidget(textBox);
              hlay->addWidget(label);
              auto table = new QTableView(this);
              lay->addLayout(hlay);
              lay->addWidget(table);
              auto db = QSqlDatabase::addDatabase("QSQLITE");
              db.setDatabaseName("bukhari.db");
              db.open();
              auto model = new QSqlTableModel;
              model->setTable("Hadith");
              model->select();
              while (model->canFetchMore()) model->fetchMore();
              db.close();
              proxy = new QSortFilterProxyModel(table);
              proxy->setSourceModel(model);
              proxy->setFilterKeyColumn(6);
              table->setModel(proxy);
              delegate = new Delegate();
              table->setItemDelegate(delegate);
              table->verticalHeader()->hide();
              label->setText(locale().toString(proxy->rowCount()));
              connect(textBox, &QLineEdit::textChanged, [=]{
                  auto query = textBox->text();
                  delegate->setQuery(query);
                  proxy->setFilterFixedString(query);
                  proxy->invalidate();
                  label->setText(locale().toString(proxy->rowCount()));
              });
              table->setVerticalScrollMode(QTableView::ScrollPerPixel);
              //table->resizeRowsToContents();
          }
          

          wanted to see the whole content in the last column BUT couldn't make it work.

          EDIT

          Found the difference 2, In WPF I actually have converted string to lower before applying filter BUT in Qt I didn't and there is two Heard.

          1 Reply Last reply
          0
          • D deleted385

            found a solution in other forum that works with this delegate:

            Delegate::Delegate(){ selection.setBackground(Qt::green); /*QTextCharFormat*/}
            void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
                if(index.column() == 0){
                    QTextDocument doc(index.model()->data(index, Qt::DisplayRole).toString());
                    int position = 0;
                    QTextCursor cur;
                    do {
                        cur = doc.find(query, position); // query is private QString
                        cur.setCharFormat(selection);
                        cur.movePosition(QTextCursor::Right);
                        position = cur.position();
                    } while (!cur.isNull());
                    painter->save();
                    painter->translate(option.rect.x(), option.rect.y());
                    doc.drawContents(painter);
                    painter->restore();
                    if(option.state == QStyle::State_Selected){
                        qDebug() << "here";
                        painter->fillRect(option.rect, Qt::red);
                    }
                }
                else QStyledItemDelegate::paint(painter, option, index);
            }
            

            and these in mainwindow:

            Window::Window(QWidget *parent) : QWidget(parent){
                auto lay = new QVBoxLayout(this);
                auto hlay = new QHBoxLayout;
                auto textBox = new QLineEdit(this);
                auto label = new QLabel(this);
                label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
                hlay->addWidget(textBox);
                hlay->addWidget(label);
                auto table = new QTableView(this);
                lay->addLayout(hlay);
                lay->addWidget(table);
                auto model = new QStandardItemModel;
                for (int i = 0; i < 50000; i++) {
                    auto col1 = new QStandardItem("Column 1 : " + QString::number(i));
                    auto col2 = new QStandardItem("Column 2 : " + QString::number(i));
                    model->appendRow(QList<QStandardItem*>() << col1 << col2);
                }
                proxy = new QSortFilterProxyModel(table);
                proxy->setSourceModel(model);
                proxy->setFilterKeyColumn(0);
                table->setModel(proxy);
                delegate = new Delegate();
                table->setItemDelegate(delegate);
                table->horizontalHeader()->hide();
                table->verticalHeader()->hide();
                label->setText(locale().toString(proxy->rowCount()));
                connect(textBox, &QLineEdit::textChanged, [=]{
                    auto query = textBox->text();
                    delegate->setQuery(query);
                    proxy->setFilterFixedString(query);
                    proxy->invalidate();
                    label->setText(locale().toString(proxy->rowCount()));
                });
            }
            

            x1.gif

            Now, when I click on an item in column 1, it doesn't highlight and doesn't print qDebug() << "here";.

            mrjjM Offline
            mrjjM Offline
            mrjj
            Lifetime Qt Champion
            wrote on last edited by
            #6

            hi

            try with

            if (option.state & QStyle::State_Selected)

            and not

            if(option.state == QStyle::State_Selected){

            to see if you get the qDebug() << "here";

            D 1 Reply Last reply
            2
            • mrjjM mrjj

              hi

              try with

              if (option.state & QStyle::State_Selected)

              and not

              if(option.state == QStyle::State_Selected){

              to see if you get the qDebug() << "here";

              D Offline
              D Offline
              deleted385
              wrote on last edited by
              #7

              @mrjj, yes that works, now I get the red background and debug output. The paint is called twice when I click an item.

              mrjjM 1 Reply Last reply
              0
              • D deleted385

                @mrjj, yes that works, now I get the red background and debug output. The paint is called twice when I click an item.

                mrjjM Offline
                mrjjM Offline
                mrjj
                Lifetime Qt Champion
                wrote on last edited by
                #8

                @Emon-Haque said in How to change the text background in QTableView:

                @mrjj, yes that works, now I get the red background and debug output. The paint is called twice when I click an item.

                The reason that it works is that option.state also have other states like item is editable and
                enabled SET already (default values) so you should use the & to test for other states as its a bit flag thing.

                Im not sure it means anything the paint is called twice. there can be many reasons for that.

                1 Reply Last reply
                2
                • D Offline
                  D Offline
                  deleted385
                  wrote on last edited by deleted385
                  #9

                  Had to set the document size to get some content:

                  Delegate::Delegate(){ selection.setBackground(Qt::green); /*QTextCharFormat*/}
                  void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
                      if(index.column() == 6){
                          if(option.state & QStyle::State_Selected)
                              painter->fillRect(option.rect, Qt::gray);
                  
                          QTextDocument doc(index.model()->data(index, Qt::DisplayRole).toString());
                          doc.setPageSize(QSizeF(option.rect.width(), option.rect.height()));
                          int position = 0;
                          QTextCursor cur;
                          do {
                              cur = doc.find(query, position); // query is private QString
                              cur.setCharFormat(selection);
                              cur.movePosition(QTextCursor::Right);
                              position = cur.position();
                          } while (!cur.isNull());
                          painter->save();
                          painter->translate(option.rect.x(), option.rect.y());
                          doc.drawContents(painter);
                          painter->restore();
                      }
                      else QStyledItemDelegate::paint(painter, option, index);
                  }
                  

                  and have hidden all columns except the last and also set stretch and resizemode at the end of Window constructor:

                  ...
                  table->setVerticalScrollMode(QTableView::ScrollPerPixel);
                  table->hideColumn(0);
                  table->hideColumn(1);
                  table->hideColumn(2);
                  table->hideColumn(3);
                  table->hideColumn(4);
                  table->hideColumn(5);
                  table->horizontalHeader()->setStretchLastSection(true);
                  table->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
                  

                  and now it's like this:

                  x5.gif

                  1. it takes a long time to respond when I resize,
                  2. content is jumbled,
                  3. when I select an item it takes a while to response,
                  4. it doesn't show whole content in the cell,
                  5. feels like it doesn't scroll per pixel, and
                  6. when I type in search box it shows up those text after a long time.

                  EDIT

                  Here's link of the project in GitHub for testing. The sample database, bukhari.db, has been included.

                  1 Reply Last reply
                  1
                  • D Offline
                    D Offline
                    deleted385
                    wrote on last edited by
                    #10

                    It's performance is similar without the delegate and with plain single column QListView. Looks like Qt model view architecture still isn't able to handle medium size dataset.

                    1 Reply Last reply
                    0
                    • U Offline
                      U Offline
                      uart
                      wrote on last edited by
                      #11

                      Hello, I'm designing a combobox that contains 2 column items, and can do filters and shorting based on what is typed in the combobox, the problem is that the combobox doesn't always appear when I write a search.

                      void Dialog_Sell::getCmbStock(){
                      QStandardItemModel *model= new QStandardItemModel(arrStock.size(), 2, this);
                      for(int i=0; i<arrStock.size(); i++){
                      QStandardItem *kol1 = new QStandardItem( QString("%0").arg(code[i]) );
                      QStandardItem *kol2 = new QStandardItem( QString("%0").arg(name[i]) );
                      model->setItem(i, 0, kol1);
                      model->setItem(i, 1, kol2);
                      }

                      delegate = new Delegate();
                      QTableView* tableView = new QTableView( this );
                      QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
                      
                      proxyModel->setSourceModel(model);
                      proxyModel->setFilterKeyColumn(-1);
                      tableView->setModel( proxyModel);
                      tableView->setItemDelegate(delegate);
                      tableView->verticalHeader()->setVisible(false);
                      tableView->horizontalHeader()->setVisible(false);
                      tableView->setColumnWidth ( 0, 60 );
                      tableView->setColumnWidth ( 1, 260 );
                      tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
                      //tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
                      tableView->setStyleSheet("background-color: rgb(255, 255, 255);");
                      tableView->setAutoScroll(false);
                      tableView->setShowGrid(false);
                      tableView->setAlternatingRowColors(true);
                      tableView->setSortingEnabled(false);
                      tableView->setVerticalScrollMode(QTableView::ScrollPerPixel);
                      
                      
                      connect(ui->cmbStock, &QComboBox::editTextChanged, [=]{
                          QString query = ui->cmbStock->currentText();
                          delegate->setQuery(query);
                          //proxyModel->setFilterRegularExpression(query);
                          proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
                          proxyModel->invalidate();
                          //return ;
                      });
                      
                      
                      ui->cmbStock->setModel(proxyModel);
                      ui->cmbStock->setView( tableView );
                      ui->cmbStock->setStyleSheet("QComboBox QAbstractItemView {min-width: 250px;}"
                                                  "QComboBox { background-color: white; }");
                      

                      }

                      qt combo.png

                      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