paintEvent is not painting complete objects



  • I am trying this piece of code below:

    widget.h

    #ifndef WIDGET_H
    #define WIDGET_H

    #include <QWidget>
    #include <QLabel>
    #include <QPainter>

    namespace Ui {
    class Widget;
    }

    class Widget : public QWidget
    {
    Q_OBJECT

    public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    void paintEvent(QPaintEvent *e);
    void operation();
    void drawLabel();
    
    int flag = 0;
    int startX, startY;
    QString labelText;
    

    private:
    Ui::Widget *ui;
    };

    #endif // WIDGET_H

    widget.cpp

    #include "widget.h"
    #include "ui_widget.h"

    Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
    {
    ui->setupUi(this);
    }

    Widget::~Widget()
    {
    delete ui;
    }

    void Widget::paintEvent(QPaintEvent *e)
    {
    QPainter *painter = new QPainter(this);
    if(flag == 1)
    {
    painter->setPen(QPen(Qt::black, 1));
    painter->drawLine(startX, startY, startX+30, startY);
    painter->drawLine(startX+50, startY, startX+80, startY);
    drawLabel();
    }
    }

    void Widget::drawLabel()
    {
    QLabel *label = new QLabel(this);
    label->setStyleSheet("border: 1px solid black");
    label->setGeometry(startX+30, startY-10, 20, 20);
    label->setText(labelText);
    label->show();
    }

    void Widget::operation()
    {
    int found = 1;

    if(found)
    {
        labelText = "2";
        startX = 20;
        startY = 50;
        flag = 1;
        update();
    
        labelText = "3";
        startX = 100;
        startY = 50;
        flag = 1;
        update();
    }
    

    }

    main.cpp

    #include "widget.h"
    #include <QApplication>

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    Widget w;
    w.operation();
    w.show();

    return a.exec();
    

    }

    I want to draw a label between two lines side by side, but when I run the above code label with labelText 3, between two lines is visible. labelText 2, between two lines is not painted. I don,t understand why? Can anybody help me to solve this?


  • Moderators

    @NIXIN
    you are doing multiple mistakes in your code.

    1. you are creating widgets on every paintEvent call! This is very bad design.
    2. in your Widget::operation() you update your data and call update() to immediately set the data again and call update() again. You should know that update() schedules a paint-event, where multiple calls to update() still result in a single paint-event (in the next event loop iteration). So it doesn't repaint immediately. There is a repaint() method for this, but as i said you already have a bad design by creating widgets on every paintEvent call.

    You should draw the text using QPainter instead creating a QLabel every time. But still the label text "2" would never be rendered, since you immediately overwrite it afterwards.



  • can you provide a small piece of code, how to do it?


  • Moderators

    @NIXIN

    void Widget::paintEvent(QPaintEvent *e)
    {
        QPainter painter(this);  // do create the painter on the stack
        ....
        drawLabel( p );
    }
    
    void Widget::drawLabel(QPainter & p)
    {
    QRect rect(startX+30, startY-10, 20, 20);
    p.setPen( QPen::QPen(Qt::black, 1.0, Qt::SolidLine) );
    p.setBrush( Qtt::NoBrush );
    p.drawRect( rect );
    p.drawText( rect, Qt::AlignCenter, labelText );
    }
    

    But as i said, this only draws the last label text set. You should store your data (position, label text,...) in a list (and call update() once). And draw each using drawLabel() for example.



  • That's fine, what I want to know is how to not delete the drawing from previous paintevent,
    because as per your statement:
    " But still the label text "2" would never be rendered, since you immediately overwrite it afterwards."
    I want to keep the drawing for both:

    if(found)
    {
    labelText = "2";
    startX = 20;
    startY = 50;
    flag = 1;
    update();

    labelText = "3";
    startX = 100;
    startY = 50;
    flag = 1;
    update();
    

    }


  • Moderators

    @NIXIN
    as i said store your data (e.g. struct) in a list and draw each item separately while iterating over the list in the paintEvent()



  • Can you please provide a small code


  • Moderators

    @NIXIN
    come on thats not that hard...

    struct Data {
        Data(QString t, int x, int y, int f)
             : labelText(t), startX(x), startY(y), flag(f)
        {
        }
    
        QString labelText;;
        int startX;;
        int startY;
        int flag;
    }
    QList<Data> dataList;
    ...
    void Widget::operation()
    {
    int found = 1;
    
    if(found)
    {
        dataList.clear();
    
        dataList << Data("2", 20, 50, 1);
        dataList << Data("3", 100, 50, 1);
        update();
    }
    
    }
    
    void Widget::paintEvent(QPaintEvent *e)
    {
        QPainter painter(this);  // do create the painter on the stack
        ....
        foreach( Data d, dataList )
             drawLabel( p, data );
    }
    
    void Widget::drawLabel(QPainter & p, const Data & d)
    {
    QRect rect(d.startX+30, d.startY-10, 20, 20);
    p.setPen( QPen::QPen(Qt::black, 1.0, Qt::SolidLine) );
    p.setBrush( Qt::NoBrush );
    p.drawRect( rect );
    p.drawText( rect, Qt::AlignCenter, d.labelText );
    }
    



  • Qt Champions 2018

    How about composing the widget instead of painting it.
    Put two QFrame with a HLine style and a label in a QVBoxLayout. So much easier!



  • @VRonin Depends on what he wants to achieve.
    Is it a widget, than I agree with you.
    Is it some CAD-drawing (looks like a diagram symbol to me), then I would prefer drawing.
    For the latter the Graphics View Framework would be more practical though.


Log in to reply