Qt编写自定义控件:圆形/半圆形仪表盘

cpp 复制代码
#include "widget.h"
#include <QPainter>
#include <QtMath>
#include <QDebug>
#include <QPainterPath>

QRect getEllipseRect(const QPoint &center, int rx, int ry)
{
    return QRect(center.x() - rx, center.y() - ry, 2 * rx, 2 * ry);
}

struct Graphical_DashboardPrivate
{
    int maximumValue{100};
    int maximumScaleValue{280};
    int startAngle;
    int endAngle;
    bool semiCircle{0};//是否半圆

    QColor backgroundColor{"#ff000000"};
    QColor borderGradient_topLeftColor{"#ffffffff"};
    QColor borderGradient_bottomRightColor{"#ff000000"};
    QColor scaleColor{"#ffffffff"};
    QColor scaleValueColor{"#ffffffff"};
    QColor pointerBorderColor{"#ffffffff"};
    QColor pointerbackgroundColor{"#ffA0A0A0"};
    QColor centralWheelGradient_insideColor{"#ff808080"};
    QColor centralWheelGradient_outsideColor{"#ffffffff"};

    int pointerBorderWidth{3};
    int centralWheelRadius{26};

    qreal anglePerVel;

    void calculateClockwiseRange();
    void drawFrame(QPainter &painter);
    void drawScale(QPainter &painter);
    void drawScaleReadings(QPainter &painter);
    void drawPointer(QPainter &painter);
};

void Graphical_DashboardPrivate::calculateClockwiseRange()
{
    if(maximumScaleValue >= 180)
    {
        startAngle = (360 - maximumScaleValue) / 2 + 90;  // 计算起始角度,转换为顺时针角度
        endAngle = (startAngle + maximumScaleValue) % 360;    // 计算结束角度,转换为顺时针角度
        anglePerVel = (360.0 - (startAngle - endAngle)) / maximumScaleValue;
    }
    else
    {
        startAngle = 270 - (maximumScaleValue/2);
        endAngle = startAngle + maximumScaleValue;
        anglePerVel = (endAngle - startAngle) / maximumScaleValue;
    }
}

//边框
void Graphical_DashboardPrivate::drawFrame(QPainter &painter)
{
    //500 = radius
    static QPoint origin{0,0};
    painter.save();
    painter.setPen(Qt::transparent);
    QLinearGradient lg(-500,-500,500,500);
    lg.setColorAt(0,borderGradient_topLeftColor);
    lg.setColorAt(1,borderGradient_bottomRightColor);
    painter.setBrush(lg);

    if(semiCircle)
    {
        auto rect = getEllipseRect(origin,500,500);
        int newHeight = rect.height() / 2;
        rect.setHeight(newHeight + 50);
        painter.setClipRect(rect);
    }
    painter.drawEllipse(origin,480,480);

    auto rect = getEllipseRect(origin,430,430);
    QPainterPath path;
    path.addEllipse(rect);
    painter.setClipPath(path);

    if(semiCircle)
    {
        int newHeight = rect.height() / 2;
        rect.setHeight(newHeight+12);
    }

    painter.fillRect(rect,backgroundColor);
    painter.restore();
}

//刻度
void Graphical_DashboardPrivate::drawScale(QPainter &painter)
{
    painter.save();
    painter.rotate(startAngle);//将坐标系顺时针旋转150°,到达起始位置

    int step = maximumScaleValue / 5;
    float angleStep;
    if(maximumScaleValue >= 180)
    {
        angleStep = (360.0 - (startAngle - endAngle)) / step;
    }
    else
    {
        angleStep = (endAngle - startAngle) / step;
    }

    for (int i = 0; i <= maximumScaleValue; i += 5)
    {
        if (i % 20 == 0)//粗线
        {
            painter.setPen(QPen(scaleColor,10));
            painter.drawLine(420, 0, 340, 0);
        }
        else if (i % 10 == 0)//中等
        {
            painter.setPen(QPen(scaleColor,6));
            painter.drawLine(420, 0, 380, 0);
        }
        else if (i % 5 == 0)//短线
        {
            painter.setPen(QPen(scaleColor,4));
            painter.drawLine(400, 0, 380, 0);
        }
        painter.rotate(angleStep);
    }
    painter.restore();
}

//刻度值
void Graphical_DashboardPrivate::drawScaleReadings(QPainter &painter)
{
    painter.save();
    painter.setPen(scaleValueColor);

    qreal x,y,w,h,angle,angleArc;

    auto font = painter.font();
    font.setPixelSize(45);
    painter.setFont(font);
    QFontMetricsF fm(font);

    for (int i = 0; i <= maximumScaleValue; i+= 20)
    {
        if (i % 20 == 0)
        {
            angle = 360 - (startAngle + i * anglePerVel); //角度
            angleArc = angle * 3.14 / 180; //转换为弧度

            x = 280 * cos(angleArc);
            y = -280 * sin(angleArc); // 负号的意义在于 Y轴正方向向下

            QString speed = QString::number(i * maximumValue/maximumScaleValue);

            w = fm.width(speed);
            h = fm.height();
            painter.drawText(QPointF(x - w / 2,y + h/4),speed);
        }
    }

    painter.restore();
}

//绘制指针
void Graphical_DashboardPrivate::drawPointer(QPainter &painter)
{
    painter.save();

    int currentValue = maximumScaleValue/2;
    float curAngle = startAngle + currentValue * anglePerVel;
    painter.rotate(curAngle); //旋转坐标系

    QRadialGradient haloGradient(0, 0, 60, 0, 0);  //辐射渐变
    haloGradient.setColorAt(0, pointerbackgroundColor.darker());
    haloGradient.setColorAt(1, pointerbackgroundColor);
    if(pointerBorderWidth == 0)
    {
        painter.setPen(Qt::transparent);
    }
    else
    {
        painter.setPen(QPen(pointerBorderColor,pointerBorderWidth));
    }
    painter.setBrush(haloGradient);

    const QPointF points[3]
    {
        QPointF(0.0, centralWheelRadius/2),
        QPointF(0.0, -centralWheelRadius/2),
        QPointF(300.0, 0),
    };
    painter.drawPolygon(points,3); //绘制指针
    painter.restore();

    painter.save();

    //绘制旋转中心
    QRadialGradient rg(0,0,40);
    rg.setColorAt(0.0,centralWheelGradient_insideColor);
    rg.setColorAt(0.5,centralWheelGradient_outsideColor);
    rg.setColorAt(1.0,centralWheelGradient_insideColor);
    painter.setPen(Qt::NoPen);
    painter.setBrush(rg);
    painter.drawEllipse(QPoint(0,0),centralWheelRadius,centralWheelRadius);

    painter.restore();
}


Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setPalette(QPalette(QPalette::Background,Qt::white));
    d_ptr = new Graphical_DashboardPrivate;
    d_ptr->calculateClockwiseRange();
}

Widget::~Widget()
{
}

void Widget::paintEvent(QPaintEvent *event)
{
    auto thisRect = rect();
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHints(QPainter::TextAntialiasing);
    painter.setRenderHints(QPainter::SmoothPixmapTransform);

    int side;
    if(d_ptr->semiCircle)
    {
        side = qMin(thisRect.width(), thisRect.height()*2);
        side = qMin(thisRect.width(), thisRect.height()*2 - (side/20) * 2);
        painter.translate(thisRect.width() / 2, thisRect.height() - side /20 );
    }
    else
    {
        side = qMin(thisRect.width(), thisRect.height());
        painter.translate(thisRect.width() / 2, thisRect.height() / 2);
    }

    painter.scale(side / 1000.0, side / 1000.0);

    d_ptr->drawFrame(painter);
    d_ptr->drawScale(painter);
    d_ptr->drawScaleReadings(painter);
    d_ptr->drawPointer(painter);
}

相关推荐
矛取矛求2 小时前
QT的前景与互联网岗位发展
开发语言·qt
Leventure_轩先生2 小时前
[WASAPI]从Qt MultipleMedia来看WASAPI
开发语言·qt
晓纪同学11 小时前
QT-简单视觉框架代码
开发语言·qt
威桑11 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服11 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
fyzy11 小时前
Qt获取本地计算的CPU温度
qt
cbdg375711 小时前
Qt 6 QML Settings location 不创建指定路径文件
qt
了一li11 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
杨德杰11 小时前
QT网络(一):主机信息查询
网络·qt
黄金右肾12 小时前
Qt之串口设计-线程实现(十二)
qt·thread·serialport