Qt Beginner QPainter and QRect

43,142

Solution 1

The paint event is the method that is called by the paint system when a widget needs to be redrawn. That is why simply naming your own method does not work. It is never called by the paint system.

You really should be using the QPaintEvent. It gives you the rect that needs to be drawn. This rect will be based upon the size of the widget, so instead of using an explicit rect in your paint event, set your widget to the right size. A paint event will be generated should your widget ever move, resize, etc.

void MyWidget::paintEvent(QPaintEvent *event)
{
    QRect rect = event->rect();
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

Now if you want to separate your paint logic into another method, that is fine. But you would need to have it called from the paint event:

void MyWidget::paintEvent(QPaintEvent *event)
{
    QRect rect = event->rect();
    draw(rect);
}

void MyWidget::draw(QRect &rect)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    painter.drawText(rect, Qt::AlignCenter,
                      "Data");
    painter.drawRect(rect);
}

If you want to completely bypass the paint events as you said, and only want to create a static rectangle to display, one way is to just draw it once to a pixmap and display it in a QLabel:

QPixMap pix(200,100);
QPainter painter(&pix);
// do paint operations
painter.end()
someLabel.setPixmap(pix)

Solution 2

Any data that your paintEvent() needs should be accessible as fields of the containing class, in your case, private fields of MyWidget. These private fields can be exposed to clients of MyWidget via "setters" which would set the data values before calling update() on MyWidget which will trigger a call to paintEvent().

Solution 3

This playlist contains the best Qt tutorials , starting tutorial 74 would be useful for you (Qpainter and QPen), tutorial 75 is how to draw rectangles using QRect.

Solution 4

As well @Mat told you: the "event" is the correct way to launch a painter.
QPainter can only be evoked after a QPaintEvent event, which carries the safe region where the object may be drawn.

So you must find another strategy to transport your data, to help I will propose a method simple, which can be adjusted to many cases.

widget.cpp

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

#define MIN_DCX    (0.1)
#define MAX_DCX    (5.0)

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{    
    dcx=MIN_DCX;
    setFixedSize(170, 100);
}

void Widget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event); 
    QPainter painter;
    painter.begin(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::black);
    pcx=dcx*2;
    QRect rect = QRect(50-dcx,25-dcx,60+pcx,40+pcx);
    painter.drawText(rect, Qt::AlignCenter,printData);
    painter.drawRect(rect);
    painter.end();

}

void Widget::setPrintData(QString value){
   printData = value;
   dcx=(dcx>MAX_DCX)?MIN_DCX:dcx+MIN_DCX;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent);
    void setPrintData(QString value);

protected:
    void paintEvent(QPaintEvent *event);

private:
    QString printData;
    float dcx;
    float pcx;
};


#endif

window.cpp

#include <QtGui>
#include "widget.h"
#include "window.h"

#define MAX_SDCX  20

Window::Window()
    : QWidget()
{
    gobject = new Widget(this);

    textMode=1;
    rectMode=1;
    gobject->setPrintData(msgs[textMode]);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(gobject, 0, 0);
    setLayout(layout);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(dataOnAir()));
    timer->start(10);

    setWindowTitle(tr("Rect Shaking"));
}



void Window::dataOnAir(){
    if((++rectMode)>MAX_SDCX){
        rectMode=0;
        textMode^=1;
    }
    gobject->setPrintData(msgs[textMode]);
    gobject->repaint();
}

window.h

#ifndef WINDOW_H
#define WINDOW_H

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

class Window : public QWidget
{
    Q_OBJECT

public:
    Window();

private slots:
    void dataOnAir();

private:
    Widget *gobject;
    const QString msgs[2] = {"Hello","World"};
    int textMode;
    int rectMode;
};

#endif

main.cpp

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

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    Window window;
    window.show();
    return app.exec();
}

As you can see in the code is executed a timer, outside the object "widget"

every 10ms sends a repaint the widget to redraw a "rect" with a different size and every 20 cycles (200ms) changes the text "hello" for "world"

In this example you can see that in any way need overwrite the QPainterDevice architecture.

You may also notice that the "event" within the "paintEvent" is silenced and not used directly, but it is essential to execute a sequence QPainter.

Share:
43,142
Ash
Author by

Ash

Updated on July 24, 2022

Comments

  • Ash
    Ash almost 2 years

    How would I go about drawing a rectangle?

    I have tried two different ways;

    void MyWidget::paintEvent(QPaintEvent *)
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(Qt::black);
        QRect rect = QRect(290, 20, 70, 40);
        painter.drawText(rect, Qt::AlignCenter,
                          "Data");
        painter.drawRect(rect);
    }
    

    Which works fine (even though the parameter is not named nor used), but I don't want to use the QPaintEvent * I have no use for it.

    So I tried just renaming my function;

    void MyWidget::draw()
    {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setPen(Qt::black);
        QRect rect = QRect(290, 20, 70, 40);
        painter.drawText(rect, Qt::AlignCenter,
                          "Data");
        painter.drawRect(rect);
    }
    

    This doesn't display anything (yet has no errors).

    Why would it not work if I don't use QPaintEvent * ??

  • Ash
    Ash over 11 years
    Thank you, I'll give it a look.
  • RTOSkit
    RTOSkit over 11 years
    I apologize if some argument may seem reorder, I started to write a reply yesterday at 22:00 CET but then I have to go to dinner for the "New Year" and just now I got back, and publish complete response. I had not seen that others had writing.. Sorry. Happy Year to all!
  • Ash
    Ash over 11 years
    Wow thanks for the tips :) you are a star!! this is exactly what I'm looking for!
  • Ash
    Ash over 11 years
    Happy New year! Thanks for your help :)
  • RTOSkit
    RTOSkit over 11 years
    Although that your first solution working look elegant, your solution really just translates the region inherited from the event, and simply increases a call on the stack, the user should always set up your "data" outside the function, so I think this double call, no be useful. Sorry to disagree with your first solution. Your second easy solution, looks good.