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

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能15 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G15 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt