[SOLVED] Mousehover entire row selection in QTableView



  • Hi guys,

    I would like to do a simple thing, when I hover an item in my custom QTableView, make the entire row have a different background color.
    Unfortunately I cannot do this with stylesheet, as item are considered to be a row and a column, not an entire row.
    @QTableView::item:hover{ /* not working for entire row */
    }@

    I found a good thread convering this "here":http://qt-project.org/forums/viewthread/13099 but my problem is somewhat different because i'm not using a QStandardItemModel, I have my own model.

    I think i'm 90% there but I just have problem settting the background color on the entire row. I can already monitor when the user hover a new row, and print a message, but not change the color.
    The line : _modelSource->setData(inn, QBrush(QColor(Qt::yellow), Qt::SolidPattern), Qt::BackgroundRole); //CHANGE FOR THIS BACKGROUND COLOR
    Should change the color but it doesn't do anything right now.

    Thanks if you can give me some help!

    1- MyTableView.cpp (custom QTableView)
    @#include "mytableview.h"
    #include <QDebug>
    #include <sortfilterproxymodel.h>
    #include <workouttablemodel.h>

    MyTableView::MyTableView(QWidget *parent) : QTableView(parent) {

    this->setMouseTracking(true);
    currHovered = -1;
    

    }

    //-------------------------------------------------------------------------------------------------------
    void MyTableView::mouseMoveEvent(QMouseEvent* event) {

    QPoint pos = event->pos();
    
    QModelIndex index = indexAt(pos);
    int row = index.row();
    
    if (index.isValid()) {
    
         qDebug() << "MOUSE MOVED VALID INDEX! x:" <<pos.x() << " y:" << pos.y() << " ROW: " << row;
    
        if ( row == currHovered) return; //early exit
    
        SortFilterProxyModel *_model = static_cast<SortFilterProxyModel*>(model());
        WorkoutTableModel *_modelSource = static_cast<WorkoutTableModel*>(_model->sourceModel());
    
        for ( int col = 0; col < _modelSource->columnCount(); col++ ) {
    
            QModelIndex inn = _modelSource->index(row, col);
    
    
            QVariant dta = inn.data(Qt::BackgroundRole);   // CURRENT BACKGROUND COLOR
            qDebug() << "VARIANT: " << dta;
    
            // SET DATA NOT WORKING ?
            _modelSource->setData(inn, QBrush(QColor(Qt::yellow), Qt::SolidPattern), Qt::BackgroundRole);  //CHANGE FOR THIS BACKGROUND COLOR
            qDebug() << "changing color for row:" << row << " col:" << col;
    
        }
        currHovered = row;
    }
    

    }

    //------------------------------------------------------------------------------------------------------
    void MyTableView::mousePressEvent(QMouseEvent* event) {

    QTableView::mousePressEvent(event);
    
    if (event->buttons() == Qt::RightButton) {
        // TODO: Contextual menu on right click for item removal
        qDebug() << "Right BUTT";
    }
    

    }@

    //----------------- LOG EXAMPLE
    MOUSE MOVED VALID INDEX! x: 311 y: 58 ROW: 0
    VARIANT: QVariant(QBrush, QBrush(QColor(Invalid) , LinearGradientPattern ) )
    changing color for row: 0 col: 0
    VARIANT: QVariant(QBrush, QBrush(QColor(ARGB 1, 1, 1, 0) , SolidPattern ) )
    changing color for row: 0 col: 1
    VARIANT: QVariant(Invalid)
    changing color for row: 0 col: 2
    VARIANT: QVariant(Invalid)
    changing color for row: 0 col: 3
    VARIANT: QVariant(Invalid)
    changing color for row: 0 col: 4
    VARIANT: QVariant(Invalid)
    changing color for row: 0 col: 5



  • Turn out I need to implement the function
    @bool QAbstractItemModel::setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ) [virtual]@
    in my custom model for it to work, will post code when it does



  • I found a better solution with Delegate

    Added this function "mouseMoveEvent" below in my custom QTableView:
    The only problem I have now is that this rowDelegate overwrite one of my column delegate

    MyTableView.cpp
    @#include "mytableview.h"
    #include <QDebug>
    #include <sortfilterproxymodel.h>
    #include <workouttablemodel.h>
    #include "delegaterowhover.h"

    MyTableView::MyTableView(QWidget *parent) : QTableView(parent) {

    this->setMouseTracking(true);
    
    m_background_delegate = new delegateRowHover(this);
    myDelegate = new MyDelegate(this);
    
    setItemDelegateForColumn(5, myDelegate);
    

    }

    //-------------------------------------------------------------------------------------------------------
    void MyTableView::mouseMoveEvent(QMouseEvent* event) {

    QPoint pos = event->pos();
    QModelIndex index = indexAt(pos);
    int row = index.row();
    qDebug() << "ROW TO CHANGE COLOR: " << row;
    
    if (index.isValid()) {
    
        //        qDebug() << "MOUSE MOVED VALID INDEX! x:" <<pos.x() << " y:" << pos.y() << " ROW: " << row;
    
        SortFilterProxyModel *_model = static_cast<SortFilterProxyModel*>(model());
    
        for (int i=0; i<_model->rowCount(); i++) {
            for (int j=0; j<_model->columnCount(); j++) {
    
                if (i == row) {
                    QModelIndex inn = _model->index(i, j);
                    setItemDelegateForRow(inn.row(), m_background_delegate);
    
                }
                else {
                    QModelIndex inn = _model->index(i, j);
                    setItemDelegateForRow(inn.row(), new QStyledItemDelegate);
                }
            }
        }
    
    // TO FIX, row delegate remove my colum 5 delegate..
    

    // setItemDelegateForColumn(5, myDelegate);

    }
    

    }

    //------------------------------------------------------------------------------------------------------
    void MyTableView::mousePressEvent(QMouseEvent* event) {

    QTableView::mousePressEvent(event);
    
    if (event->buttons() == Qt::RightButton) {
        // TODO: Contextual menu on right click for item removal
        qDebug() << "Right BUTT";
    }
    

    }
    @



  • turn out I had to put all delegate in the same delegate class to fix that



  • While learning Qt I found a less complicated way to emulate row hovering.

    1- set mouse tracking to your QTableView and connect this slot:
    @ ui->tableView_workout->setMouseTracking(true);
    connect(ui->tableView_workout, SIGNAL(entered(QModelIndex)), this, SLOT(drawHoverWidget(QModelIndex)) );
    @

    2- Draw a QRubberBand whenever the selection change, will produce the same effect like a row hovering.
    @void Main_WorkoutPage::drawHoverWidget(QModelIndex index) {

    qDebug() << "drawHoverWidget"  << index.row();
    
    
    /// Dont redraw widget if row selection is the same
    if (index.row() == currentRowHovered)
        return;
    
    
    /// Hide last rubberBand
    rubberBandHover->hide();
    
    /// Change index column to first one (selection behavior: select row)
    QModelIndex newIndex =  ui->tableView_workout->model()->index(index.row(), 0,  QModelIndex() );
    QRect recIndex(ui->tableView_workout->visualRect(newIndex));
    
    rubberBandHover->move(recIndex.topLeft());
    rubberBandHover->resize(ui->tableView_workout->width(), recIndex.size().height());
    rubberBandHover->show();
    

    }@

    Seems to work well for now, only problem is you need to erase the widget when the mouse is no longer on a row, you can create a new signal "NoSelection" (on leaveEvent(QEvent *event) inside your QTableView, and emit the the "No Selection" signal when the row is invalid



  • DON'T use the above code, I found it was getting too complex, so i found this way on the web:
    http://qt-project.org/forums/viewthread/13099

    google > me
    Good luck



  • I got tired of all thoses complex solution and coded all of it directly in the delegate!
    Your delegate needs a pointer to it's QTableView for it to work..
    Here is what I have now:
    https://www.dropbox.com/s/7d5k499xh7e3t3r/selectHoverHell.png

    I must say this is a hell of a nightmare to code, just for a simple functionality, make me a sad panda sometimes that I chosse QT for the UI!! grr

    @void delegateRowHover::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

    QStyleOptionViewItemV4 opt = option;
    initStyleOption(&opt, index);
    
    int hoveredRow = -1;
    if(option.state & QStyle::State_MouseOver) {
        hoveredRow = index.row();
    }
    
    if (hoveredRow != -1)
    {
        /// Get most left index to get good QRect coords (row selection behavior)
        QModelIndex leftIndex = index.model()->index(hoveredRow, 0, QModelIndex());
        QRect recTopLeft(tableView->visualRect(leftIndex));
        recTopLeft.setWidth(tableView->width());
    
        painter->fillRect(recTopLeft, QBrush(Qt::red));
    }@


  • There you go, working solution after 1000x trial and error :

    That being said, really bad and not reusable, I have to modify the QRect size because the original is too big.

    @
    void delegateRowHover::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {

    QStyleOptionViewItemV4 opt = option;
    initStyleOption(&opt, index);
    
    
    int hoveredRow = -1;
    if(option.state & QStyle::State_MouseOver) {
        hoveredRow = index.row();
    }
    
    
    
    if (hoveredRow != -1)
    {
        if(option.state & QStyle::State_Selected) {
        }
        else {
            /// Get most left index to get good QRect coords (row selection behavior)
            QModelIndex leftIndex = index.model()->index(hoveredRow, 0, QModelIndex());
            QRect recTopLeft(tableView->visualRect(leftIndex));
            recTopLeft.setWidth(tableView->width()-1);
            recTopLeft.setHeight(68);
            painter->drawText(recTopLeft, "123");
            painter->drawRect(recTopLeft);
            qDebug() << recTopLeft;
        }
    }@

Log in to reply
 

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