Qt Beginner QPainter and QRect
Asked Answered
K

5

9

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 * ??

Kampmeier answered 31/12, 2012 at 11:58 Comment(7)
Why do you expect a randomly named function to be called to do anything? Your first method is fine. Not naming parameters you don't need (yet) is both legal and idiomatic C++.Cati
Ok, would it be possible to get the first method to work if I didn't have the parameter, yet kept the rest the same?Kampmeier
No, that parameter is necessary whether you use it or not. I don't understand why you'd want to remove it - it doesn't cost you anything to ignore it.Cati
It causes issues for me later on in my code, I have other parameters passed i.e. const QString &data. Is there an alternative way to draw a rectangle, moving away from using event parameters? Thanks for your replies.Kampmeier
The signature for event functions is fixed, you cannot change them at all (except possibly for adding parameters with default values, but I wouldn't do that either even if it works). You need to describe your problem in more detail in your question, it looks like you're trying to do something "the wrong way".Cati
I have a detailsForm.cpp where the user enters the details of a packet. When this is sent in the mainwindow.cpp the packet details are displayed in a new tab. I had it working fine when I was just using QTextEdit. I wanted to change this to QGraphicsView and display the packet details inside of rectangles which make the packet look more realistic. You're right about me going about this the wrong way tbh. I'm just messing around trying to work it out.Kampmeier
If you are switching from widgets to using qgraphics, then why not just use QGraphicsRectItem or subclass a graphics item?Arachnoid
A
10

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)
Arachnoid answered 31/12, 2012 at 20:0 Comment(2)
Wow thanks for the tips :) you are a star!! this is exactly what I'm looking for!Kampmeier
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.Planarian
S
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().

Sukkah answered 31/12, 2012 at 19:49 Comment(0)
S
1

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.

Shippen answered 31/12, 2012 at 13:44 Comment(0)
P
1

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.

Planarian answered 1/1, 2013 at 3:47 Comment(2)
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!Planarian
Happy New year! Thanks for your help :)Kampmeier
S
0

Overriding the paintEvent() function of a widget enables you to customize the widget and this function is called periodically to redraw the widget. Therefore any drawing should be made in this function. However overriding paintEvent() may cause some performance issues. I would prefer using a QGraphicsScene and QGraphicsView then I would add a rectangle to the scene which is the common way of doing this kind of drawing stuff. Please check the GraphicsView Framework

http://qt-project.org/doc/qt-4.8/graphicsview.html

Stonefish answered 31/12, 2012 at 20:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.