如何使表视图的角落项目的角落变圆?

huangapple go评论76阅读模式
英文:

How to make table view corner items corners round?

问题

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.

英文:

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-&gt;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-&gt;setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(option.rect, 5, 5);
painter-&gt;fillPath(path, option.backgroundBrush);
QStyledItemDelegate::paint(painter, option, index);

Then added it to the table widget object:

table-&gt;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-&gt;frame-&gt;setStyleSheet(&quot;* { border: 1px solid; border-radius: 5 }&quot;);
QWidget* child = qobject_cast&lt;QWidget*&gt;(ui-&gt;frame-&gt;children().at(0));
ui-&gt;frame-&gt;setFixedSize(child-&gt;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.

答案1

得分: 1

Solution

sigma 说道:
>看起来你可以通过构建一个带有圆角矩形形状的 QRegion 并使用 QWidget::setMask(QRegion const&) 来实现这个目标。

这使我找到了这个 答案如何实现QWidget的圆角,并基于它,我制作了以下的最小可重现示例:

#include &lt;QApplication&gt;
#include &lt;QTableWidget&gt;
#include &lt;QHeaderView&gt;
#include &lt;QPainterPath&gt;

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

    //以下只是我的设置
    //只是一个容器窗口
    QWidget* w = new QWidget();

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

    //填充表格
    table-&gt;insertColumn(0);
    table-&gt;insertColumn(1);
    table-&gt;insertRow(0);
    table-&gt;insertRow(1);
    table-&gt;insertRow(2);

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

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

    //移除表头
    table-&gt;verticalHeader()-&gt;setVisible(false);
    table-&gt;horizontalHeader()-&gt;setVisible(false);

    //表格和其项的样式表
    table-&gt;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-&gt;viewport()-&gt;setStyleSheet("background: rgb(4, 104, 38);"
                                     "border: 1px solid white;"
                                     "border-radius: 10px;"
                                     "color: black;");

    //解决方法
    //在表格视图上设置掩码
    //获取视图的矩形
    QRect rect = table-&gt;viewport()-&gt;rect();
    QPainterPath path;
    //我必须调整它以隐藏突出的角
    //只是让它变小
    rect.adjust(+1, +1, -1, -1);
    //将矩形添加到路径中,以便我们可以将其绘制为圆角矩形
    path.addRoundedRect(rect, 7, 7);
    //将圆角矩形提供给我们将用作掩码的区域
    QRegion mask = QRegion(path.toFillPolygon().toPolygon());
    //设置视图的掩码
    table-&gt;viewport()-&gt;setMask(mask);

    w-&gt;setMaximumSize(800, 600);
    w-&gt;show();

    return a.exec();
}

这是它的外观,你可以看到一些边缘和边框是粗糙的,但这是我能做到的最好的,看看是否可以调整样式表以使其看起来更好:

如何使表视图的角落项目的角落变圆?


关于你的方法的解释


第一种方法:

在这种情况下,样式表不起作用,因为 QTableWidgetItem 不是从 QWidget 派生的。

你唯一可以样式化它的方式是通过 ::item 子控件,但没有办法在没有状态的情况下获取单个项目。


第二种方法:

你没有指定你如何使用 QStyledItemDelegate::paint,所以我猜你没有正确覆盖它,看看这个 答案 上的信息:Qt subclassed QStyledItemDelegate paint method is never called

即使你修复了这个问题,你仍然需要绘制仅具有特定圆角的矩形。看看这个,了解如何做到:在Qt中绘制仅有2个角是圆角的矩形

我尝试过这种方法,我不建议使用它,因为为了获得所需的结果需要太多工作和混乱。


第三种方法:

使用 QFrame 作为容器只会增加另一层,这会导致表格

英文:

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 &lt;QApplication&gt;
#include &lt;QTableWidget&gt;
#include &lt;QHeaderView&gt;
#include &lt;QPainterPath&gt;
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-&gt;setGeometry(100,100,200,90);
table-&gt;setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
table-&gt;setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//filling the table
table-&gt;insertColumn(0);
table-&gt;insertColumn(1);
table-&gt;insertRow(0);
table-&gt;insertRow(1);
table-&gt;insertRow(2);
table-&gt;setItem(0,0,new QTableWidgetItem(&quot;item1&quot;));
table-&gt;item(0,0)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
table-&gt;setItem(1,0,new QTableWidgetItem(&quot;item2&quot;));
table-&gt;item(1,0)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
table-&gt;setItem(2,0,new QTableWidgetItem(&quot;item3&quot;));
table-&gt;item(2,0)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
table-&gt;setItem(0,1,new QTableWidgetItem(&quot;item4&quot;));
table-&gt;item(0,1)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
table-&gt;setItem(1,1,new QTableWidgetItem(&quot;item5&quot;));
table-&gt;item(1,1)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
table-&gt;setItem(2,1,new QTableWidgetItem(&quot;item6&quot;));
table-&gt;item(2,1)-&gt;setData(Qt::BackgroundRole, QVariant(QBrush(Qt::green)));
//removing headers
table-&gt;verticalHeader()-&gt;setVisible(false);
table-&gt;horizontalHeader()-&gt;setVisible(false);
//stylesheet of table and its items
table-&gt;setStyleSheet(&quot;QTableWidget&quot;
&quot;{&quot;
&quot;background: rgb(4, 104, 38);&quot;
&quot;border: 1px solid white;&quot;
&quot;border-radius: 10px;&quot;
&quot;color: black;&quot;
&quot;selection-background-color: transparent;&quot;
&quot;}&quot;
&quot;QTableWidget::item&quot;
&quot;{&quot;
&quot;border: 1px solid white;&quot;
&quot;border-radius: 0px;&quot;
&quot;}&quot;);
table-&gt;viewport()-&gt;setStyleSheet(&quot;background: rgb(4, 104, 38);&quot;
&quot;border: 1px solid white;&quot;
&quot;border-radius: 10px;&quot;
&quot;color: black;&quot;);
//SOLUTION
//setting mask on the table&#39;s viewport
//get the viewport&#39;s rect
QRect rect = table-&gt;viewport()-&gt;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&#39;ll use as a mask
QRegion mask = QRegion(path.toFillPolygon().toPolygon());
//set the viewport&#39;s mask
table-&gt;viewport()-&gt;setMask(mask);
w-&gt;setMaximumSize(800,600);
w-&gt;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:

如何使表视图的角落项目的角落变圆?


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.

huangapple
  • 本文由 发表于 2023年6月8日 19:39:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76431485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定