4

I have a QTableWidget (alternatively, a QListWidget) which I want to style in the following way:

  • The "selected" color of the cells should be a certain fixed color. I am able to do this by using the following stylesheet:

    QTableWidget::item:selected { background-color: #FF0000 }
    
  • The "unselected" color of the cells should be set individually, according to a vector of colors that I have within my code. I am able to do this by assigning a QBrush to each QTableWidgetItem, when created:

    QColor col = color_vector.value(i);
    QBrush brush(col);
    item->setData(Qt::BackgroundRole, QVariant(brush));
    
  • The entire table should have rounded corners (essentially "clipping" the borders of the corner items in the table).

The last part I have not been able to achieve.


I have tried the following approaches:

  • Set the stylesheet:

I can round the corners of the table using this stylesheet:

QTableWidget 
{ 
    border: 1px solid;
    background: transparent;
    border-radius: 5px 
}

Even though this draws a rounded border around the table, the background of the individual cells still "pokes out" at the corners.

I can set the same border-radius for QTableWidget::item, but then all corners of all cells will be rounded, and not just at the edges of the table. Since QTableWidgetItem is itself not a QWidget, I don't think I can assign the styling to specific items.

  • Create a QDelegate:

I tried to subclass the QStyledItemDelegate and override the paint() function. But it doesn't seem to do much. Maybe I'm making an error (still very new to Qt, so very possible), or maybe the changes are overridden by the options I already set in the stylesheet/setData?

This is what I tried for the paint(painter, option, index) implementation:

painter->setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(option.rect, 5, 5);
painter->fillPath(path, option.backgroundBrush);
QStyledItemDelegate::paint(painter, option, index);

Then added it to the table widget object:

table->setItemDelegateForRow(0, new RoundedCornerDelegate(table));

This would not even be the final desired outcome yet (I'd still have to figure out a way to draw only one rounded corner), but it doesn't seem to work: the item's background is still drawn as a pure rectangle. Even if I change the brush in the above code to something different, I don't see a different-color rounded rectangle being drawn on top (maybe below?).

  • Put the table inside a QFrame:

I haven't used QFrame before, so I'm not sure if this is the intended use. But I created a QFrame in the UI editor, and added the table as a child. Then I tried:

ui->frame->setStyleSheet("* { border: 1px solid; border-radius: 5 }");
QWidget* child = qobject_cast<QWidget*>(ui->frame->children().at(0));
ui->frame->setFixedSize(child->size());

But this just gives weird results: the border around the QFrame only seems to exist where the child does not, and the child is not fully visible.

Borage
  • 43
  • 3
  • 1
    It looks like you might be able to achieve this by constructing a QRegion with a rounded rectangular shape and using [`QWidget::setMask(QRegion const&)`](https://doc.qt.io/qt-6/qwidget.html#setMask-1). However, this is something I haven't tried myself, so feel free to post your own answer if you figure it out. – sigma Jun 09 '23 at 19:20

1 Answers1

1

Solution

sigma said:

It looks like you might be able to achieve this by constructing a QRegion with a rounded rectangular shape and using QWidget::setMask(QRegion const&).

That allowed me to find this answer on: How to round QWidget corners, and based on it, I made the following minimal reproducible example:

#include <QApplication>
#include <QTableWidget>
#include <QHeaderView>
#include <QPainterPath>

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

    //the following is just my setup
    //just a container widget
    QWidget *w = new QWidget();

    QTableWidget *table = new QTableWidget(w);
    table->setGeometry(100,100,200,90);
    table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    //filling the table
    table->insertColumn(0);
    table->insertColumn(1);
    table->insertRow(0);
    table->insertRow(1);
    table->insertRow(2);

    table->setItem(0,0,new QTableWidgetItem("item1"));
    table->item(0,0)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
    table->setItem(1,0,new QTableWidgetItem("item2"));
    table->item(1,0)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
    table->setItem(2,0,new QTableWidgetItem("item3"));
    table->item(2,0)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));

    table->setItem(0,1,new QTableWidgetItem("item4"));
    table->item(0,1)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
    table->setItem(1,1,new QTableWidgetItem("item5"));
    table->item(1,1)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
    table->setItem(2,1,new QTableWidgetItem("item6"));
    table->item(2,1)->setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));

    //removing headers
    table->verticalHeader()->setVisible(false);
    table->horizontalHeader()->setVisible(false);

    //stylesheet of table and its items
    table->setStyleSheet("QTableWidget"
                         "{"
                            "background: rgb(4, 104, 38);"
                            "border: 1px solid white;"
                            "border-radius: 10px;"
                            "color: black;"
                            "selection-background-color: transparent;"
                         "}"
                         "QTableWidget::item"
                         "{"
                            "border: 1px solid white;"
                            "border-radius: 0px;"
                         "}");

    table->viewport()->setStyleSheet("background: rgb(4, 104, 38);"
                                     "border: 1px solid white;"
                                     "border-radius: 10px;"
                                     "color: black;");

    //SOLUTION
    //setting mask on the table's viewport
    //get the viewport's rect
    QRect rect = table->viewport()->rect();
    QPainterPath path;
    //I had to adjust it so it hides the corners that were poking out
    //just made it smaller
    rect.adjust(+1,+1,-1,-1);
    //add the rect to the path so we can draw it as a rounded one
    path.addRoundedRect(rect, 7, 7);
    //feed the rounded rect to a region that we'll use as a mask
    QRegion mask = QRegion(path.toFillPolygon().toPolygon());
    //set the viewport's mask
    table->viewport()->setMask(mask);

    w->setMaximumSize(800,600);
    w->show();

    return a.exec();
}

Here's how it looks, you can see that some of the edges and border are rough, but this is as good as I could make it, see if you can tweak the style sheets to make it look better:

Result: QTableWidget with round corners


Explanation regarding your methods


First Method:

Style sheets won't work in this case, because QTableWidgetItem is not derived from QWidget.

The only way you could style it is through ::item sub-control, but no way to get a single item without a state.


Second Method:

You haven't specified how exactly you're using QStyledItemDelegate::paint, so my guess is that you're not overriding it correctly, see this answer on Qt subclassed QStyledItemDelegate paint method is never called.

And event if you fix that, you'll then need to draw a rectangle with only specific corners round. Take a look at this to see how: Drawing Rectangle with only 2 corners rounded in Qt

I tried this method, and I advise against it, too much work and mess for the desired result.


Third Method:

Using a QFrame as a container would only add another layer, it would cause the same problem between the table widget and its viewport.