在简易版中我们只绘制了简单的文本和数值。改进版我们将速度和高度设计成滚动的刻度,并显示刻度值。添加航迹标和高度选择指针。
1. 速度和高度滚动刻度实现
实现步骤
- 定义滚动刻度
- 使用
QPainter
绘制一组分布均匀的刻度线和对应的数值。 - 刻度值随着飞行参数(速度、高度)的变化而动态滚动。
- 绘制指示框
- 在固定位置绘制一个窗口,作为指示器。
- 通过移动刻度,实现滚动效果。
- 实时更新数值
- 实现信号槽机制或定时器(
QTimer
)动态更新速度和高度,并重新绘制滚动效果。
完整代码示例
cpp
#include <QWidget>
#include <QPainter>
#include <QTimer>
public PFDWidget : public QWidget {
Q_OBJECT
public:
explicit PFDWidget(QWidget *parent = nullptr)
: QWidget(parent), airspeed(120), altitude(10000) {
// 定时器更新数据
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PFDWidget::updateData);
timer->start(100); // 每 100ms 更新一次
}
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 设置背景
painter.fillRect(rect(), Qt::black);
// 绘制速度滚动刻度
drawAirspeedScale(painter);
}
private:
double airspeed;
double altitude;
void drawAirspeedScale(QPainter &painter) {
int centerY = height() / 2;
int scaleWidth = 60;
// 保存状态
painter.save();
// 设置绘图区域
QRect airspeedRect(10, 10, scaleWidth, height()-20);
painter.setClipRect(airspeedRect);
// 绘制背景框
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::darkGray);
painter.drawRect(airspeedRect);
// 绘制刻度
painter.setPen(Qt::white);
int step = 10;
int range = 5;
int startValue = static_cast<int>(airspeed/step) *step;
for(int i=-range; i<=range; ++i) {
int value = startValue + i * step;
int y = centerY + (i * 30) - static_cast<int>((airspeed - startValue) * 3);
if(y >= 10 && y <= height() - 10) {
painter.drawText(10, y, scaleWidth-20, 20, Qt::AlignRight, QString::number(value));
painter.drawLine(scaleWidth-20, y, scaleWidth-5, y);
}
}
// 绘制固定指示框
painter.setPen(Qt::yellow);
painter.drawRect(5, centerY - 15, scaleWidth, 30);
// 显示当前数值
painter.drawText(5, centerY-15, scaleWidth, 30, Qt::AlignCenter, QString::number(static_cast<int>(airspeed)));
// 恢复状态
painter.restore();
}
void drawAltitudeScale(QPainter &painter) {
int centerY = height() / 2;
int scaleWidth = 60;
// 保存状态
painter.save();
// 设置绘图区域
QRect altitudeRect(width() - scaleWidth - 10, 10, scaleWidth, height()-20);
painter.setClipRect(altitudeRect);
// 绘制背景框
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::darkGray);
painter.drawRect(altitudeRect);
// 绘制刻度
painter.setPen(Qt::white);
int step = 100;
int range = 5;
int startValue = static_cast<int>(altitude/step)*step;
for(int i=-range; i<=range; ++i) {
int value = startValue + i * step;
int y = centerY + (i*30) - static_cast<int>((altitude-startValue)*0.3);
if(y >= 10 && y <= height()-10) {
painter.drawText(width() - scaleWidth, y, scaleWidth - 20, Qt::AlignLeft, QString::number(value));
painter.drawLine(width() - scaleWidth, y, width()-scaleWidth+15, y);
}
}
// 绘制固定指示框
painter.setPen(Qt::yellow);
painter.drawRect(width()-scaleWidth-5, centerY-15, scaleWidth, 30);
// 显示当前数值
painter.drawText(width()-scaleWidth-5, centerY-15, scaleWidth, 30, Qt::AlignCenter, QString::number(static_cast<int>(altitude)));
// 恢复状态
painter.restore();
}
private slots:
void updateData() {
// 模拟数据变化
airspeed += 0.5;
altitude += 2.0;
// 循环更新显示
if(airspeed > 200)
airspeed = 100;
if(altitude > 11000)
altitude = 10000;
update();
}
};
运行效果
- 滚动刻度
- 中间为当前值,刻度在窗口内上下滚动。
- 刻度会根据速度和高度动态更新,滚动效果流畅。
- 固定指示框
- 在中间绘制一个固定窗口,显示精确的当前值。
- 动态更新
- 使用定时器每 100ms 更新速度和高度,实现实时变化。
2. 添加航迹标和高度选择指针
这里的航迹标和高度选择指针以简单方式呈现,不涉及真实运行环境。
功能说明
- 航迹标
- 一个三角形符号,显示当前航迹角。
- 通常绘制带速度滚动刻度旁边。
- 高度选择指针
- 一个标记线或箭头,用于显示目标高度。
- 在高度滚动刻度上绘制。
修改后的代码
cpp
#include <QWidget>
#include <QPainter>
#include <QTimer>
public PFDWidget : public QWidget {
Q_OBJECT
public:
explicit PFDWidget(QWidget *parent = nullptr)
: QWidget(parent), airspeed(120), altitude(10000), heading(45), targetAltitude(10500) {
// 定时器更新数据
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PFDWidget::updateData);
timer->start(100); // 每 100ms 更新一次
}
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 设置背景
painter.fillRect(rect(), Qt::black);
// 绘制速度滚动刻度
drawAirspeedScale(painter);
// 绘制航迹标
drawHeadingMarker(painter);
// 绘制目标高度指针
drawTargetAltitudePointer(painter);
}
private:
double airspeed;
double altitude;
double heading;
double targetAltitude;
void drawAirspeedScale(QPainter &painter) {
int centerY = height() / 2;
int scaleWidth = 60;
// 保存状态
painter.save();
// 设置绘图区域
QRect airspeedRect(10, 10, scaleWidth, height()-20);
painter.setClipRect(airspeedRect);
// 绘制背景框
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::darkGray);
painter.drawRect(airspeedRect);
// 绘制刻度
painter.setPen(Qt::white);
int step = 10;
int range = 5;
int startValue = static_cast<int>(airspeed/step) *step;
for(int i=-range; i<=range; ++i) {
int value = startValue + i * step;
int y = centerY + (i * 30) - static_cast<int>((airspeed - startValue) * 3);
if(y >= 10 && y <= height() - 10) {
painter.drawText(10, y, scaleWidth-20, 20, Qt::AlignRight, QString::number(value));
painter.drawLine(scaleWidth-20, y, scaleWidth-5, y);
}
}
// 绘制固定指示框
painter.setPen(Qt::yellow);
painter.drawRect(5, centerY - 15, scaleWidth, 30);
// 显示当前数值
painter.drawText(5, centerY-15, scaleWidth, 30, Qt::AlignCenter, QString::number(static_cast<int>(airspeed)));
// 恢复状态
painter.restore();
}
void drawAltitudeScale(QPainter &painter) {
int centerY = height() / 2;
int scaleWidth = 60;
// 保存状态
painter.save();
// 设置绘图区域
QRect altitudeRect(width() - scaleWidth - 10, 10, scaleWidth, height()-20);
painter.setClipRect(altitudeRect);
// 绘制背景框
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::darkGray);
painter.drawRect(altitudeRect);
// 绘制刻度
painter.setPen(Qt::white);
int step = 100;
int range = 5;
int startValue = static_cast<int>(altitude/step)*step;
for(int i=-range; i<=range; ++i) {
int value = startValue + i * step;
int y = centerY + (i*30) - static_cast<int>((altitude-startValue)*0.3);
if(y >= 10 && y <= height()-10) {
painter.drawText(width() - scaleWidth, y, scaleWidth - 20, Qt::AlignLeft, QString::number(value));
painter.drawLine(width() - scaleWidth, y, width()-scaleWidth+15, y);
}
}
// 绘制固定指示框
painter.setPen(Qt::yellow);
painter.drawRect(width()-scaleWidth-5, centerY-15, scaleWidth, 30);
// 显示当前数值
painter.drawText(width()-scaleWidth-5, centerY-15, scaleWidth, 30, Qt::AlignCenter, QString::number(static_cast<int>(altitude)));
// 恢复状态
painter.restore();
}
void drawHeadingMarker(QPainter &painter) {
int markerX = width()/2 - 30;
QPolygon markker;
makrker << QPoint(markerX, height()/2-10)
<< QPoint(markerX-10, height()/2)
<< QPoint(markerX, height()/2+10);
painter.setBrush(Qt::yellow);
painter.setPen(Qt::yellow);
painter.drawPolygon(marker);
// 显示航向角度值
painter.setPen(Qt::white);
painter.drawText(markerX-50, height()/2-10, 40, 20, Qt::AlignRight, QString::number(static_cast<int>(heading));
}
void MainWindow::drawTargetAltitudePointer(QPainter &painter)
{
int scaleWidth = 60;
int centerY = height() / 2;
int step = 100;
int range = centerY - 30;
int deltaY = static_cast<int>((targetAltitude - altitude)*0.3);
int pointerY = centerY - deltaY;
if(pointerY >= 10 && pointerY <= height()-10) {
painter.setPen(Qt::cyan);
painter.drawLine(width()-scaleWidth-20, pointerY, width()-10, pointerY);
painter.setPen(Qt::cyan);
painter.drawText(width()-scaleWidth-80, pointerY-10, 60, 20, Qt::AlignRight, QString::number(static_cast<int>(targetAltitude)));
}
}
private slots:
void updateData() {
// 模拟数据变化
airspeed += 0.5;
altitude += 2.0;
heading += 1;
if(heading >= 360) heading -= 360;
targetAltitude = 10500 + qrand() % 500;
// 循环更新显示
if(airspeed > 200)
airspeed = 100;
if(altitude > 11000)
altitude = 10000;
update();
}
};
代码解析
- 航迹标
- 使用
QPolygon
绘制一个黄色三角形。 - 显示在速度刻度旁,附带显示当前航向角。
- 高度选择指针
- 根据目标高度和当前高度的差异,计算指针的位置。
- 使用一条水平线和数值标注指示目标高度。
- 实时更新
- 定时器出发航向和目标高度的动态变化,模拟飞行状态。