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);
}

相关推荐
徐霞客3204 小时前
Qt入门1——认识Qt的几个常用头文件和常用函数
开发语言·c++·笔记·qt
姆路4 小时前
QT Designer内存飙升
qt
Bruce小鬼6 小时前
QT文件基本操作
开发语言·qt
懷淰メ6 小时前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
Mr.Q11 小时前
OpenCV和Qt坐标系不一致问题
qt·opencv
重生之我是数学王子14 小时前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
----云烟----1 天前
QT中QString类的各种使用
开发语言·qt
「QT(C++)开发工程师」1 天前
【qt版本概述】
开发语言·qt
一路冰雨1 天前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
老赵的博客1 天前
QT 自定义界面布局要诀
开发语言·qt