qtqt5qtablewidgetqtstylesheetsmousehover

Qt stylesheet conflict between QTableView::item and cellWidget


I'm using Qt 5.15.2 on Windows. I have a QTableWidget which has a bunch of QComboBoxes as cell widgets. The application uses the following stylesheet:

QComboBox:hover{background: yellow;}
QTableView::item:hover{color: red;}

Now when showing the table and hovering over a QComboBox on the last visible row, the table starts auto-scrolling down, like so:

enter image description here

This doesn't happen if I hover over a normal QTableWidgetItem on the last visible row as shown on the image.

If I remove either of the hover keyword from the stylesheet, then things work fine but I lose my style. My guess is that there is a conflict between QTableView::item and QComboBox when it's used as a cell widget so I've tried being more explicit about which QComboBoxes the stylesheet should apply to by using

QTableView QComboBox:hover{background: yellow;}
QTableView::item:hover{color: red;}

but that didn't help either. Any suggestions on how to solve this please?

Here is the code I've used for this example:

#include <QTableWidget>
#include <QBoxLayout>
#include <QComboBox>
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    qApp->setStyleSheet(
                "QComboBox{background: yellow;}"
                "QTableView::item:hover{color: red;}"
                        );

    auto rows = 100;
    auto cols = 5;
    auto table = new QTableWidget(rows, cols);

    for(auto i = 0; i != rows; ++i)
    {
        for(auto j = 0; j != cols; ++j)
        {
            if(j == 0)
            {
                auto item = new QTableWidgetItem("hello");
                table->setItem(i, j, item);
            }
            else
            {
                auto cb = new QComboBox();
                cb->addItems(QStringList() << "Item1" << "Item2");
                table->setCellWidget(i, j, cb);
            }
        }
    }

    table->setMinimumSize(800,600);
    table->show();
    return a.exec();
}

UPDATE

As suggested by this answer, using setAutoScroll(false) works around the issue of the autoscrolling.

Unfortunately, the table loses the ability to autoscroll when the current item changes with the directional arrows as shown below.

I've also just confirmed that the whole issue described in my post is not reproducible with Qt versions prior to Qt 5.15, so I'm guessing this is a Qt regression bug.

enter image description here


Solution

  • Your QTableWidget seems to be auto-scrolling when you hover a partially visible item, my guess is that it treats it as a selected cell when hovered, which causes the auto-scroll.

    To fix your issue, you can simply do this:

    table->setAutoScroll(false);
    

    I applied it to your MRE and it worked.


    Reasoning behind this solution:

    I came up with the conclusion of your table auto-scrolling by using an eventFilter, which helped me notice QEvent::Move of the QComboBoxes, and if they are moving without me moving them, that means it's auto, so I googled this:

    qtablewidget disable ensure visible

    Which then led me to this answer.


    To get around the auto scroll disabled for key press, you can use an event filter to basically switch in on and off, here's how:

    main.cpp

    #include <QApplication>
    #include <QTableWidget>
    #include <QComboBox>
    #include <QObject>
    #include <QEvent>
    #include "myEventFilter.h"
    
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        qApp->setStyleSheet(
            "QComboBox{background: yellow;}"
            "QTableView::item:hover{color: red;}"
            );
        QString s = "della3";
        fprintf(stderr,"%s", s.toLatin1().data());
    
        auto rows = 100;
        auto cols = 5;
        auto table = new QTableWidget(rows, cols);
    
        for(auto i = 0; i != rows; ++i)
        {
            for(auto j = 0; j != cols; ++j)
            {
                if(j == 0)
                {
                    auto item = new QTableWidgetItem("hello");
                    table->setItem(i, j, item);
                }
                else
                {
                    auto cb = new QComboBox();
                    cb->addItems(QStringList() << "Item1" << "Item2");
                    table->setCellWidget(i, j, cb);
                }
            }
        }
    
        table->setMinimumSize(800,600);
        table->setAutoScroll(false);
    
        myEventFilter *filter = new myEventFilter();
        table->installEventFilter(filter);
        //give the address of table to the QTableWidget pointer of the eventFilter
        filter->t=table;
    
        table->show();
        
        return a.exec();
    }
    

    myEventFilter.h

    #ifndef MYEVENTFILTER_H
    #define MYEVENTFILTER_H
    
    #include <QObject>
    #include <QEvent>
    #include <QTableWidget>
    
    class myEventFilter : public QObject
    {
        Q_OBJECT
    
    public:
        myEventFilter (QObject *parent = nullptr) {};
        //I used the below pointer to point at table
        //since it won't be able to access otherwise as far as I know
        QTableWidget *t;
    
    protected:
        bool eventFilter(QObject *obj, QEvent *event) override
        {
            if(event->type() == QEvent::KeyPress)
            {
                t->setAutoScroll(true);
            }
            if(event->type() == QEvent::KeyRelease)
            {
                t->setAutoScroll(false);
            }
    
            return QObject::eventFilter(obj,event);
        };
    };
    
    #endif // MYEVENTFILTER_H