QT 中通过自定义一个QWidget 来绘制一个车速表
车速表的绘制,网络上有很多可以参考的例子,本设计也是参考网上的例子加入一些新的功能实现显示车速,属性显示在页面,通过属性修改控件中相关的值。
首先需要绘制出来仪表盘,分成五个步骤来实现。
绘制背景
通过绘制两个圆形,半径分别是radius和radius*0.9,使用不同的背景色。
绘制刻度线
先设置一个起始角度Angle,两边对称,然后将其它的角度按照设定的最高车速均分。
painter->rotate(Angle);
double rotate = (double)(360 - (Angle * 2)) / 200;
绘制出刻度线,两个等分绘制一个刻度,10份绘制一个长一点的线
for (int i = 0; i <= 200; i++) {
QColor color = QColor(84, 84, 84);
if(i>120) color = QColor(250, 0, 0);
if((i % 10) == 0)
{
painter->setPen(QPen(color, 1.3*lineWidth));
painter->drawLine(0, r, 0, r / 1.2);
}
else if((i % 2) == 0)
{
painter->setPen(QPen(color, 1*lineWidth));
painter->drawLine(0, r, 0, r / 1.1);
}
painter->rotate(rotate);
}
刻度上添加数字
最高车速分成10份,总共显示10个数字。先要计算每一个数字的旋转角度。
int gap = (360-Angle*2) / 10;
再计算出每一个数字的坐标
int angle = 90+Angle+gap*i; //角度,10格子画一个刻度值
float angleArc =( angle % 360) * 3.14 / 180; //转换为弧度
int x = (r)*cos(angleArc);
int y = (r)*sin(angleArc);
QString speed = QString::number(i*20);
int w = (int)fm.width(speed);
int h = (int)fm.height();
x = x - w/2;
y = y + h/4;
painter->drawText(QPointF(x, y),speed);
绘制速度指针
使用QPolygon 来构造一个多边形,然后根据角度来旋转。
QPolygon pts;
int r = radius*0.6;
pts.setPoints(3, -2, 0, 2, 0, 0, r);
中心部分画出两个圆形出来。
// 画中心圆圈
QRadialGradient radial(0, 0, 14); //渐变
radial.setColorAt(0.0, QColor(100, 100, 100));
radial.setColorAt(1.0, QColor(250, 50, 50));
painter->setPen(Qt::NoPen); //填满没有边界
painter->setBrush(radial);
painter->drawEllipse(-7, -7, 14, 14);
绘制文字描述车速
painter->setFont(QFont("Arial", 8));
painter->setPen(QPen(QColor(255,255,255)));
QFontMetricsF fm = QFontMetricsF(painter->font());
QString speed = QString::number(percent) + " km/h";
int w = (int)fm.width(speed);
// int h = (int)fm.height();
painter->drawText(QPointF(-w/2, (int)(0.5*radius)),speed);
最后贴出完整的代码:
#ifndef DASHBOARD_H
#define DASHBOARD_H
#include <QWidget>
# include<componentInterface.h>
class dashboard: public componentInterface
{
Q_OBJECT
Q_PROPERTY(double minValue READ getMinValue WRITE setMinValue USER true)
Q_PROPERTY(double maxValue READ getMaxValue WRITE setMaxValue USER true)
Q_PROPERTY(double value READ getValue WRITE setValue USER true)
public:
dashboard(QWidget *parent = nullptr);
//获取和设置最小值
double getMinValue() const;
void setMinValue(double minValue);
//获取和设置最大值
double getMaxValue() const;
void setMaxValue(double maxValue);
void setValue(double ivalue) override;
double getValue() const;
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
QString name()override {return "dashboard";};
void setWindowTitle(const QString & value) override{ QWidget::setWindowTitle(value);};
void move(int ax, int ay) override {QWidget::move(ax,ay);};
void show() override{QWidget::show();};
QMetaObject getQMeat() override {return this->staticMetaObject;};
void setParent(QWidget * parent) override { QWidget::setParent(parent);};
void setComponentIndex(int index) override {ID=index;}
private:
void paintEvent(QPaintEvent *event) override;
bool eventFilter(QObject *watched, QEvent *event)override;
void drawBg(QPainter *painter);
void drawDial(QPainter *painter);
void drawScaleNum(QPainter *painter);
void drawIndicator(QPainter *painter);
void drawText(QPainter *painter);
private:
const static int radius;
double maxv;
double minv;
double percent {0};
int m_refSize {200};
int Angle =45;
bool isPressed; //鼠标是否按下
QPoint lastPoint; //鼠标按下最后坐标
int ID {-1};
signals:
void valueChanged(double value);
void clicked(bool,QObject *);
void doubleClicked();
void customContextMenuRequested(const QPoint &pos);
};
#endif // DASHBOARD_H
#include "dashboard.h"
#include<QPainter>
#include <QCoreApplication>
#include <QtMath>
#include<QDebug>
#include<QMouseEvent>
const int dashboard::radius = 100;
dashboard::dashboard(QWidget *parent)
{
maxv=200;
minv=0;
this->installEventFilter(this);
QWidget::setParent(parent);
}
void dashboard::setValue(double ivalue)
{
percent = ivalue;
update();
emit valueChanged(ivalue);
}
double dashboard::getValue() const
{
return percent;
}
QSize dashboard::sizeHint() const
{
return QSize(m_refSize, m_refSize);
}
QSize dashboard::minimumSizeHint() const
{
return QSize(30, 10);
}
void dashboard::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
float scale = qMin(width(),height());
painter.scale(scale/m_refSize,scale/m_refSize);
painter.translate(m_refSize/2,m_refSize/2); //设置坐标原点
drawBg(&painter);
drawDial(&painter);
drawScaleNum(&painter);
drawIndicator(&painter);
drawText(&painter);
QWidget::paintEvent(event);
}
void dashboard::drawBg(QPainter *painter)
{
int r = radius;
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(QColor(172, 172, 172));
painter->drawEllipse(-r, -r, r * 2, r * 2);
r = radius * 0.9;
painter->setBrush(QColor(40, 40, 40));
painter->setPen(Qt::NoPen);
painter->drawEllipse(-r, -r, r * 2, r * 2);
painter->restore();
}
void dashboard::drawDial(QPainter *painter)
{
int r = radius*0.85;
double lineWidth = 1;
painter->save();
painter->rotate(Angle);
double rotate = (double)(360 - (Angle * 2)) / 200;
for (int i = 0; i <= 200; i++) {
QColor color = QColor(84, 84, 84);
if(i>120) color = QColor(250, 0, 0);
if((i % 10) == 0)
{
painter->setPen(QPen(color, 1.3*lineWidth));
painter->drawLine(0, r, 0, r / 1.2);
}
else if((i % 2) == 0)
{
painter->setPen(QPen(color, 1*lineWidth));
painter->drawLine(0, r, 0, r / 1.1);
}
painter->rotate(rotate);
}
painter->restore();
}
void dashboard::drawScaleNum(QPainter *painter)
{
painter->save();
int r = (int)(radius*0.6);
painter->setFont(QFont("Arial", 9));
painter->setPen(QPen(QColor(255,255,255)));
QFontMetricsF fm = QFontMetricsF(painter->font());
int gap = (360-Angle*2) / 10;
for(int i=0; i<=10; i+=1)
{
int angle = 90+Angle+gap*i; //角度,10格子画一个刻度值
float angleArc =( angle % 360) * 3.14 / 180; //转换为弧度
int x = (r)*cos(angleArc);
int y = (r)*sin(angleArc);
QString speed = QString::number(i*20);
int w = (int)fm.width(speed);
int h = (int)fm.height();
x = x - w/2;
y = y + h/4;
painter->drawText(QPointF(x, y),speed);
}
painter->restore();
}
void dashboard::drawIndicator(QPainter *painter)
{
painter->save();
QPolygon pts;
int r = radius*0.6;
pts.setPoints(3, -2, 0, 2, 0, 0, r);
double degRotate =Angle + (360.0 - Angle - Angle) / 200 * percent;
//画指针
painter->rotate(degRotate);
QRadialGradient haloGradient(0, 0, 60, 0, 0); //辐射渐变,内部填充颜色
haloGradient.setColorAt(0, QColor(100, 100, 100));
haloGradient.setColorAt(1, QColor(250, 50, 50)); //red
painter->setPen(QColor(250, 150, 150)); // 边框颜色
painter->setBrush(haloGradient);
painter->drawConvexPolygon(pts);
// painter->restore();
// painter->save();
// 画中心圆圈
QRadialGradient radial(0, 0, 14); //渐变
radial.setColorAt(0.0, QColor(100, 100, 100));
radial.setColorAt(1.0, QColor(250, 50, 50));
painter->setPen(Qt::NoPen); //填满没有边界
painter->setBrush(radial);
painter->drawEllipse(-7, -7, 14, 14);
painter->restore();
}
void dashboard::drawText(QPainter *painter)
{
painter->save();
painter->setFont(QFont("Arial", 8));
painter->setPen(QPen(QColor(255,255,255)));
QFontMetricsF fm = QFontMetricsF(painter->font());
QString speed = QString::number(percent) + " km/h";
int w = (int)fm.width(speed);
// int h = (int)fm.height();
painter->drawText(QPointF(-w/2, (int)(0.5*radius)),speed);
painter->restore();
}
bool dashboard::eventFilter(QObject *watched, QEvent *event)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (event->type() == QEvent::MouseButtonPress) {
//限定鼠标左键
if (mouseEvent->button() == Qt::LeftButton) {
lastPoint = mouseEvent->pos();
isPressed = true;
emit clicked(isPressed,this);
return true;
}
else if (mouseEvent->button() == Qt::RightButton)
{
isPressed = true;
emit clicked(isPressed,this);
emit customContextMenuRequested ( mouseEvent->pos());
}
} else if (event->type() == QEvent::MouseMove) {
//允许拖动并且鼠标按下准备拖动
if (isPressed) {
int offsetX = mouseEvent->pos().x() - lastPoint.x();
int offsetY = mouseEvent->pos().y() - lastPoint.y();
int x = this->x() + offsetX;
int y = this->y() + offsetY;
if (true) {
//可以自行调整限定在容器中的范围,这里默认保留20个像素在里面
int offset = 20;
bool xyOut = (x + this->width() < offset || y + this->height() < offset);
bool whOut = false;
QWidget *w = (QWidget *)this->parent();
if (w) {
whOut = (w->width() - x < offset || w->height() - y < offset);
}
if (xyOut || whOut) {
return false;
}
}
this->move(x, y);
return true;
}
} else if (event->type() == QEvent::MouseButtonRelease) {
isPressed = false;
}
return QWidget::eventFilter(watched, event);
}
double dashboard::getMinValue() const
{
return minv;
}
void dashboard::setMinValue(double minValue)
{
minv=minValue;
}
double dashboard::getMaxValue() const
{
return maxv;
}
void dashboard::setMaxValue(double maxValue)
{
maxv=maxValue;
}
#ifndef COMPONENTINTERFACE_H
#define COMPONENTINTERFACE_H
#include<QString>
#include <QWidget>
#include<QtCore>
class componentInterface : public QWidget
{
public:
virtual ~componentInterface(){};
virtual QString name()=0;
virtual void setValue(double ivalue)=0;
virtual void setWindowTitle(const QString &)=0;
virtual void move(int ax, int ay)=0;
virtual void show()=0;
virtual QMetaObject getQMeat()=0;
virtual void setParent(QWidget * parent)=0;
virtual void setComponentIndex(int index)=0;
virtual int getComponentIndex()=0;
// virtual void clicked(bool,QWidget*)=0;
};
#endif // COMPONENTINTERFACE_H