QTableWidget hover entire row without hard-coding background color



  • I've got a QTableWidget, and I want the entire row to be highlighted when mouse hovers on an item.
    All the solutions I read from other questions hard code the background color in code, which is not what I want.
    I'm using Qt stylesheet to set the background color of the hovered item:

    QTableWidget::item:hover {
    	background-color: #27292b;
    }
    

    And I tried to trick the items in a row into hovered state using a delegate:

    #include <QStyledItemDelegate>
    class RowHoverDelegate: public QStyledItemDelegate
    {
        Q_OBJECT
    public:
        RowHoverDelegate(QTableWidget *tableWidget, QObject *parent = 0);
        void paint(QPainter *painter, const QStyleOptionViewItem &option,
                   const QModelIndex &index) const;
    private:
        QTableWidget *m_tableWdt;
    };
    
    
    // in the implementation file:
    
    RowHoverDelegate::RowHoverDelegate(QTableWidget *tableWidget, QObject *parent)
        : QStyledItemDelegate(parent), m_tableWdt(tableWidget)
    {}
    
    void RowHoverDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                 const QModelIndex &index) const
    {
        if (option.state & QStyle::State_MouseOver)
        {
            QTableWidgetItem *hovItem = m_tableWdt->item(index.row(), index.column());
            if (hovItem)
            {
                int row = hovItem->row();
                int columnCount = m_tableWdt->columnCount();
    
                for (int col = 0; col < columnCount; ++col)
                {
                    QTableWidgetItem *item = m_tableWdt->item(row, col);
                    if (item)
                    {
                        QModelIndex idx = m_tableWdt->model()->index(row, col);
                        // copy option from the hovered item (opt is in State_MouseOver state)
                        QStyleOptionViewItem opt = option;
                        // update index and rect
                        opt.index = idx;
                        opt.rect = m_tableWdt->visualItemRect(item);
                        QStyledItemDelegate::paint(painter, opt, idx);  // paint the item
                    }
                }
                return;
            }
        }
        QStyledItemDelegate::paint(painter, option, index);
    }
    

    In the caller:

        m_tableWdt = new QTableWidget();
        // ...
        RowHoverDelegate *delegate = new RowHoverDelegate(m_tableWdt, m_tableWdt);
        m_tableWdt->setItemDelegate(delegate);
    

    This delegate causes the hovered item and items on the left hand side of it change background, the right hand side items remains unchanged, strange. Can anyone explain this ?



  • OK, I solved it. The key is to update the viewport of the QTableWidget as stated here by Steffen

    Here is the impoved delegate:

    class RowHoverDelegate : public QStyledItemDelegate
    {
        Q_OBJECT
    public:
        RowHoverDelegate(QTableWidget *tableWidget, QObject *parent = 0);
        void paint(QPainter *painter, const QStyleOptionViewItem &option,
                   const QModelIndex &index) const;
    
    protected slots:
        void onItemEntered(QTableWidgetItem *item);
    
    private:
        QTableWidget *m_tableWdt;
        int m_hoveredRow;
    };
    

    Implementation:

    RowHoverDelegate::RowHoverDelegate(QTableWidget *tableWidget, QObject *parent)
    	: QStyledItemDelegate(parent), m_tableWdt(tableWidget), m_hoveredRow(-1)
    {
        // mouse tracking have to be true, otherwise itemEntered won't emit
    	m_tableWdt->setMouseTracking(true);
    	connect(m_tableWdt, SIGNAL(itemEntered(QTableWidgetItem *)), this, SLOT(onItemEntered(QTableWidgetItem *)));
    }
    
    void RowHoverDelegate::onItemEntered(QTableWidgetItem *item)
    {
    	m_hoveredRow = item->row();
    	m_tableWdt->viewport()->update();  // force update
    }
    
    void RowHoverDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                                 const QModelIndex &index) const
    {
    	QStyleOptionViewItem opt = option;
    	if (index.row() == m_hoveredRow)
    	{
    		opt.state |= QStyle::State_MouseOver;
    	}
    	QStyledItemDelegate::paint(painter, opt, index);
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.