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

相关推荐
笨笨马甲1 小时前
附加模块--Qt OpenGL模块功能及架构
开发语言·qt
uyeonashi5 小时前
【QT控件】输入类控件详解
开发语言·c++·qt
galaxy_strive15 小时前
绘制饼图详细过程
开发语言·c++·qt
委婉待续1 天前
Qt的学习(一)
开发语言·qt·学习
笨笨马甲1 天前
Qt Quick Layout功能及架构
开发语言·qt
feiyangqingyun1 天前
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
c++·qt·udp·gb28181
jllws11 天前
Qt学习及使用_第1部分_认识Qt---学习目的及技术准备
qt·c++框架
到点就困告1 天前
海康工业相机SDK二次开发(VS+QT+海康SDK+C++)
数码相机·qt·海康
唐墨1232 天前
android与Qt类比
android·开发语言·qt
道剑剑非道2 天前
QT开发技术【ffmpeg + QAudioOutput】音乐播放器 完善
开发语言·qt·ffmpeg