欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt自绘时钟表盘-1

程序员文章站 2022-03-10 22:16:02
...

一、效果图

Qt自绘时钟表盘-1
二、效果图拆解
效果图上的表盘外圈分为三个同心圆环,每个圆环的颜色不一样,造就出一种表盘突出的错觉。内部刻度线分为长刻度线和短刻度线,两种刻度线的绘制方法一样,都是两个等腰梯形合并成的复杂图形。再往圆心是表盘数字,最中间的刻度线分为时分秒针,这三个指针都带有透明度,最短的是时针,稍微长点的是分针,最长的红色指针是分针,指针的绘制方法和小刻度线的绘制方法类似,只是尾部不是梯形,而是半圆。
三、绘制过程拆解
①首先绘制表盘的外圈(看上去有点像是凸出的部分),这里实际上是由三个同心圆环组成,由于色差欺骗眼睛。三个同心圆环的绘制过程相同,都是采用QPainterPath对象计算大圆和小圆之间的差集,然后绘制这个差集。
②内部的小刻度线看上去是一头大一头小,而且看上去还比较圆滑,其实也是被眼睛欺骗了,这个小刻度线放大之后的效果如下:
Qt自绘时钟表盘-1
实际上就是由于两个等腰梯形合并在一起的,由于效果图上的点线距离太小,所以看上去就是圆滑的。这里长短刻度线绘制方法一样,长刻度线实际就是梯形的高度更长点而已。这里的刻度线都指向圆心,在绘图的时候没有采用旋转图形,而是采用旋转坐标系实现。
③中间的数字是采用QPainter的成员函数drawText实现,后面代码详解。
④绘制表盘指针,这里指针的绘制方式和刻度线的绘制方法相同,都是采用QPainterPath组装闭合绘图路径实现的,不一样的地方是,刻度线是由两个等腰梯形组成,而指针是由一个等腰梯形和一个半圆组成的。
⑤最后是添加定时器,定时获取系统当前时间,计算当前时间的时分秒对应只针的角度,刷新界面,将指针旋转对应的角度后绘制出来。
四、绘制前准备
这里时间表盘被分为12个区间,因为一个圆对应的角度是360°,所以每个区间对应的度数是30°,每两个小刻度之间的度数是6°(这个度数在后面绘制图形是用来旋转坐标系的)。
还有一个问题就是Qt的坐标系和常规的数学坐标系不一样,它是向右为x轴,向下为y轴,所以向右为正,向下为正。
五、详细绘制过程
①准备工作,先设置对话框窗口大小,设置窗口背景颜色等,代码如下所示:

//初始化变量
hour=0;
minute=0;
second=0;
//设置窗口大小
setFixedSize(600,600);
//去掉问号
Qt::WindowFlags flags= this->windowFlags();
setWindowFlags(flags&~Qt::WindowContextHelpButtonHint);
//背景设置成白色
QPalette bgpal=palette();
bgpal.setColor(QPalette::Background,QColor(255,255,255));
setPalette(bgpal);

这里已经定义成员变量并初始化时分秒,都是double类型,为什么是double类型,下文有答案。
设置完成之后的窗口运行出来一片空白,这里就补贴效果图了。
②重载窗口的绘图函数,初始化QPainter对象,将绘图中心移动到窗口中心,设置画笔画刷等属性,代码如下所示:

//初始化画图对象
QPainter painter(this);
//绘图坐标移动到中心
int width=this->width();
int height=this->height();
painter.translate(width>>1,height>>1);
int radius=((width>height)?height:width)/2-30;
//启用反锯齿
painter.setRenderHint(QPainter::Antialiasing, true);
//取消画笔
painter.setPen(Qt::NoPen);

这一步里面初始化了一个变量radius,表盘的半径。
③绘制表盘的外圆环,这里采用的是QPainterPath计算差集,原理如下,一个大圆的路径-一个小圆的路径=一个同心圆环,绘制这个圆环即可。代码如下所示:
调用函数:

DrawCircularRing1(painter,radius,radius-4);

实现函数:

//保存绘图对象
painter.save();
//计算大小圆路径
QPainterPath outRing;
QPainterPath inRing;
outRing.moveTo(0,0);
inRing.moveTo(0,0);
outRing.addEllipse(-radius1,-radius1, 2*radius1,2*radius1);
inRing.addEllipse(-radius2,-radius2,2*radius2,2*radius2);
outRing.closeSubpath();
//设置画刷
painter.setBrush(QColor(200,200,200));
//大圆减去小圆得到圆环
painter.drawPath(outRing.subtracted(inRing));
//恢复绘图对象
painter.restore();

效果图如下所示:
Qt自绘时钟表盘-1
③其他两个还原的实现方法相同,只是半径和颜色不同,代码如下:
调用函数:

DrawCircularRing2(painter,radius-4,radius-20);

实现函数如下:

//保存绘图对象
painter.save();
//计算大小圆路径
QPainterPath outRing;
QPainterPath inRing;
outRing.moveTo(0,0);
inRing.moveTo(0,0);
outRing.addEllipse(-radius1,-radius1, 2*radius1,2*radius1);
inRing.addEllipse(-radius2,-radius2,2*radius2,2*radius2);
outRing.closeSubpath();
//设置画刷
painter.setBrush(QColor(235,235,235));
//大圆减去小圆得到圆环
painter.drawPath(outRing.subtracted(inRing));
//恢复绘图对象
painter.restore();

调用函数:

DrawCircularRing3(painter,radius-20,radius-25);

实现函数如下:

//保存绘图对象
painter.save();
//计算大小圆路径
QPainterPath outRing;
QPainterPath inRing;
outRing.moveTo(0,0);
inRing.moveTo(0,0);
outRing.addEllipse(-radius1,-radius1, 2*radius1,2*radius1);
inRing.addEllipse(-radius2,-radius2,2*radius2,2*radius2);
outRing.closeSubpath();
//设置画刷
painter.setBrush(QColor(180,180,180));
//大圆减去小圆得到圆环
painter.drawPath(outRing.subtracted(inRing));
//恢复绘图对象
painter.restore();

效果图如下所示:
Qt自绘时钟表盘-1
④绘制刻度线,这里直接给出代码,具体的绘制思路很简单,就是先组装好要绘制的路径(两个等腰梯形合并成一个图形),然后将绘图坐标系移动到对应点上,然后旋转对应的角度,最后绘制这个图形记录,如果需要了解具体绘制过程,可以参考我的另一篇文章:Qt自绘汽车仪表盘里面的子弹头绘制过程,写的非常详细,这里不再赘述。代码如下所示:
调用函数:

DrawScaleLine(painter,radius-35);

实现函数:

//设置画刷
painter.setBrush(QColor(100,100,100));
//组装点的路径图
QPainterPath pointPath1;
pointPath1.moveTo(-2,-2);
pointPath1.lineTo(-1,-4);
pointPath1.lineTo(1,-4);
pointPath1.lineTo(2,-2);
pointPath1.lineTo(1,8);
pointPath1.lineTo(-1,8);
QPainterPath pointPath2;
pointPath2.moveTo(-2,-2);
pointPath2.lineTo(-1,-4);
pointPath2.lineTo(1,-4);
pointPath2.lineTo(2,-2);
pointPath2.lineTo(1,20);
pointPath2.lineTo(-1,20);
//绘制25个刻度
for(int i=0;i<60;++i){
    QPointF point(0,0);
    painter.save();
    //计算并移动绘图对象中心点
    point.setX(radius*qCos(((90-i*6)*M_PI)/180));
    point.setY(radius*qSin(((90-i*6)*M_PI)/180));
    //计算并移动绘图对象的中心点
    painter.translate(point.x(),-point.y());
    //计算并选择绘图对象坐标
    painter.rotate(i*6);
    //绘制路径
    if(i%5){
        painter.drawPath(pointPath1);
    }
    else{
        painter.drawPath(pointPath2);
    }
    painter.restore();
}

效果图如下所示:
Qt自绘时钟表盘-1
⑤绘制表盘数字,这个就很简单了,因为数字不需要旋转,具体绘制代码如下所示:
调用代码:

DrawDialNumber(painter,radius-70);

实现代码:

painter.setPen(QColor(100,100,100));
QFont font;
font.setFamily("SimHei");
font.setPointSize(16);
painter.setFont(font);
//绘制13个小点
for(int i=0;i<12;++i){
    QPointF point(0,0);
    painter.save();
    //计算并移动绘图对象中心点
    point.setX(radius*qCos(((60-i*30)*M_PI)/180));
    point.setY(radius*qSin(((60-i*30)*M_PI)/180));
    //计算并移动绘图对象的中心点
    painter.translate(point.x(),-point.y());
    //绘制路径
    painter.drawText(-15, -15, 30, 30,Qt::AlignCenter,QString::number(i+1));
    painter.restore();
}

效果图如下所示:
Qt自绘时钟表盘-1
⑥绘制时针、分针、秒针。具体代码如下所示:
调用函数:

DrawHourPointer(painter,radius-150);

实现函数:

painter.setPen(Qt::NoPen);
//组装点的路径图
QPainterPath pointPath;
pointPath.moveTo(10,0);
pointPath.lineTo(1,-radius);
pointPath.lineTo(-1,-radius);
pointPath.lineTo(-10,0);
pointPath.arcTo(-10,0,20,20,180,180);
painter.save();
//计算并选择绘图对象坐标
painter.rotate(hour*30);
//设置画刷
painter.setBrush(QColor(0,0,0,200));
//绘制路径
painter.drawPath(pointPath);
painter.restore();

调用函数:

DrawMinutePointer(painter,radius-120);

实现函数:

//组装点的路径图
QPainterPath pointPath;
pointPath.moveTo(10,0);
pointPath.lineTo(1,-radius);
pointPath.lineTo(-1,-radius);
pointPath.lineTo(-10,0);
pointPath.arcTo(-10,0,20,20,180,180);
painter.save();
//计算并选择绘图对象坐标
painter.rotate(minute*6);
//设置画刷
painter.setBrush(QColor(0,0,0,200));
//绘制路径
painter.drawPath(pointPath);
painter.restore();

调用函数:

DrawSecondPointer(painter,radius-100);

实现函数:

//组装点的路径图
QPainterPath pointPath;
pointPath.moveTo(10,0);
pointPath.lineTo(1,-radius);
pointPath.lineTo(-1,-radius);
pointPath.lineTo(-10,0);
pointPath.arcTo(-10,0,20,20,180,180);
QPainterPath inRing;
inRing.addEllipse(-5,-5,10,10);
painter.save();
//计算并选择绘图对象坐标
painter.rotate(second*6);
//设置画刷
painter.setBrush(QColor(255,0,0,200));
//绘制路径
painter.drawPath(pointPath.subtracted(inRing));
painter.restore();

效果图如下:
Qt自绘时钟表盘-1
此时三个指针重叠在一起的。为了让它出来就分开,所以需要在构造函数中,获取一下当前时间,并执行一次绘制,代码如下:

//执行一次绘图
QTime curr_time =QTime::currentTime();
second=curr_time.second();
minute=curr_time.minute()+second/60;
hour=curr_time.hour()+minute/60;

效果图如下:
Qt自绘时钟表盘-1
⑦最后一步,为了让时间动起来,所以在界面上添加定时器,代码如下所示:

//设置定时器
startTimer(1000);

实现代码:

void Clock1::timerEvent(QTimerEvent*)
{
    QTime curr_time=QTime::currentTime();
    second=curr_time.second();
    minute=curr_time.minute()+second/60;
    hour=curr_time.hour()+minute/60;
    update();
}

到此,效果就和效果图中一样了,完美收工。
六、最后总结
①上面描述的是绘制的整个过程,可以根据自身需要设置不同的颜色值,在表盘上以数字方式打印当前时间等,实现不同的效果。
②需要源代码的可以加群联系作者,群二维码如下:
Qt自绘时钟表盘-1

相关标签: Qt自绘控件