What is the correct way of paint my own background of widgets?



  • void XXX::resizeEvent(QResizeEvent *event)
    {
        const QString& txt = pdt->txt;
        if(txt.length()==0) return;
        int h = height();
        int w = width();
        int nw = this->fontMetrics().width(txt) + 1.25*h;
        if(nw>w){
            resize(nw,h);
        }
    }
    void XXX::paintEvent(QPaintEvent*)
    {
        int h = height();
        int w = width();
        QFont font = this->font();
        font.setPixelSize(h/2);
        this->setFont(font);
        //
        QPainterPath path;
        path.setFillRule(Qt::WindingFill);
        path.moveTo(h/2,0);
        path.arcTo(0,0, h,h, 90, 180);
        path.lineTo(w-h/2, h);
        path.arcTo(w-h,0, h,h, 270,180);
        path.closeSubpath();
        //
        QPainter painter;
        painter.begin(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.fillPath(path, pdt->currentBackgroundColor);
        QBrush brsh;
        brsh.setColor(Qt::black);
        painter.setBrush(brsh);
        if(pdt->txt.size()>0)
           painter.drawText(QPointF(h/2,2*h/3), pdt->txt);
        painter.end();
    }
    

    Above code is my way of painting background of Label widget.

    When I dynamically push more then one this widgets to QHBoxLayout, these labels will be partially overlaped with nearest labels.

    The next label's begin pos() is in previous label's paint region.

    My OS is macOS 10.12.5, My Qt Version is 5.9 Official.


  • Moderators

    @Manifolds said in What is the correct way of paint my own background of widgets?:

    When I dynamically push more then one this widgets to QHBoxLayout, these labels will be partially overlaped with nearest labels.
    The next label's begin pos() is in previous label's paint region.

    I do not quite understand this?! QHBoxlayout doesn't layout widgets overlapped.
    Normally you shouldn't be able to paint outside of the widget unless you have set some attributes (e.g. Qt::WA_PaintUnclipped) on the widget to paint outside of the clip region?

    Nevertheless you may want to try:

    painter.setClipRect( this->rect() );
    


  • @raven-worx
    painter.setClipRect( this->rect() );

    can't solve my problem.

    I just want to design a label widget that have any shape border and any color fill.
    I guess the reason is that, when I add the widget to the layout, it hasn't resize correctly, ie. reimplement the resizeEvent function is not enough.





  • @Manifolds
    I think you should not use resize() in resizeEvent(), the QHBoxLayout will handle the size of the widgets, the overlap may be caused by your resize.


  • Moderators

    You definitely shouldn't call resize() from resizeEvent(), paintEvent() or most other event handlers for that matter.
    A better way to handle overflowing text is to elide it. You also don't need path rendering to draw a rounded rectangle. There's a dedicated method for that. You also shouldn't change the widget's font from the paintEvent. Change the painter's font instead.
    For example:

    void ShapedTag::paintEvent(QPaintEvent*)
    {
        int radius = height() /2;
    
        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing);
        p.setBrush(pdt->currentBackgroundColor);
        p.setPen(Qt::transparent);
        p.drawRoundedRect(rect(), radius, radius);
    
        QFont f = font();
        f.setPixelSize(radius);
        p.setFont(f);
    
        QRect textRect = rect().adjusted(radius, 0, -radius, 0);
        QString text = QFontMetrics(f).elidedText(pdt->txt, Qt::ElideRight, textRect.width());
        p.setPen(Qt::black);
        p.drawText(textRect, Qt::AlignCenter | Qt::TextSingleLine, text);
    }
    

    If you don't want to elide the text you should rather calculate the size of the font based on the width, rather than height and then implement hasHeightForWidth() and heightForWidth() to make it proportional.



  • @Chris-Kawa said in What is the correct way of paint my own background of widgets?:

    void ShapedTag::paintEvent(QPaintEvent*)
    {
    int radius = height() /2;

    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    p.setBrush(pdt->currentBackgroundColor);
    p.setPen(Qt::transparent);
    p.drawRoundedRect(rect(), radius, radius);
    
    QFont f = font();
    f.setPixelSize(radius);
    p.setFont(f);
    
    QRect textRect = rect().adjusted(radius, 0, -radius, 0);
    QString text = QFontMetrics(f).elidedText(pdt->txt, Qt::ElideRight, textRect.width());
    p.setPen(Qt::black);
    p.drawText(textRect, Qt::AlignCenter | Qt::TextSingleLine, text);
    

    }

    Thank you for your reply.

    I want to design a uiLabel like label, it can auto resize the width that according my text content.
    Your solution can seperate the labels, but all the labels are have the same width if I do not calculate and set the layout stretches.


  • Moderators

    Right, so you can't elide the text if that's the effect you want.
    You'll have to put some constraints on your label.
    It can't have a dynamic font size because that changes the width of the text, which changes height of the label proportionally, which changes font size, which changes text width, which changes height of the label... I hope you see where this is going ;)

    So if you don't adjust font size dynamically all you have to do is implement minimumSizeHint() and return the calculated width of the text + the rounded rectangle margins.



  • @Chris-Kawa

    I follow your instruction and succeed.

    But It's not so smart, I will spend some time to read QT Source, and find out how the QLabel class being designed.

    I manually add height and font point size to the ShapedTag, and the function setText will calculate the widget width, and at last in funtion minimumSizeHint return the QSize I have calculated.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.