QT自定义控件--时钟

一、效果展示

在这里插入图片描述

二、绘制原理详解

1、绘制时钟盘

首先我们需要确定绘制一个时钟表盘需要哪些参数,在此我把黑色圆环称为外部,白色圆称为内部。绘制时钟表盘我们需要确定时钟的中心(Center_pos)、外部表盘的半径(R_Edge)、内部表盘的半径(R_Inside)、圆心指针端点小圆半径(R_Pointer)、外部表盘的颜色(Color_Edge)、内部表盘的颜色(Color_Inside)。
之后对表盘进行拆分绘制
①、先以中心为圆心,外部表盘的半径为半径,外部表盘的颜色绘制一个圆;②、再以中心为圆心,内部表盘的半径为半径,内部表盘的颜色绘制一个圆;③、最后以中心为圆心,圆心指针端点小圆半径为半径,圆心指针端点小圆颜色绘制一个圆,这样我们的时钟盘就绘制好了。

2、绘制刻度线

刻度线是一条线段,所以我们需要确定线段的起点和终点。那么我们怎么才能求出刻度线的起点和终点呢?我们需要确定刻度线起点和终点的半径和角度就可以算出每条刻度线对应的起点和终点坐标。
确定角度:我们知道时钟有十二个大刻度(分别是1-12),每个大刻度之间又有五个小刻度,所以总共有12 * 5 = 60个刻度;一个圆有360°,所以每个刻度占360° / 60 = 6°。==坐标是以圆心所在横线右方向为x正半轴,圆心所在竖线上方向为y轴正半轴,0°参考点为x轴正半轴,顺时针旋转。==我们从圆心正上方(即12点)处开始绘制,那么我们的基础仰角就是90° * 3 = 270°,依次往下是276°、282°,以此类推。
确定半径:我们以刻度线靠近外部的端点称为起点,靠近内部的端点称为终点。从实物图可以看出,起点的半径即为内部圆的半径,且每条刻度线的起点对应的半径是相等的;终点的半径分为大刻度半径和小刻度半径,大刻度半径明显要小于小刻度半径,在此我们设置一个相对于内部圆半径的偏移值就可以了,但是一定要保证大刻度半径的偏移值要大于小刻度半径的偏移值。
知道了每条刻度线起点与终点对应的半径和每条刻度线对应的角度之后,我们就可以采用如下公式计算出起点和终点了:

int x = 圆心的横坐标 + 半径 * cos(角度 * PI /180);
int y = 圆心的纵坐标 + 半径 * sin(角度 * PI /180);

公式详解如下:
在这里插入图片描述
求出起点和终点之后我们就直接画线段就好了。

3、绘制刻度值

我们只需要在标注大刻度的值(即1-12),绘制字体我们需要确定绘制的坐标和内容。内容相对简单,至于绘制的坐标,可参考绘制刻度线是求坐标的办法,基本上一模一样,在此就不赘述了。唯一需要注意的是,我们绘制刻度值时,是绘制一个矩形,然后往里面填入想要绘制的文字。QRect是以左上角的坐标为参考,如果我们把计算得到的文字应填入的坐标直接传给QRect,那么就会出现刻度线和刻度值没对齐的现象,尤其是位数多的刻度值(例如12),所以我们在绘制刻度值时,应对求出的坐标做适当的偏移,这样就可以改善刻度值与刻度线不对齐的问题了。

4、绘制指针

时钟的指针分为时针、分针和秒针,绘制原理基本上一模一样,所以在此就以绘制时针为例说明一下。
时针我们将其看作是一个等腰三角形,底边在中心指针圆域中,顶点指向刻度,所以我们需要知道三个点的坐标才可以绘制出时针(三角形)。三个点的坐标求法参考求刻度线坐标的方法求解,无非就是半径大小发生了变化,大家可以仔细思考一下。求出坐标之后,就直接依据三个坐标绘制三角形,并填充为想要的颜色就行了。

三、运行流程详解

我们设置了一个定时1秒的定时器,让其调用update()函数,从而调用paintEvent()函数,所以绘制图形是始终在刷新的。我们可以通过QTime::currentTime.hourQTime::currentTime.minute
QTime::currentTime.second分别获取系统的时分秒,然后将其赋给H、M、S,这样由于时分秒始终在更新,而指针的绘制又依据了时分秒,这样就可以看到时钟指示的是当前的时间,并且指针一直在正常的运转。

四、具体代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPaintEvent>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QtMath>
#include <QTimer>

#define PI 3.14

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    void paintEvent(QPaintEvent *);//重写绘制事件函数
    void Init_Parameter();//初始化参数函数
    void Set_Mask(QPainter *);//设置窗体透明
    void Draw_Clock(QPainter *);//绘制时钟
    void Draw_Broad(QPainter *);//绘制时钟盘
    void Draw_Dial(QPainter *);//绘制刻度盘
    void Draw_Text(QPainter *);//绘制刻度值
    void Draw_Pointer(QPainter *);//绘制指针

private:
    QTimer *timer;//定时器
    QPoint Center_pos;//时钟圆心坐标
    int R_Edge;//外部圆半径
    int R_Inside;//内部圆半径
    int R_Center;//中心小圆半径
    int R_Pointer;//中心指针圆半径
    QColor Color_Edge = QColor(0,0,0,255);//外部圆颜色
    QColor Color_Inside = QColor(255,255,255,255);//内部圆颜色
    QColor Color_Center = QColor(0,0,0,255);//中心小圆颜色
    int Div_Max = 12;//大刻度值
    int Div_Min = 5;//小刻度值
    float BaseAngle = 270;//基础仰角
    int H;//时
    int M;//分
    int S ;//秒

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include <QTime>
#include <QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    timer = new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(update()));//定时调用绘制事件函数
    timer->start(1000);//开启定时器,执行周期为1秒针


}

Widget::~Widget()
{

}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);//设置抗锯齿
    Draw_Clock(&painter);
}

void Widget::Draw_Clock(QPainter *painter)
{
    Init_Parameter();
    Set_Mask(painter);

    Draw_Broad(painter);
    Draw_Dial(painter);
    Draw_Text(painter);
    Draw_Pointer(painter);
}

void Widget::Init_Parameter()
{
    Center_pos = QPoint(this->width()/2,this->height()/2);//以窗体中心为圆心
    R_Edge = this->height()/2;//以窗体高度的一半为半径
    R_Inside = R_Edge-10;
    R_Center = 15;
    R_Pointer = 6;
    QTime Time = QTime::currentTime();//获取当前系统时间

    H=Time.hour();//分别设置时、分、秒
    M=Time.minute();
    S=Time.second();
}

void Widget::Set_Mask(QPainter *painter)
{
    painter->save();//保存

    QBrush brush = QBrush(Qt::transparent);//设置画刷为透明
    painter->setBrush(brush);
    painter->fillRect(this->rect(),QColor(0,0,0,0));

    painter->restore();//恢复
}

void Widget::Draw_Broad(QPainter *painter)
{
    painter->save();

    QBrush brush = QBrush(Color_Edge);//绘制外部圆
    painter->setBrush(brush);
    painter->drawEllipse(Center_pos,R_Edge,R_Edge);

    brush = QBrush(Color_Inside);//绘制内部圆
    painter->setBrush(brush);
    painter->drawEllipse(Center_pos,R_Inside,R_Inside);

    brush = QBrush(Color_Center);//绘制中心小圆
    painter->setBrush(brush);
    painter->drawEllipse(Center_pos,R_Center,R_Center);

    painter->restore();
}

void Widget::Draw_Dial(QPainter *painter)
{
    painter->save();

    for(int Loop = 0; Loop <= Div_Max*Div_Min; Loop++)
    {
        float Angle = BaseAngle + (360 / (Div_Max * Div_Min))*Loop;//从基础仰角开始绘制的每条刻度线对应的角度
        int R = R_Inside-1;
        int x_start = Center_pos.x() + R * cos((Angle / 180) * PI);
        int y_start = Center_pos.y() + R * sin((Angle / 180) * PI);

        if(Loop % Div_Min == 0)//判断是否为大刻度
        {
            QPen pen(Qt::black);
            pen.setWidth(4);
            painter->setPen(pen);
            R = R_Inside-20;
        }
        else  //判断是否为小刻度
        {
            QPen pen(Qt::black);
            pen.setWidth(2);
            painter->setPen(pen);
            R = R_Inside-15;
        }

        int x_end = Center_pos.x() + R * cos((Angle / 180) * PI);
        int y_end = Center_pos.y() + R * sin((Angle / 180) * PI);

        painter->drawLine(QPoint(x_start,y_start),QPoint(x_end,y_end));//绘制刻度线
    }


    painter->restore();
}

void Widget::Draw_Text(QPainter *painter)
{
    painter->save();

    QPen qPen(Qt::black);
    qPen.setWidth(5);   //设置画笔的粗细
    painter->setPen(qPen);
    QFont qFont("楷体",28,QFont::Bold,false);
    painter->setFont(qFont);

    int Dial_Text = 12;
    for(int Loop = 0;Loop < Div_Max;Loop++)
    {
        if(Dial_Text >12 )
            Dial_Text = 1;
        int R = R_Inside-60;
        float Angle = BaseAngle + (360 / Div_Max )*Loop;
        int x = Center_pos.x() + R * cos((Angle / 180) * PI);
        int y = Center_pos.y() + R * sin((Angle / 180) * PI);

        painter->drawText(QRect(x-20,y-20,80,80),QString::number(Dial_Text++));
    }
    painter->drawText(Center_pos.x()-60,Center_pos.y()+60,"劳力士");


    painter->restore();
}

void Widget::Draw_Pointer(QPainter *painter)
{
    painter->save();

    QBrush qBrush = QBrush(QColor(Qt::black));
    painter->setBrush(qBrush);
    QPen qPen(Qt::black);
    qPen.setWidth(2);   //设置画笔的粗细
    painter->setPen(qPen);
//**********绘制秒针***********************************************************************************
    float Angle = BaseAngle + (360 / (Div_Max * Div_Min))*S;
    float RightAngle = Angle + 90;//右侧角度
    float LeftAngle = Angle - 90;//左侧角度
    int R = R_Inside-1;
    int x_start = Center_pos.x() + R * cos((Angle / 180) * PI);
    int y_start = Center_pos.y() + R * sin((Angle / 180) * PI);

    R = R_Pointer-1;
    int x_end1 = Center_pos.x() + R * cos(RightAngle * 3.14 / 180);//过圆心的右侧切点
    int y_end1 = Center_pos.y() + R * sin(RightAngle * 3.14 / 180);
    int x_end2 = Center_pos.x() + R * cos(LeftAngle * 3.14 / 180);//过圆心的左侧切点
    int y_end2 = Center_pos.y() + R * sin(LeftAngle * 3.14 / 180);

    QPointF qTriangle_S[3] = {QPoint(x_start,y_start),QPoint(x_end1,y_end1),QPoint(x_end2,y_end2)};
    painter->drawPolygon(qTriangle_S,3);
//**********绘制分针***********************************************************************************
    Angle = BaseAngle + (360 / (Div_Max * Div_Min))*M;
    RightAngle = Angle + 90;//右侧角度
    LeftAngle = Angle - 90;//左侧角度
    R = R_Inside-60;
    x_start = Center_pos.x() + R * cos((Angle / 180) * PI);
    y_start = Center_pos.y() + R * sin((Angle / 180) * PI);

    R = R_Pointer-1;
    x_end1 = Center_pos.x() + R * cos(RightAngle * 3.14 / 180);//过圆心的右侧切点
    y_end1 = Center_pos.y() + R * sin(RightAngle * 3.14 / 180);
    x_end2 = Center_pos.x() + R * cos(LeftAngle * 3.14 / 180);//过圆心的左侧切点
    y_end2 = Center_pos.y() + R * sin(LeftAngle * 3.14 / 180);

    QPointF qTriangle_M[3] = {QPoint(x_start,y_start),QPoint(x_end1,y_end1),QPoint(x_end2,y_end2)};
    painter->drawPolygon(qTriangle_M,3);
//**********绘制时针***********************************************************************************
    Angle = BaseAngle + (360 / Div_Max)*H;
    RightAngle = Angle + 90;//右侧角度
    LeftAngle = Angle - 90;//左侧角度
    R = R_Inside-120;
    x_start = Center_pos.x() + R * cos((Angle / 180) * PI);
    y_start = Center_pos.y() + R * sin((Angle / 180) * PI);

    R = R_Pointer-1;
    x_end1 = Center_pos.x() + R * cos(RightAngle * 3.14 / 180);//过圆心的右侧切点
    y_end1 = Center_pos.y() + R * sin(RightAngle * 3.14 / 180);
    x_end2 = Center_pos.x() + R * cos(LeftAngle * 3.14 / 180);//过圆心的左侧切点
    y_end2 = Center_pos.y() + R * sin(LeftAngle * 3.14 / 180);

    QPointF qTriangle_H[3] = {QPoint(x_start,y_start),QPoint(x_end1,y_end1),QPoint(x_end2,y_end2)};
    painter->drawPolygon(qTriangle_H,3);

    painter->restore();
}
  • 9
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
旋转仪表盘是一种常见的显示控件,可以用来展示一些数据,如速度、油量、温度等,下面我将介绍如何使用Qt编写一个旋转仪表盘控件。 首先,我们需要在Qt中创建一个新的自定义控件类。可以通过Qt Creator中的“添加新文件”功能来创建一个QWidget派生类。在这个类中,我们需要实现paintEvent()函数来绘制仪表盘。 在paintEvent()函数中,我们可以使用QPainter来绘制仪表盘的各个部分,包括刻度线、指针、文字等。具体实现可以参考以下代码: ```C++ void RotatingDial::paintEvent(QPaintEvent *event) { // 设置背景色 QPalette pal(palette()); pal.setColor(QPalette::Background, Qt::white); setAutoFillBackground(true); setPalette(pal); // 绘制刻度线 QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.translate(width() / 2, height() / 2); painter.setPen(QPen(Qt::black, 2)); for (int i = 0; i < 60; i++) { painter.drawLine(0, -100, 0, -90); painter.rotate(6); } // 绘制指针 painter.save(); painter.rotate(m_angle); painter.setBrush(Qt::red); painter.setPen(Qt::NoPen); painter.drawConvexPolygon(m_pointer, 3); painter.restore(); // 绘制文字 QFont font("Arial", 12, QFont::Bold); painter.setFont(font); painter.drawText(-30, 50, "Speed"); } ``` 在上面的代码中,我们首先设置了控件的背景色为白色。然后使用QPainter绘制了60条刻度线,并旋转6度。接着,我们绘制了一个红色的指针,并根据m_angle属性来旋转指针的角度。最后,我们使用QPainter绘制了文字“Speed”。 在我们的自定义控件中,我们需要一个属性来控制指针的角度。我们可以使用Q_PROPERTY宏来定义这个属性,例如: ```C++ class RotatingDial : public QWidget { Q_OBJECT Q_PROPERTY(int angle READ angle WRITE setAngle) public: RotatingDial(QWidget *parent = nullptr); int angle() const; void setAngle(int angle); private: int m_angle; QPolygon m_pointer; }; ``` 在上面的代码中,我们使用Q_PROPERTY宏定义了angle属性,并提供了getter和setter函数。我们还定义了一个私有变量m_angle来存储当前的角度,以及一个QPolygon对象m_pointer来存储指针的形状。 在setAngle()函数中,我们设置m_angle属性,并根据新的角度计算指针的位置: ```C++ void RotatingDial::setAngle(int angle) { if (angle != m_angle) { m_angle = angle; m_pointer.setPoint(0, QPoint(0, -90)); m_pointer.setPoint(1, QPoint(5, 0)); m_pointer.setPoint(2, QPoint(-5, 0)); m_pointer.translate(0, 100); update(); } } ``` 在上面的代码中,我们首先判断新的角度是否与当前的角度相同。如果不同,我们就更新m_angle属性,并重新计算指针的位置。最后,我们调用update()函数来触发paintEvent()函数的调用,从而完成控件的重绘。 最后,我们可以在MainWindow类中使用我们的自定义控件,例如: ```C++ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { RotatingDial *dial = new RotatingDial(this); dial->setAngle(30); QHBoxLayout *layout = new QHBoxLayout; layout->addWidget(dial); setCentralWidget(new QWidget); centralWidget()->setLayout(layout); } ``` 在上面的代码中,我们创建了一个RotatingDial对象,并设置了初始角度为30度。然后,将其添加到QHBoxLayout布局中,并设置为主窗口的中央控件。 至此,我们已经完成了一个简单的旋转仪表盘的自定义控件。你可以根据需要对其进行扩展和优化,使其更加适合你的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值