本文详细的介绍了自绘时钟的过程以及详细的示例。重写paintEvent绘图事件实现了绘制时钟轮廓以及分针、时针、秒钟。使用了定时器实现了分针、时针、秒钟的不停变动。也可以自己设置分针、时针、秒钟的刻度位置。
接下来先看JPG示例图片,然后会详细的给大家介绍实现过程。
先看效果图:
本文作者原创,未经允许请勿转载,本帖原创请勿照抄。
QT 绘制时钟目录
一、 外圆详解
1、重写重绘函数:
paintEvent(QPaintEvent *) 槽函数,重绘动作全部在这里实现。
2、开启反锯齿:
因为如果不开启反锯齿的化自绘图形会有毛刺开启后绘制出来的图形会平滑。
3、前三层圆环:
调用绘制函数进行圆环的绘制。
4、第四层圆环:
second变量是全局秒针变量,第四层是蓝色跟着秒针动的蓝色圆环。
void paintEvent(QPaintEvent *);
void MainWindow::paintEvent(QPaintEvent *)
{
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);
/**
* 参数二:半径
* 参数三:开始的角度
* 参数四:指扫取的角度-顺时针(360度 / 8 = 45度)
* 参数五:圆环的高度
* 参数六:填充色
**/
//绘制第一层圆环
gradientArc(&painter, 185, 0, 360, 5, qRgb(201, 200, 200));
//绘制第二层圆环
gradientArc(&painter, 180, 0, 360, 20, qRgb(235, 235, 235));
//绘制第三层圆环
gradientArc(&painter, 160, 0, 360, 8, qRgb(180, 180, 180));
//绘制第四层圆环
gradientArc(&painter, 150, 90, second*6, 3, qRgb(98, 246, 255));
}
void MainWindow::gradientArc(QPainter *painter, int radius, int startAngle, int angleLength, int arcHeight, QRgb color)
{
// 渐变色
QRadialGradient gradient(0, 0, radius);
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1.0, color);
painter->setBrush(gradient);
// << 1(左移1位)相当于radius*2 即:150*2=300
//QRectF(-150, -150, 300, 300)
QRectF rect(-radius, -radius, radius << 1, radius << 1);
QPainterPath path;
path.arcTo(rect, startAngle, angleLength);
// QRectF(-120, -120, 240, 240)
QPainterPath subPath;
subPath.addEllipse(rect.adjusted(arcHeight, arcHeight, -arcHeight, -arcHeight));
// path为扇形 subPath为椭圆
path -= subPath;
painter->setPen(Qt::NoPen);
painter->drawPath(path);
}
二、绘制刻度线
1. 绘制刻度线
每隔四个格子会有一个比较大的刻度,所以需要两种刻度线,也就是说需要两个QPainter画笔,绘画两种不同的刻度线。
2. QPainter画笔
画出两个不同的刻度线,在计算各个刻度线的点,最后进行判断绘制大的刻度线还是小的刻度线。
//绘制表盘刻度线
DrawScaleLine(painter,radius-125);
void MainWindow::DrawScaleLine(QPainter& painter,int radius)
{
//初始化的第一个XY轴点坐标
int ponX=0,ponY=0;
//设置画刷
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();
}
}
三、绘制表盘上数字
1. 绘制表盘数字
大体和绘制刻度线类似,先设置字体Color、在设置字体Size。
2. 使用循环
在循环的过程中判断出坐标位置和0-12的具体数字。
//绘制表盘上数字
DrawDialNumber(painter,radius-160);
void MainWindow::DrawDialNumber(QPainter& painter,int radius)
{
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();
}
}
四、绘制时针
1. 使用QPainter
画笔绘制时针。
2. hour变量
通过hour变量来绘制出时针指向的坐标位置。
//绘制时针
DrawHourPointer(painter,radius-240);
void MainWindow::DrawHourPointer(QPainter& painter,int radius)
{
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();
}
五、绘制分针
1. QPainter画笔
使用QPainter画笔绘制分针。
2. minute变量
通过minute变量来绘制出分针指向的坐标位置。
//绘制分针
DrawMinutePointer(painter,radius-210);
void MainWindow::DrawMinutePointer(QPainter& painter,int radius)
{
//组装点的路径图
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();
}
六、绘制秒针
1. QPainter画笔
使用QPainter画笔绘制秒针。
2. second变量
通过second变量来绘制出分针指向的坐标位置。
//绘制秒针
DrawSecondPointer(painter,radius-190);
void MainWindow::DrawSecondPointer(QPainter& painter,int radius)
{
//组装点的路径图
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();
}
上面内容实现了时钟的实现,下面文章实现了时针、分针、秒针跟着系统时间的变化而变化。
实现了输入数字,时针、分针、秒针指向某个刻度。
提供了手动点击时针、分针、秒针,指向下一个刻度的思路。
七、时间事件
这里我有一篇文章专门介绍时间定时器:https://blog.csdn.net/qq_37529913/article/details/111468642
关键代码:
double hour;
double minute;
double second;
//开启一个定时器
id1=startTimer(1000);
qDebug()<<QString::number(id1);//打印定时器ID
QTime curr_time=QTime::currentTime();
second=curr_time.second();
minute=curr_time.minute()+second/60;
hour=curr_time.hour()+minute/60;
update();
八、自动、手动、输入值
1. 自动:
参考 七、时间事件 每一秒执行一次事件事件,每执行一次时间事件,时针、分针、秒针变量重新赋值之后update()更新,时针、分针、秒针位置发生变化。
2. 输入值:
上面实现了定时器自动更新位置,所以,需要停止定时器,在改变时针、分针、秒针变量重新赋值之后update()更新,就可以了。
3. 手动:
这个暂时还没更好的方法;思路:添加控件设置样式表或者重绘控件,形状改变成时针、分针、秒针的样子,设置透明。绑定点击消息,来指针改变位置。
switch(groupButton1->checkedId())
{
case 0:
ui->radioButton->setEnabled(false);
ui->radioButton_2->setEnabled(true);
ui->radioButton_3->setEnabled(true);
//开启一个定时器
startTimer(1000);
break;
case 1:
ui->radioButton_2->setEnabled(false);
ui->radioButton_3->setEnabled(true);
ui->radioButton->setEnabled(true);
//关闭定时器
killTimer(1);
break;
case 2:
ui->radioButton_3->setEnabled(false);
ui->radioButton_2->setEnabled(true);
ui->radioButton->setEnabled(true);
//关闭定时器
killTimer(1);
//lineEdit
QString mString = ui->lineEdit->text();
second=mString.toInt();
minute=mString.toInt();
hour=mString.toInt();
update();
break;
}
九、结语
有写到快一点钟了...每写一篇博客,从开始向做文章,到开始实现,再到实现完成,再写完整篇文章真是一个浩大的工程。
想到哪里写到哪里了,结尾,大家有空的话可以看一看。
1. 经历1
之前我看到一篇博客,i++ 和 i-- 的区别。
我不说大家会怎么解释,我觉得 i++就是算完i++的代码块然后i在+1,i-- 就是算完 i-- 所在的代码块然后 i-1 就完了,一篇博客顶多10行就结束了,最多肯定不超100行... 但是没想到这位大佬 进行了一大堆的论证,而且还画图举例详细刨析,当时的我感觉这才是真真的文章.................. 是什么码字水准能把这么一个知识点码成一片这么完整的文章,这位老哥也是下了功夫了。
连接就不给了给大家截个大图,有兴趣可以去看看。
2. 经历2
个人觉得,在工作中在完成每日工作的情况下应该不断总结和想办法自我提升,应该认清自己的能力,因为只有自己知道哪方面有不足的地方。在我没开始写CSDN博客的时候也是浑浑噩噩,但是随着第一篇、第二篇、第三篇、第四篇博客写出来的时候、也在博客上面看到那么多大佬那么多可能没有你幸运,但是还在默默的努力的人,还有那么多人在不断的奋斗,你也应该慢慢的放下王者荣耀、放下和平精英,多努努力,找找属于自己的方向。当然也不是劝解看到我博客的各位放弃游戏,而是能让自己玩游戏变得合理化,而不是下班打游戏睡觉。
当然在这里说这个也不是吐槽或者别的,只是把自己最近半年的有些想法,和有些事情和大家分享分享~
这篇文章结束了~也希望大家点点关注、赞、收藏。一直在努力,虽然文采不太好,没有那么多污段子,没有什么突出的地方,但是作者会一直努力哒~
在几个月后公众号什么的可能会搞一个,到时候可能吧这方面的经验分享出来~ 下篇文章再见吧,兄Die~
本文项目下载地址:https://download.csdn.net/download/qq_37529913/13750454