Non-pixelized rounded corner for top-level window

二次信任 提交于 2019-12-22 08:41:01

问题


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 ?


回答1:


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 )



回答2:


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);
}



回答3:


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



来源:https://stackoverflow.com/questions/3829317/non-pixelized-rounded-corner-for-top-level-window

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!