Implementing Radar and Car Dashboard with QPainter in C++ Qt
Event Handling for Drawing
The QPaintEvent class in Qt handles drawing operations when widgets need repainting. This occurs during initial display, resizing, exposure after being obscured, or through explicit calls to update() or repaint().
Custom drawing is implemented by overriding paintEvent(QPaintEvent*) in QWidget subclasses:
void MyWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
// Drawing logic here
}
Painter Basics
Initialization and Settings
QPainter facilitates rendering on screens. It supports lines, shapes, text, and images.
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
Pen and Brush Configuration
Confiugre stroke and fill properties:
QPen pen;
pen.setColor(Qt::blue);
pen.setWidth(5);
painter.setPen(pen);
painter.setBrush(Qt::yellow);
Basic Shapes
Drawing text:
painter.drawText(rect(), Qt::AlignCenter, "Qt Example");
painter.drawText(600, 550, "Custom Position");
Drawing lines:
painter.drawLine(10, 200, 300, 20);
painter.drawLine(QLine(10, 300, 400, 30));
painter.drawLine(QPoint(10, 400), QPoint(300, 590));
Drawing rectangles:
painter.drawRect(QRect(500, 30, 200, 100));
Drawing ellipses:
painter.drawEllipse(QRect(500, 30, 200, 100));
painter.drawEllipse(QPoint(400, 300), 200, 100);
painter.drawEllipse(rect().center(), 200, 100);
painter.drawEllipse(QPoint(400, 500), 80, 80);
Drawing arcs:
QRect rectangle(650, 250, 100, 100);
int startAngle = 0 * 16;
int spanAngle = 270 * 16;
painter.drawArc(rectangle, startAngle, spanAngle);
Drawing pies:
int startAngle = 0 * 16;
int spanAngle = 120 * 16;
painter.drawPie(QRect(300, 200, 200, 200), startAngle, spanAngle);
Gradient Effects
Linear Gradients
Create gradients from one color to another along a line:
QLinearGradient linearGradient(0, 0, 100, 100);
linearGradient.setColorAt(0.0, Qt::red);
linearGradient.setColorAt(1.0, Qt::blue);
QBrush brush(linearGradient);
painter.setBrush(brush);
painter.drawRect(this->rect());
Radial Gradeints
Define color transitions from center outward:
QRadialGradient radialGradient(50, 50, 50);
radialGradient.setColorAt(0.0, Qt::yellow);
radialGradient.setColorAt(1.0, Qt::black);
QBrush brush(radialGradient);
painter.setBrush(brush);
painter.drawRect(this->rect());
Conical Gradients
Generate color rotations around a point:
QConicalGradient conicalGradient(100, 100, 0);
conicalGradient.setColorAt(0.0, Qt::red);
conicalGradient.setColorAt(0.5, Qt::blue);
conicalGradient.setColorAt(1.0, Qt::red);
QBrush brush(conicalGradient);
painter.setBrush(brush);
painter.drawRect(this->rect());
Simulated Radar Scanner
Coordinate Transformation
Shift the origin of the painter's coordinate system to the widget's center:
painter.translate(rect().center());
Radar Interface Implementation
Draws a black background, concentric circles, axes, and a conical gradient pie:
void Widget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(Qt::black);
painter.drawRect(rect());
QPen pen(Qt::green, 4);
painter.setPen(pen);
painter.translate(rect().center());
painter.setBrush(Qt::NoBrush);
for(int i = 50; i <= 300; i += 50)
painter.drawEllipse(QPoint(0, 0), i, i);
painter.drawLine(QPoint(-300, 0), QPoint(300, 0));
painter.drawLine(QPoint(0, -300), QPoint(0, 300));
QConicalGradient conGradient(0, 0, startAngle);
conGradient.setColorAt(0, QColor(0, 255, 0, 200));
conGradient.setColorAt(0.1, QColor(0, 255, 0, 100));
conGradient.setColorAt(0.2, QColor(0, 255, 0, 0));
conGradient.setColorAt(1, QColor(0, 255, 0, 0));
painter.setBrush(conGradient);
painter.setPen(Qt::NoPen);
painter.drawPie(QRect(-300, -300, 600, 600), startAngle * 16, spanAngle * 16);
}
Dynamic Scanning
Control the scan angle using a timer:
QTimer *timer = new QTimer(this);
timer->setInterval(20);
timer->start();
startAngle = 0;
spanAngle = 70;
connect(timer, &QTimer::timeout, [=]() {
startAngle += 1;
if(startAngle >= 360)
startAngle = 0;
update();
});
Car Dashboard Design
Initial Layout
Use translation and radial gradients for base design:
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(Qt::black);
painter.drawRect(rect());
painter.translate(rect().center());
QRadialGradient radialGradient(QPoint(0, 0), 300);
radialGradient.setColorAt(0.0, QColor(255, 0, 0, 50));
radialGradient.setColorAt(1.0, QColor(255, 0, 0, 250));
QBrush brush(radialGradient);
painter.setBrush(brush);
painter.drawEllipse(QPoint(0, 0), 300, 300);
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(Qt::white, 3));
painter.drawEllipse(QPoint(0, 0), 60, 60);
Scale Markings
Use rotation and save/restore to manage coordinate systems:
painter.rotate(135);
painter.drawLine(300 - 20, 0, 300 - 3, 0);
painter.setFont(QFont("宋体", 18));
painter.drawText(300 - 50, 12, QString::number(0));
double angle = 270 * 1.0 / 50;
for(int i = 1; i <= 50; i++) {
painter.rotate(angle);
if(i % 10 == 0) {
painter.drawLine(300 - 20, 0, 300 - 3, 0);
if(135 + angle * i < 270) {
painter.rotate(180);
painter.drawText(-300 + 30, 12, QString::number(i));
painter.rotate(180);
} else {
painter.drawText(300 - 65, 12, QString::number(i));
}
} else {
painter.drawLine(300 - 8, 0, 300 - 3, 0);
}
}
Pointer and Scan Area
Draw pointer and scan sector:
painter.restore();
painter.save();
painter.rotate(135 + spanAngle);
painter.drawLine(60, 0, 300 - 65, 0);
painter.restore();
painter.save();
QRect rect(-300 + 65, -300 + 65, 600 - 130, 600 - 130);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 51, 183, 200));
painter.drawPie(rect, -135 * 16, -spanAngle * 16);
Animated Pointer
Animate pointer movement:
QTimer *timer = new QTimer(this);
timer->start(15);
spanAngle = 0;
direction = 1;
connect(timer, &QTimer::timeout, [=]() {
if(direction == 1) {
spanAngle++;
if(spanAngle >= 273)
direction = 0;
} else {
spanAngle--;
if(spanAngle <= 0)
direction = 1;
}
update();
});
Display speed:
painter.restore();
painter.setFont(QFont("宋体", 18));
painter.drawText(QRect(-60, -60, 120, 120), Qt::AlignCenter, QString::number(int(spanAngle / 5.4)));
Optimized Dashboard Implemantation
Member Variables
private:
QTimer *timer;
int startAngle;
int spanAngle;
int direction;
int boardSpanAngle;
double angle;
void initCanvas(QPainter& painter);
void drawCurrentSpeed(QPainter& painter);
void drawScale(QPainter& painter, int radius);
void drawScaleText(QPainter& painter, int radius);
void drawSpeedLine(QPainter& painter, int length);
void drawSpeedPie(QPainter& painter, int radius);
void drawRedCircle(QPainter& painter, int radius);
void drawBlackCircle(QPainter& painter, int radius);
void drawOutShine(QPainter& painter, int radius);
void drawLogo(QPainter& painter, int radius);
Constructor Initialization
timer = new QTimer(this);
timer->start(1);
connect(timer, SIGNAL(timeout()), this, SLOT(timeOutHandler()));
startAngle = 150;
spanAngle = 0;
direction = 1;
boardSpanAngle = 240;
angle = boardSpanAngle * 1.0 / 60;
setFixedSize(800, 600);
Canvas Setup
void Widget::initCanvas(QPainter &painter) {
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(Qt::black);
painter.drawRect(rect());
QPoint center(width() / 2, height() * 0.6);
painter.translate(center);
}
Scale Drawing
void Widget::drawScale(QPainter &painter, int radius) {
painter.save();
painter.rotate(startAngle);
for(int i = 0; i <= 60; i++) {
if(i % 5 == 0) {
painter.setPen(QPen(Qt::white, 4));
if(i >= 40)
painter.setPen(QPen(Qt::red, 4));
painter.drawLine(radius - 20, 0, radius - 3, 0);
} else {
painter.setPen(QPen(Qt::white, 3.5));
if(i >= 40)
painter.setPen(QPen(Qt::red, 4));
painter.drawLine(radius - 8, 0, radius - 3, 0);
}
painter.rotate(angle);
}
}
Scale Labels
void Widget::drawScaleText(QPainter &painter, int radius) {
painter.restore();
painter.save();
int R = radius - 42;
painter.setPen(Qt::white);
QFont font("Arial", 13);
font.setBold(true);
painter.setFont(font);
for(int i = 0; i <= 60; i++) {
if(i % 5 == 0) {
painter.save();
int delx = qCos(qDegreesToRadians(210 - angle * i)) * R;
int dely = qSin(qDegreesToRadians(210 - angle * i)) * R;
painter.translate(QPoint(delx, -dely));
painter.rotate(-120 + angle * i);
painter.drawText(QRect(-30, -30, 60, 60), Qt::AlignCenter, QString::number(i * 4));
painter.restore();
}
}
}
Speed Sector
void Widget::drawSpeedPie(QPainter &painter, int radius) {
painter.restore();
QRect rect(-radius, -radius, radius * 2, radius * 2);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 0, 0, 80));
painter.drawPie(rect, -startAngle * 16, -spanAngle * 16);
}
Red Gradient Circle
void Widget::drawRedCircle(QPainter &painter, int radius) {
QRadialGradient radGradient(0, 0, radius);
radGradient.setColorAt(0, QColor(255, 0, 0, 250));
radGradient.setColorAt(1, QColor(0, 0, 0, 50));
painter.setBrush(radGradient);
painter.setPen(Qt::NoPen);
painter.drawEllipse(QPoint(0, 0), radius, radius);
}
Timer Handler
void Widget::timeOutHandler() {
if(direction == 1) {
spanAngle++;
if(spanAngle >= boardSpanAngle + 1)
direction = 0;
} else {
spanAngle--;
if(spanAngle <= 0)
direction = 1;
}
update();
}
Black Circle
void Widget::drawBlackCircle(QPainter &painter, int radius) {
painter.setBrush(Qt::black);
painter.setPen(Qt::NoPen);
painter.drawEllipse(QPoint(0, 0), radius, radius);
}
Speed Display
void Widget::drawCurrentSpeed(QPainter &painter) {
painter.setPen(Qt::white);
QFont font("Arial", 26);
font.setBold(true);
painter.setFont(font);
painter.drawText(QRect(-60, -60, 120, 80), Qt::AlignCenter, QString::number(spanAngle));
QFont font2("Arial", 12);
font2.setBold(true);
painter.setFont(font2);
painter.drawText(QRect(-60, -60, 120, 180), Qt::AlignCenter, "Km/h");
}
Outer Glow
void Widget::drawOutShine(QPainter &painter, int radius) {
QRect rect(-radius, -radius, radius * 2, radius * 2);
painter.setPen(Qt::NoPen);
QRadialGradient radGradient(0, 0, radius);
radGradient.setColorAt(0, QColor(255, 0, 0, 0));
radGradient.setColorAt(0.91, QColor(255, 0, 0, 0));
radGradient.setColorAt(0.96, QColor(255, 0, 0, 120));
radGradient.setColorAt(1, QColor(255, 0, 0, 255));
painter.setBrush(radGradient);
painter.drawPie(rect, -startAngle * 16, -60 * angle * 16);
}
Logo Display
void Widget::drawLogo(QPainter &painter, int radius) {
QRect rect(-100, radius * 0.38, 200, 60);
painter.drawPixmap(rect, QPixmap(":/icon.png"));
}