顶级窗口的非像素化圆角

发布于 2024-09-26 00:19:41 字数 933 浏览 7 评论 0原文

我想在 QDialog 上设置圆角。因为它是一个顶级窗口,所以 border-radius 不起作用,所以我必须这样做:

QRegion EnterPinDialog::roundedRect(const QRect& rect, int r)
{
    QRegion region;

    // middle and borders
    region += rect.adjusted(r, 0, -r, 0);
    region += rect.adjusted(0, r, 0, -r);

    // top left
    QRect corner(rect.topLeft(), QSize(r*2, r*2));
    region += QRegion(corner, QRegion::Ellipse);

    // top right
    corner.moveTopRight(rect.topRight());
    region += QRegion(corner, QRegion::Ellipse);

    // bottom left
    corner.moveBottomLeft(rect.bottomLeft());
    region += QRegion(corner, QRegion::Ellipse);

    // bottom right
    corner.moveBottomRight(rect.bottomRight());
    region += QRegion(corner, QRegion::Ellipse);

    return region;
}

并且我这样称呼它:

this->setMask(roundedRect(this->rect(), 8));

它可以工作,但问题是角是像素化的。

有没有办法在没有这些像素化角的情况下获得它?如果是,怎么办?

I want to set rounded corners on a QDialog. Since it is a top-level window, border-radius doesn't work, so I've to do this :

QRegion EnterPinDialog::roundedRect(const QRect& rect, int r)
{
    QRegion region;

    // middle and borders
    region += rect.adjusted(r, 0, -r, 0);
    region += rect.adjusted(0, r, 0, -r);

    // top left
    QRect corner(rect.topLeft(), QSize(r*2, r*2));
    region += QRegion(corner, QRegion::Ellipse);

    // top right
    corner.moveTopRight(rect.topRight());
    region += QRegion(corner, QRegion::Ellipse);

    // bottom left
    corner.moveBottomLeft(rect.bottomLeft());
    region += QRegion(corner, QRegion::Ellipse);

    // bottom right
    corner.moveBottomRight(rect.bottomRight());
    region += QRegion(corner, QRegion::Ellipse);

    return region;
}

and I call it this way :

this->setMask(roundedRect(this->rect(), 8));

It works, but the problem is that corners are pixelized.

Is there a way to get it without having these pixelized corners ? If yes, how ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

就是爱搞怪 2024-10-03 00:19:41

参加这个聚会有点晚了,但也许对其他人有帮助。这展示了如何通过在新的 QBitmap 上绘制来创建像素化程度较低的蒙版(它仍然没有真正抗锯齿,因为位图只有 2 种颜色,但曲线比直接使用 QPainterPath 平滑得多)。

就我而言,我想掩盖放置在主窗口内的小部件形状(作为中央小部件)。 4 个边缘周围有 4 个工具栏,我希望中心视图具有圆形边框,并让主窗口背景显示出来。正如 Harald 所建议的那样,这无法通过 CSS 实现,因为小部件的内容实际上并未剪辑到圆形边框。

// MainView is simply a QWidget subclass.
void MainView::resizeEvent(QResizeEvent *event)
{
    QBitmap bmp(size());
    bmp.clear();
    QPainter painter(&bmp);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(QColor(Qt::black));
    painter.setBrush(QColor(Qt::black));
    painter.drawRoundedRect(geometry(), 20.0f, 20.0f, Qt::AbsoluteSize);
    setMask(bmp);
}

它位于 resizeEvent 中,因为它需要知道当前小部件的大小(使用 size()geometry())。这是原始帖子的较短替代方案(我认为),但圆形边缘确实会像素化。

void MainView::resizeEvent(QResizeEvent *event)
{
    QPainterPath path;
    path.addRoundedRect(geometry(), 20.0f, 20.0f, Qt::AbsoluteSize);
    QRegion region = QRegion(path.toFillPolygon().toPolygon());
    setMask(region);
}

Kinda late to this party, but maybe it will help someone else. This shows how to create an less pixelated mask by drawing on top of a new QBitmap (it's still not really antialiased because a bitmap has only 2 colors, but the curve is much smoother than using a QPainterPath directly).

In my case I wanted to mask a widget shape which is placed within a main window (as central widget). There are 4 toolbars around the 4 edges, and I wanted the center view to have rounded borders and let the main window background show through. This was not doable via CSS as Harald suggests since the contents of the widget didn't actually clip to the rounded border.

// MainView is simply a QWidget subclass.
void MainView::resizeEvent(QResizeEvent *event)
{
    QBitmap bmp(size());
    bmp.clear();
    QPainter painter(&bmp);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(QColor(Qt::black));
    painter.setBrush(QColor(Qt::black));
    painter.drawRoundedRect(geometry(), 20.0f, 20.0f, Qt::AbsoluteSize);
    setMask(bmp);
}

It's in resizeEvent because it needs to know the current widget size (using size() and geometry()). Here's a shorter alternative of the original post (I think), but the rounded edges do get pixelated.

void MainView::resizeEvent(QResizeEvent *event)
{
    QPainterPath path;
    path.addRoundedRect(geometry(), 20.0f, 20.0f, Qt::AbsoluteSize);
    QRegion region = QRegion(path.toFillPolygon().toPolygon());
    setMask(region);
}
怪我鬧 2024-10-03 00:19:41

显示的 setAttribute( Qt.WA_TranslucentBackground, True) 方法 这里也有效...只需在顶级窗口上设置此属性,然后使用 paintEvent() 方法覆盖中的 QPainterPath 绘制窗口的形状。

这里有一些(python)代码,可以帮助您构造或绘制圆角矩形的 QPainterPath。

def drawPartiallyRoundedRect(painter,x,y,w,h, 
                             radiusTR, radiusBR, radiusBL, radiusTL,
                             doFill,fillColor,
                             doLine=False,lineColor=None,lineWidth=1,
                             antiAlias=True):


    w2 = int(w/2.0)
    h2 = int(h/2.0)


    if (doLine):
        x += lineWidth/2.0
        y += lineWidth/2.0
        w -= lineWidth
        h -= lineWidth


    T = y
    L = x
    R = x + w
    B = y + h

    # clamp values to fit within rect
    if (radiusTR > w2):
        radiusTR = w2
    if (radiusTR > h2):
        radiusTR = h2

    if (radiusTL > w2):
        radiusTL = w2
    if (radiusTL > h2):
        radiusTL = h2

    if (radiusBL > w2):
        radiusBL = w2
    if (radiusBL > h2):
        radiusBL = h2

    if (radiusBR > w2):
        radiusBR = w2
    if (radiusBR > h2):
        radiusBR = h2

    diamTR  = radiusTR + radiusTR
    diamBR  = radiusBR + radiusBR
    diamBL  = radiusBL + radiusBL
    diamTL  = radiusTL + radiusTL

    p = QPainterPath()
    if (radiusTR > 0.0):
        p.moveTo(R, T + radiusTR);
        p.arcTo(R-diamTR, T, diamTR, diamTR, 0.0, 90.0)  # TR
    else:
        p.moveTo(R,T)

    if (radiusTL > 0.0):
        p.arcTo(L, T, diamTL, diamTL, 90.0, 90.0)  # TL
    else:
        p.lineTo(L,T)

    if (radiusBL > 0.0):
        p.arcTo(L, B-diamBL, diamBL, diamBL, 180.0, 90.0);  # BL
    else:
        p.lineTo(L,B)

    if (radiusBR > 0.0):
        p.arcTo(R-diamBR, B-diamBR, diamBR, diamBR, 270.0, 90.0);  # BR
    else:
        p.lineTo(R,B)

    p.closeSubpath();

    if (antiAlias):
        painter.setRenderHint(QPainter.Antialiasing,True)
    else:
        painter.setRenderHint(QPainter.Antialiasing,False)

    if (doFill and fillColor):
        painter.setBrush( fillColor )
    elif ( doFill ): # pass doFill and None for fillColor to use current brush
        pass
    else:
        painter.setBrush( Qt.NoBrush )

    if ((lineWidth != 0.0) and doLine and lineColor):
        pen = QPen( lineColor, lineWidth,
                    Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )
        painter.setPen( pen )
    else:
        painter.setPen( Qt.NoPen )

    painter.drawPath( p )

The setAttribute( Qt.WA_TranslucentBackground, True) method shown here also works... Simply set this attribute on your top-level window, and paint the shape of your window using a QPainterPath in a paintEvent() method override.

Here's some (python) code that might help you construct, or paint, the QPainterPath for a rectangle with rounded corners.

def drawPartiallyRoundedRect(painter,x,y,w,h, 
                             radiusTR, radiusBR, radiusBL, radiusTL,
                             doFill,fillColor,
                             doLine=False,lineColor=None,lineWidth=1,
                             antiAlias=True):


    w2 = int(w/2.0)
    h2 = int(h/2.0)


    if (doLine):
        x += lineWidth/2.0
        y += lineWidth/2.0
        w -= lineWidth
        h -= lineWidth


    T = y
    L = x
    R = x + w
    B = y + h

    # clamp values to fit within rect
    if (radiusTR > w2):
        radiusTR = w2
    if (radiusTR > h2):
        radiusTR = h2

    if (radiusTL > w2):
        radiusTL = w2
    if (radiusTL > h2):
        radiusTL = h2

    if (radiusBL > w2):
        radiusBL = w2
    if (radiusBL > h2):
        radiusBL = h2

    if (radiusBR > w2):
        radiusBR = w2
    if (radiusBR > h2):
        radiusBR = h2

    diamTR  = radiusTR + radiusTR
    diamBR  = radiusBR + radiusBR
    diamBL  = radiusBL + radiusBL
    diamTL  = radiusTL + radiusTL

    p = QPainterPath()
    if (radiusTR > 0.0):
        p.moveTo(R, T + radiusTR);
        p.arcTo(R-diamTR, T, diamTR, diamTR, 0.0, 90.0)  # TR
    else:
        p.moveTo(R,T)

    if (radiusTL > 0.0):
        p.arcTo(L, T, diamTL, diamTL, 90.0, 90.0)  # TL
    else:
        p.lineTo(L,T)

    if (radiusBL > 0.0):
        p.arcTo(L, B-diamBL, diamBL, diamBL, 180.0, 90.0);  # BL
    else:
        p.lineTo(L,B)

    if (radiusBR > 0.0):
        p.arcTo(R-diamBR, B-diamBR, diamBR, diamBR, 270.0, 90.0);  # BR
    else:
        p.lineTo(R,B)

    p.closeSubpath();

    if (antiAlias):
        painter.setRenderHint(QPainter.Antialiasing,True)
    else:
        painter.setRenderHint(QPainter.Antialiasing,False)

    if (doFill and fillColor):
        painter.setBrush( fillColor )
    elif ( doFill ): # pass doFill and None for fillColor to use current brush
        pass
    else:
        painter.setBrush( Qt.NoBrush )

    if ((lineWidth != 0.0) and doLine and lineColor):
        pen = QPen( lineColor, lineWidth,
                    Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )
        painter.setPen( pen )
    else:
        painter.setPen( Qt.NoPen )

    painter.drawPath( p )
舟遥客 2024-10-03 00:19:41

根据您想要的对话框外观,您甚至可以通过 CSS 完全重新设计顶层窗口的样式,它们遵循盒模型,请参阅整个 样式表 文档。

border: 2px; border-radius 2px;

会给你一个 2px 宽、半径为 2px 的边框。

我一般来说你应该能够通过样式表处理大部分 ui 定制需求

Depending on what you want the dialog to look like you can completely restyle even toplevel windows via css, they adhere to the box model see the whole stylesheet documentation.

border: 2px; border-radius 2px;

Will give you a 2px wide border with a radius of 2px.

I general you should be able to handle most of your ui customisation needs through the stylesheets

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文