Qt Beginner QPainter and QRect
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.
Ash
Updated on July 24, 2022Comments
-
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 over 11 yearsThank you, I'll give it a look.
-
RTOSkit over 11 yearsI 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 over 11 yearsWow thanks for the tips :) you are a star!! this is exactly what I'm looking for!
-
Ash over 11 yearsHappy New year! Thanks for your help :)
-
RTOSkit over 11 yearsAlthough 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.