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
Forum Updated to NodeBB v4.3 + New Features

How to change the text background in QTableView

Scheduled Pinned Locked Moved Unsolved General and Desktop
11 Posts 4 Posters 1.3k Views 3 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.
  • 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