Qt浅谈之二:钟表(时分秒针)
一、简介
QT编写的模拟时钟,demo里的时钟只有时针和分针,在其基础上添加了秒针,构成了一个完整的时钟。能对2D绘图中坐标系统、平移变换(translate)、比例变换(scale)、旋转变换(rotate)、扭曲变换(shear)及其save()和restore()来保存和恢复坐标系的状态。
二、效果图
(1)时钟运行,秒针随系统时间移动。如图1。
三、详解
1、定时器
- Clock::Clock(QWidget *parent)
- : QWidget(parent)
- {
- QTimer *timer = new QTimer(this); //声明一个定时器
- //update()会自动产生重绘消息,调用paintEvent()
- connect(timer, SIGNAL(timeout()), this, SLOT(update())); //连接信号槽,定时器超时触发窗体更新
- timer->start(1000); //启动定时器
- setWindowTitle(tr("My Clock")); //设置窗体名称
- resize(300, 300); //设置窗体大小
- }
启动一个定时器,timer->start(1000);单位是ms,每一秒中update重绘一次窗口。
2、重绘事件
(1)先确定指针的颜色和形状大小。其坐标后面再确定。
- void Clock::paintEvent(QPaintEvent *event)
- {
- //下面三个数组用来定义表针的三个顶点,以便后面的填充
- static const QPoint hourHand[3] = {
- QPoint(3, 8),
- QPoint(-3, 8),
- QPoint(0, -40)
- };
- static const QPoint minuteHand[3] = {
- QPoint(3, 8),
- QPoint(-3, 8),
- QPoint(0, -70)
- };
- static const QPoint secondHand[3] = {
- QPoint(3, 8),
- QPoint(-3, 8),
- QPoint(0, -90)
- };//秒针
- //填充表针的颜色
- QColor hourColor(127, 0, 127); //分针颜色(第四个表示不透明度)
- QColor minuteColor(0, 127, 127, 191);
- QColor secondColor(127, 127, 0, 127);
- //...
- }
(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:
- {
- int side = qMin(width(), height()); //绘制的范围(宽、高中最小值)
- QTime time = QTime::currentTime(); //获取当前的时间
- QPainter painter(this); //声明用来绘图用的painter
- painter.setRenderHint(QPainter::Antialiasing);//绘制的图像反锯齿
- painter.translate(width() / 2, height() / 2);//重新定位坐标起始点,把坐标原点放到窗体的中央
- painter.scale(side / 300.0, side / 300.0);//设定画布的边界,用窗体宽高的最小值来计算时钟的大小,防止窗体拉伸导致的时钟变形以及显示不全
- }
再看秒针的坐标(-3, 8)、(3, 8)、(0, 90)即确定了秒针的具体大小和位置。变换后的圆心在屏幕的中心。
(3)在坐标(-40, 30)处画出时间,随系统一秒更新一次。
- {
- painter.setPen(Qt::red); //填充时针,不需要边线所以NoPen
- QString timeStr= QTime::currentTime().toString(); //绘制当前的时间
- painter.drawText(-40,30,80,30,Qt::AlignHCenter | Qt::AlignTop, timeStr);
- }
(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()。
- {
- //...
- painter.setPen(Qt::NoPen); //填充时针,不需要边线所以NoPen
- painter.setBrush(hourColor); //画刷颜色设定
- painter.save(); //保存painter的状态,保存的是当前的坐标状态,如果不保存,画完之后坐标以改变不方便画下一个
- painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0))); //将painter(的”视角“)根据时间参数转移(30° * (小时 + 分钟 / 60))
- painter.drawConvexPolygon(hourHand, 3); //填充时针的区域
- painter.restore();
- //后面的跟前面的类似,分别绘制了分针和秒针,及相应的刻度
- painter.setPen(Qt::NoPen);
- painter.setBrush(minuteColor);
- painter.save();
- painter.rotate(6.0 * (time.minute() + time.second() / 60.0)); //设旋转(角度 = 6° * (分钟 + 秒 / 60))
- painter.drawConvexPolygon(minuteHand, 3); //填充分针部分
- painter.restore();
- painter.setPen(Qt::NoPen);
- painter.setBrush(secondColor);
- painter.save();
- painter.rotate(6.0 * time.second()); //设置旋转(6° * 秒)
- painter.drawConvexPolygon(secondHand, 3); //设置填充
- painter.restore();
- //...
- }
(5)每次旋转6度,绘制长4个像素的直线,正点先不绘制,因为正点是8个像素的直线。接着绘制正点刻度和数字。不用保存画图的坐标,绘制都是旋转了360,回到了原来的最标系统中。
- { //...
- painter.setPen(minuteColor);
- for (int j = 0; j < 60; ++j) { //循环60次,绘制表盘(其实可以从1开始,到59,提高一点效率)
- if ((j % 5) != 0) //判断是否能被5整除(能被5整除表示是正点刻度,暂不绘制)
- painter.drawLine(0, -92, 0, -96); //不是正点刻度,绘制长4个像素的直线
- painter.rotate(6.0); //循环60次,每次旋转6度,所以不用save和restore
- }
- painter.setPen(hourColor); //下面画表示小时的刻度,此时要用到画笔(因为要划线)
- for (int i = 0; i < 12; ++i) {
- painter.drawLine(0, -88, 0, -96); //写上刻度数字
- if (i == 0) painter.drawText(-10,-88,20,20,Qt::AlignHCenter | Qt::AlignTop,QString::number(12));
- else painter.drawText(-10,-88,20,20,Qt::AlignHCenter | Qt::AlignTop,QString::number(i));
- painter.rotate(30.0);
- }
- }
(6)最后画上中心的小黑实心圆和外圈的空心圆,主要是计算下坐标。圆心分别为2和97。
- {
- //...
- painter.setPen(Qt::NoPen);
- painter.setBrush(secondColor);
- painter.save();
- painter.rotate(6.0 * time.second()); //设置旋转(6° * 秒)
- painter.drawConvexPolygon(secondHand, 3); //设置填充
- painter.restore();
- painter.setBrush(Qt::black);
- painter.drawEllipse(QPoint(0,0),2,2);
- painter.setBrush(Qt::NoBrush);
- painter.setPen(Qt::black);
- painter.drawEllipse(QPoint(0,0),97,97);
- //...
- }
四、总结
(1)时分秒也可以设置成四边形的,如
- static const QPoint hourHand[4] ={QPoint(0,10),QPoint(-1,-30),QPoint(0,-60),QPoint(1,-30)};
- static const QPoint minuteHand[4] ={QPoint(0,10),QPoint(-1,-40),QPoint(0,-70),QPoint(1,-40)};
- static const QPoint secondHand[4] ={QPoint(0,10),QPoint(-1,-60),QPoint(0,-90),QPoint(1,-60)};
- QColor hourColor(255,0,0);
- QColor minuteColor(0,127,127);
- QColor secondColor(0,0,255);
运行效果如下图4:
(2)解析顺序不是按代码正常顺序,请参看源码。
(3)源码已经打包上传到csdn上可登录下载(http://download.csdn.net/detail/taiyang1987912/7492657)。
(4)若需要沟通可以联系yang.ao@i-soft.com.cn
patch1
- void Clock::mouseMoveEvent(QMouseEvent *event)
- {
- if (m_pressMouse) { //移动窗口
- QPoint movePos = event->globalPos();
- move(movePos - m_movePoint);
- }
- }
- void Clock::contextMenuEvent(QContextMenuEvent *)
- {
- QCursor cur=this->cursor();
- QMenu *menu=new QMenu(this);
- QAction *deleteAction= new QAction(tr("关闭"), this);
- menu->addAction(deleteAction);
- connect(deleteAction, SIGNAL(triggered()), SLOT(close()));
- menu->exec(cur.pos());
- }
- void Clock::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- m_pressMouse = true;
- }
- m_movePoint = event->globalPos() - pos(); //窗口移动距离
- }
- void Clock::mouseReleaseEvent(QMouseEvent * event)
- {
- m_pressMouse = false;
- }
实现效果如下图5所示:(无外边框)
http://blog.csdn.net/taiyang1987912/article/details/30272105
Qt浅谈之二:钟表(时分秒针)的更多相关文章
- Qt浅谈之二十七进程间通信之QtDBus
一.简介 DBus的出现,使得Linux进程间通信更加便捷,不仅可以和用户空间应用程序进行通信,而且还可以和内核的程序进行通信,DBus使得Linux变得更加智能,更加具有交互性. DB ...
- Qt浅谈之二十App自动重启及关闭子窗口
一.简介 最近因项目需求,Qt程序一旦检测到错误,要重新启动,自己是每次关闭主窗口的所有子窗口但有些模态框会出现问题,因此从网上总结了一些知识点,以备以后的应用. 二.详解 1.Qt结构 int ma ...
- Qt浅谈之二十App自动重启及关闭子窗口(六种方法)
一.简介 最近因项目需求,Qt程序一旦检测到错误,要重新启动,自己是每次关闭主窗口的所有子窗口但有些模态框会出现问题,因此从网上总结了一些知识点,以备以后的应用. 二.详解 1.Qt结构 int ma ...
- Qt浅谈之二十六图片滑动效果
一.简介 博客中发现有作者写的仿360的代码,觉得其中图片滑动的效果很有意思,特提取其中的代码.并加上类似mac的画面移动的动画效果. 二.详解 1.代码一:界面滑动(QWidget) (1)slid ...
- Qt浅谈之二十一log调试日志
一.简单介绍 近期因调试code时,想了解程序的流程,但苦于没有一个简易的日志记录,不停使用qDebug打印输出,而终于提交代码时得去多次删除信息打印,有时还会出现新改动的代码分不清是哪些部分.而使用 ...
- Qt浅谈之二十七进程间通信之QtDBus good
一.简介 DBus的出现,使得Linux进程间通信更加便捷,不仅可以和用户空间应用程序进行通信,而且还可以和内核的程序进行通信,DBus使得Linux变得更加智能,更加具有交互性. DB ...
- Qt浅谈内存泄露(总结)
Qt浅谈内存泄露(总结) 来源 http://blog.csdn.net/taiyang1987912/article/details/29271549 一.简介 Qt内存管理机制:Qt 在内部能够维 ...
- Qt浅谈之总结(整理)
Qt浅谈之总结(整理) 来源 http://blog.csdn.net/taiyang1987912/article/details/32713781 一.简介 QT的一些知识点总结,方便以后查阅. ...
- Android开发-浅谈架构(二)
写在前面的话 我记得有一期罗胖的<罗辑思维>中他提到 我们在这个碎片化 充满焦虑的时代该怎么学习--用30%的时间 了解70%该领域的知识然后迅速转移芳草鲜美的地方 像游牧民族那样.原话应 ...
随机推荐
- mysql-实现行号
目前mysql不支持像oracle一样rownum,在网上也查找了好多,各种写法,自己进行了总结,实现方法如下 新建表: userid salay zhangsan 10000 lisi 12000 ...
- ThoughtWorks、Teambition、Trello、Slack、DevCloud 主流敏捷软件开发工具平台比较
在大公司做了6年程序员,2年项目经理的小王,正在创业公司迎来他焦虑的而立之年. 但是对于3个月前加入创业公司的决定,他现在有些烦躁和怀疑人生.在他过往的经验看来,公司新接的小项目,在过去的大公司里1个 ...
- 数学概念 —— 奇异性(Singularity,Vertical tangent)
0. 基本定义 Singularity (mathematics) 数学上的奇异性一般是指,函数在该点未定义(not defined,比如取值为无穷),或者不可微(fails to be well-b ...
- win10 uwp 使用 msbuild 命令行编译 UWP 程序
原文:win10 uwp 使用 msbuild 命令行编译 UWP 程序 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http:// ...
- Docker 入门基础
Docker是一个能把开发的应用程序自动部署到容器的开源引擎.Docker是新的容器化技术,轻巧,易移植[Build Once, Configure Once And Run Anywhere].Do ...
- 【转】ARRAYLIST VECTOR LINKEDLIST 区别与用法
转自:http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实 ...
- PHP设置时区,记录日志文件的方法
关键字:PHP 时区 时间 日志 <html> <body> <?php date_default_timezone_set('Asia/Hong_Kong'); //s ...
- 讨论2-sat建设和解决问题
2-sat问题是一种常见的问题.给定若干个01变量,变量之间满足一些二元约束,求是否有解存在.若存在,给出可行解或依照字典序给出最优解. 以下给出与其相应的图论模型:给每一个变量i设立2个点,我的习惯 ...
- 【iOS发展-49】的插件-插件该文档的凝视VVDocumenter安装与使用
文件凝视是/** */.快捷键///. 但是,这需要安装插件.VVDocumenter. 下载链接:https://github.com/onevcat/VVDocumenter-Xcode (1 ...
- WPF之动态换肤
原文:WPF之动态换肤 如何实现换肤呢,对于复杂的换肤操作,如,更换按钮样式.窗口样式等,我们需要写多个资源字典来表示不同的皮肤,通过动态加载不同的资源字典来实现换肤的效果:对于简单的换肤操作,如更改 ...