一、简介

QT编写的模拟时钟,demo里的时钟只有时针和分针,在其基础上添加了秒针,构成了一个完整的时钟。能对2D绘图中坐标系统、平移变换(translate)、比例变换(scale)、旋转变换(rotate)、扭曲变换(shear)及其save()和restore()来保存和恢复坐标系的状态。

二、效果图

(1)时钟运行,秒针随系统时间移动。如图1。

三、详解

1、定时器

  1. Clock::Clock(QWidget *parent)
  2. : QWidget(parent)
  3. {
  4. QTimer *timer = new QTimer(this);   //声明一个定时器
  5. //update()会自动产生重绘消息,调用paintEvent()
  6. connect(timer, SIGNAL(timeout()), this, SLOT(update()));  //连接信号槽,定时器超时触发窗体更新
  7. timer->start(1000);   //启动定时器
  8. setWindowTitle(tr("My Clock"));  //设置窗体名称
  9. resize(300, 300);  //设置窗体大小
  10. }

启动一个定时器,timer->start(1000);单位是ms,每一秒中update重绘一次窗口。

2、重绘事件

(1)先确定指针的颜色和形状大小。其坐标后面再确定。

  1. void Clock::paintEvent(QPaintEvent *event)
  2. {
  3. //下面三个数组用来定义表针的三个顶点,以便后面的填充
  4. static const QPoint hourHand[3] = {
  5. QPoint(3, 8),
  6. QPoint(-3, 8),
  7. QPoint(0, -40)
  8. };
  9. static const QPoint minuteHand[3] = {
  10. QPoint(3, 8),
  11. QPoint(-3, 8),
  12. QPoint(0, -70)
  13. };
  14. static const QPoint secondHand[3] = {
  15. QPoint(3, 8),
  16. QPoint(-3, 8),
  17. QPoint(0, -90)
  18. };//秒针
  19. //填充表针的颜色
  20. QColor hourColor(127, 0, 127);  //分针颜色(第四个表示不透明度)
  21. QColor minuteColor(0, 127, 127, 191);
  22. QColor secondColor(127, 127, 0, 127);
  23. //...
  24. }

(2)qMin(width(), height());获取长宽的最小值,以确保绘制的时钟是圆形的,并使用painter.scale(side / 300.0, side / 300.0);来执行比例变换,实现缩放效果,比如窗口变成600,则600/300.0放大2倍。

painter.translate(width() / 2, height() / 2);将最标原点从(0, 0)移动到窗口中心则原来的原点最标就变成(-150, -150)。

坐标变换后具体的坐标如下图2:

  1. {
  2. int side = qMin(width(), height());  //绘制的范围(宽、高中最小值)
  3. QTime time = QTime::currentTime();   //获取当前的时间
  4. QPainter painter(this);              //声明用来绘图用的painter
  5. painter.setRenderHint(QPainter::Antialiasing);//绘制的图像反锯齿
  6. painter.translate(width() / 2, height() / 2);//重新定位坐标起始点,把坐标原点放到窗体的中央
  7. painter.scale(side / 300.0, side / 300.0);//设定画布的边界,用窗体宽高的最小值来计算时钟的大小,防止窗体拉伸导致的时钟变形以及显示不全
  8. }


再看秒针的坐标(-3, 8)、(3, 8)、(0, 90)即确定了秒针的具体大小和位置。变换后的圆心在屏幕的中心。

(3)在坐标(-40, 30)处画出时间,随系统一秒更新一次。

  1. {
  2. painter.setPen(Qt::red);   //填充时针,不需要边线所以NoPen
  3. QString timeStr= QTime::currentTime().toString();     //绘制当前的时间
  4. painter.drawText(-40,30,80,30,Qt::AlignHCenter | Qt::AlignTop, timeStr);
  5. }


(4)根据当前的时间,计算时针的移动角度6.0 * (time.minute() + time.second() / 60.0,使用rotate进行最标旋转,比如最标旋转30度如下图3所示。

painter.drawConvexPolygon(minuteHand, 3); 画出时针的三角形,如图2所示。然后painter.restore();恢复坐标到图2,不恢复的话无法确定分针的角度。

在分别计算分针的角度6.0 * (time.minute() + time.second() / 60.0)和秒针的角度6.0 * time.second()。

  1. {
  2. //...
  3. painter.setPen(Qt::NoPen);   //填充时针,不需要边线所以NoPen
  4. painter.setBrush(hourColor);  //画刷颜色设定
  5. painter.save();  //保存painter的状态,保存的是当前的坐标状态,如果不保存,画完之后坐标以改变不方便画下一个
  6. painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0))); //将painter(的”视角“)根据时间参数转移(30° * (小时 + 分钟 / 60))
  7. painter.drawConvexPolygon(hourHand, 3);  //填充时针的区域
  8. painter.restore();
  9. //后面的跟前面的类似,分别绘制了分针和秒针,及相应的刻度
  10. painter.setPen(Qt::NoPen);
  11. painter.setBrush(minuteColor);
  12. painter.save();
  13. painter.rotate(6.0 * (time.minute() + time.second() / 60.0));  //设旋转(角度 = 6° * (分钟 + 秒 / 60))
  14. painter.drawConvexPolygon(minuteHand, 3);  //填充分针部分
  15. painter.restore();
  16. painter.setPen(Qt::NoPen);
  17. painter.setBrush(secondColor);
  18. painter.save();
  19. painter.rotate(6.0 * time.second());  //设置旋转(6° * 秒)
  20. painter.drawConvexPolygon(secondHand, 3);  //设置填充
  21. painter.restore();
  22. //...
  23. }

(5)每次旋转6度,绘制长4个像素的直线,正点先不绘制,因为正点是8个像素的直线。接着绘制正点刻度和数字。不用保存画图的坐标,绘制都是旋转了360,回到了原来的最标系统中。

  1. {   //...
  2. painter.setPen(minuteColor);
  3. for (int j = 0; j < 60; ++j) {  //循环60次,绘制表盘(其实可以从1开始,到59,提高一点效率)
  4. if ((j % 5) != 0)           //判断是否能被5整除(能被5整除表示是正点刻度,暂不绘制)
  5. painter.drawLine(0, -92, 0, -96);  //不是正点刻度,绘制长4个像素的直线
  6. painter.rotate(6.0);   //循环60次,每次旋转6度,所以不用save和restore
  7. }
  8. painter.setPen(hourColor);    //下面画表示小时的刻度,此时要用到画笔(因为要划线)
  9. for (int i = 0; i < 12; ++i) {
  10. painter.drawLine(0, -88, 0, -96);     //写上刻度数字
  11. if (i == 0)  painter.drawText(-10,-88,20,20,Qt::AlignHCenter | Qt::AlignTop,QString::number(12));
  12. else  painter.drawText(-10,-88,20,20,Qt::AlignHCenter | Qt::AlignTop,QString::number(i));
  13. painter.rotate(30.0);
  14. }
  15. }

(6)最后画上中心的小黑实心圆和外圈的空心圆,主要是计算下坐标。圆心分别为2和97。

  1. {
  2. //...
  3. painter.setPen(Qt::NoPen);
  4. painter.setBrush(secondColor);
  5. painter.save();
  6. painter.rotate(6.0 * time.second());  //设置旋转(6° * 秒)
  7. painter.drawConvexPolygon(secondHand, 3);  //设置填充
  8. painter.restore();
  9. painter.setBrush(Qt::black);
  10. painter.drawEllipse(QPoint(0,0),2,2);
  11. painter.setBrush(Qt::NoBrush);
  12. painter.setPen(Qt::black);
  13. painter.drawEllipse(QPoint(0,0),97,97);
  14. //...
  15. }

四、总结

(1)时分秒也可以设置成四边形的,如

  1. static const QPoint hourHand[4]   ={QPoint(0,10),QPoint(-1,-30),QPoint(0,-60),QPoint(1,-30)};
  2. static const QPoint minuteHand[4] ={QPoint(0,10),QPoint(-1,-40),QPoint(0,-70),QPoint(1,-40)};
  3. static const QPoint secondHand[4] ={QPoint(0,10),QPoint(-1,-60),QPoint(0,-90),QPoint(1,-60)};
  4. QColor hourColor(255,0,0);
  5. QColor minuteColor(0,127,127);
  6. QColor secondColor(0,0,255);

运行效果如下图4:

(2)解析顺序不是按代码正常顺序,请参看源码。

(3)源码已经打包上传到csdn上可登录下载(http://download.csdn.net/detail/taiyang1987912/7492657)。

(4)若需要沟通可以联系yang.ao@i-soft.com.cn

patch1

让背景框架透明,在加上移动盘面和右键退出就构成了一个比较理想的时钟(思路来自同学的程序)。
  1. void Clock::mouseMoveEvent(QMouseEvent *event)
  2. {
  3. if (m_pressMouse) {   //移动窗口
  4. QPoint movePos = event->globalPos();
  5. move(movePos - m_movePoint);
  6. }
  7. }
  8. void Clock::contextMenuEvent(QContextMenuEvent *)
  9. {
  10. QCursor cur=this->cursor();
  11. QMenu *menu=new QMenu(this);
  12. QAction *deleteAction= new QAction(tr("关闭"), this);
  13. menu->addAction(deleteAction);
  14. connect(deleteAction, SIGNAL(triggered()), SLOT(close()));
  15. menu->exec(cur.pos());
  16. }
  17. void Clock::mousePressEvent(QMouseEvent *event)
  18. {
  19. if (event->button() == Qt::LeftButton) {
  20. m_pressMouse = true;
  21. }
  22. m_movePoint = event->globalPos() - pos();   //窗口移动距离
  23. }
  24. void Clock::mouseReleaseEvent(QMouseEvent * event)
  25. {
  26. m_pressMouse = false;
  27. }

实现效果如下图5所示:(无外边框)

 

http://blog.csdn.net/taiyang1987912/article/details/30272105

Qt浅谈之二:钟表(时分秒针)的更多相关文章

  1. Qt浅谈之二十七进程间通信之QtDBus

    一.简介 DBus的出现,使得Linux进程间通信更加便捷,不仅可以和用户空间应用程序进行通信,而且还可以和内核的程序进行通信,DBus使得Linux变得更加智能,更加具有交互性.        DB ...

  2. Qt浅谈之二十App自动重启及关闭子窗口

    一.简介 最近因项目需求,Qt程序一旦检测到错误,要重新启动,自己是每次关闭主窗口的所有子窗口但有些模态框会出现问题,因此从网上总结了一些知识点,以备以后的应用. 二.详解 1.Qt结构 int ma ...

  3. Qt浅谈之二十App自动重启及关闭子窗口(六种方法)

    一.简介 最近因项目需求,Qt程序一旦检测到错误,要重新启动,自己是每次关闭主窗口的所有子窗口但有些模态框会出现问题,因此从网上总结了一些知识点,以备以后的应用. 二.详解 1.Qt结构 int ma ...

  4. Qt浅谈之二十六图片滑动效果

    一.简介 博客中发现有作者写的仿360的代码,觉得其中图片滑动的效果很有意思,特提取其中的代码.并加上类似mac的画面移动的动画效果. 二.详解 1.代码一:界面滑动(QWidget) (1)slid ...

  5. Qt浅谈之二十一log调试日志

    一.简单介绍 近期因调试code时,想了解程序的流程,但苦于没有一个简易的日志记录,不停使用qDebug打印输出,而终于提交代码时得去多次删除信息打印,有时还会出现新改动的代码分不清是哪些部分.而使用 ...

  6. Qt浅谈之二十七进程间通信之QtDBus good

    一.简介 DBus的出现,使得Linux进程间通信更加便捷,不仅可以和用户空间应用程序进行通信,而且还可以和内核的程序进行通信,DBus使得Linux变得更加智能,更加具有交互性.        DB ...

  7. Qt浅谈内存泄露(总结)

    Qt浅谈内存泄露(总结) 来源 http://blog.csdn.net/taiyang1987912/article/details/29271549 一.简介 Qt内存管理机制:Qt 在内部能够维 ...

  8. Qt浅谈之总结(整理)

    Qt浅谈之总结(整理) 来源 http://blog.csdn.net/taiyang1987912/article/details/32713781 一.简介 QT的一些知识点总结,方便以后查阅. ...

  9. Android开发-浅谈架构(二)

    写在前面的话 我记得有一期罗胖的<罗辑思维>中他提到 我们在这个碎片化 充满焦虑的时代该怎么学习--用30%的时间 了解70%该领域的知识然后迅速转移芳草鲜美的地方 像游牧民族那样.原话应 ...

随机推荐

  1. AndroidStudio一步步教你修改项目包名(最详细,最易懂)

    如果你看了很多篇博文还是修改不了包名,我相信这篇可以帮你解决修改包名的问题 修改项目包名,实现不覆盖安装(如果只是想不覆盖安装,更改build.gradle里面的包名就OK了,那这篇博文到这里就可以结 ...

  2. 动态备份SQL-SERVER数据库——SQLDMO

    转载:http://www.cnblogs.com/liulanglang/archive/2007/12/04/981812.html 上周要写一个SQL-SERVER数据库备份还原的程序,很没有思 ...

  3. Servlet接口的实现类,路径配置映射,ServletConfig对象,ServletContext对象及web工程中文件的读取

    一,Servlet接口实现类:sun公司为Servlet接口定义了两个默认的实现类,分别为:GenericServlet和HttpServlet. HttpServlet:指能够处理HTTP请求的se ...

  4. LVS实现负载均衡原理及安装配置

    LVS实现负载均衡原理及安装配置 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均衡设备F ...

  5. 《深入浅出WPF》笔记——资源篇

    原文:<深入浅出WPF>笔记--资源篇 前面的记录有的地方已经用到了资源,本文就来详细的记录一下WPF中的资源.我们平时的“资源”一词是指“资财之源”,是创造人类社会财富的源泉.在计算机程 ...

  6. jQuery.noop

    一个空函数 当你仅仅想要传递一个空函数的时候,就用他吧.这对一些插件作者很有用,当插件提供了一个可选的回调函数接口,那么如果调用的时候没有传递这个回调函数,就用jQuery.noop来代替执行.

  7. 组态档(configuration file)与建构档

    组态档,或者叫 configuration file,配置文件.组态档是用一种建构软件专用的特殊编程语言写的 CMake 脚本. 使用组态档能改变程序的设置,而不用重新编译程序. CMake 的组态档 ...

  8. Delphi 获取外部程序句柄与发送消息

    --记录下来备以后用 [打开外部程序.消息.句柄],技术有限,希望不要误人子弟了. 源码unit Unit1; interface uses Windows, Messages, SysUtils, ...

  9. 学习Hadoop和Spark的好的资源

    1. 官网http://spark.apache.org 有各种资源链接: 2. 总结得很好的个人博客[从零开始学Hadoop系列]1)初识http://blog.csdn.net/u01016816 ...

  10. 【狼窝乀野狼】Parallel浅尝辄止

    前段时间看到园子里面有同学在用Parallel进行批量插入数据库.后面也有很多同学针对这一事件给出了自己的看法和见解.我在这里不评论内容的好坏,至少能将自己东西总结分享这个是要靠勇气和毅力. 闲话少说 ...