No.01 桌面时钟

一、Qt版本:

Qt Creator 4.4.1 Based on Qt 5.9.3 (MSVC 2015, 32 bit)


二、效果图:


No.01 桌面时钟_第1张图片  No.01 桌面时钟_第2张图片


三、功能简介:

1)鼠标滚轮放大缩小

2)时钟走动音效

3)右键菜单

4)托盘菜单


四、上代码:


1) 头文件 clock.h

#ifndef CLOCK_H
#define CLOCK_H

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class Clock : public QWidget
{
    Q_OBJECT

public:
    Clock(QWidget *parent = 0);
    ~Clock();

private slots:
    void slot_showMaxiNormal();
    void slot_EffectControl();

private:
    static const QPoint hourHand[4];
    static const QPoint minuteHand[4];
    static const QPoint secondHand[4];

    QPen mHourHandPen;
    QPen mMinuteHandPen;
    QPen mSecondHandPen;

    QPoint mPos;

    // 右键菜单
    QMenu *m_pRBMenu;
    QAction *m_pCloseAct;
    QAction *m_pMaxiMinimizeAct;

    // 音效控制菜单
    QMenu *m_pSDMenu;
    QAction *m_pSoundOffAct;
    QAction *m_pSoundOnAct;

    QSoundEffect *m_pEffect;

    QStringList m_fontList;

    QSystemTrayIcon *systemTray;

protected:
    void paintEvent(QPaintEvent *event);
    void drawHourHand(QPainter *painter);
    void drawMinuteHand(QPainter *painter);
    void drawSecondHand(QPainter *painter);
    void drawLcdNumber(QPainter *painter);
    void drawClockDial(QPainter *painter);

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void wheelEvent(QWheelEvent *event);

    void createMenu();
    void createTray();
    void contextMenuEvent(QContextMenuEvent *event);

};

#endif // CLOCK_H

2) 核心代码 clock.cpp

#include "clock.h"

#include 
#include 
#include 
#include 
#include 
#include 

const QPoint Clock::hourHand[4] = {
    QPoint(3, 5),
    QPoint(0, 13),
    QPoint(-3, 5),
    QPoint(0, -40)
}; // 时针绘图区域
const QPoint Clock::minuteHand[4] = {
    QPoint(3, 5),
    QPoint(0, 16),
    QPoint(-3, 5),
    QPoint(0, -68)
}; // 分针绘图区域
const QPoint Clock::secondHand[4] = {
    QPoint(3, 5),
    QPoint(0, 18),
    QPoint(-3, 5),
    QPoint(0, -85)
}; // 秒针绘图区域

Clock::Clock(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint); // 去掉标题栏,去掉任务栏显示,窗口置顶
    this->setWindowIcon( QIcon(":/ico/clock.ico") );
    this->setWindowTitle( tr("桌面时钟") );
    this->resize(200, 200);

    this->setAttribute(Qt::WA_TranslucentBackground, true); // 窗口透明,去掉标题栏后方生效
//    this->setWindowOpacity(0.7); // 窗口透明度设置,其控件或绘图也透明,且达不到圆形窗口的效果

    /* 加载外部字体文件 */
    m_fontList.clear();
    int lcdFontId = QFontDatabase::addApplicationFont(":/lcd/DS-DIGI.ttf"); // 从source资源文件
    if (lcdFontId != -1)
    {
        m_fontList << QFontDatabase::applicationFontFamilies(lcdFontId);
    }

    /* 画笔设置 */
    mHourHandPen = QPen(palette().foreground(), 2.0);
    mMinuteHandPen = QPen(palette().foreground(), 1.0);

    QTimer *pTimer = new QTimer(this);
    pTimer->start(1000);
    connect( pTimer, SIGNAL(timeout()), this, SLOT(update()) );

    /* 音效控制 */
//    QString dir = QCoreApplication::applicationDirPath();
//    QString filename(dir + "/sounds/clockMoveSound.wav");
    m_pEffect = new QSoundEffect(this);
    m_pEffect->setLoopCount(QSoundEffect::Infinite); // 循环播放
//    m_pEffect->setSource( QUrl::fromLocalFile(filename) );
    m_pEffect->setSource( QUrl::fromLocalFile(":/sound/clockMoveSound.wav") );
    m_pEffect->setVolume(1.0); // 音量控制:0.0-1.0
//    m_pEffect->play(); // 播放
//    m_pEffect->stop(); // 停止

    /* 创建右键菜单 */
    createMenu();

    /* 创建系统托盘项 */
    createTray();
}

Clock::~Clock()
{
    delete m_pRBMenu;
    delete m_pCloseAct;
    delete m_pMaxiMinimizeAct;
    delete m_pSoundOnAct;
    delete m_pSoundOffAct;
    delete m_pEffect;
}

/* 重写绘图事件 */
void Clock::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    QFont font("Microsoft Yahei", 10, 75); // 字体,大小,加粗等同于QFont::Bold
    painter.setFont(font);
    painter.setRenderHint(QPainter::Antialiasing, true); //反锯齿
//    painter.setWindow(0, 0, 200, 200);

    int side = qMin(this->width(), this->height());

    /* 圆形背景的绘制 */
    painter.setPen(Qt::NoPen); // 去掉外圈线
    painter.setBrush(QColor(255, 255, 255, 125));              // 背景颜色以及透明度
    painter.drawEllipse( QPoint(width()/2, height()/2), side/2, side/2 ); // 绘制背景

    painter.setPen(QPen( QColor(233, 233, 216 ), 4 )); // 外边框颜色以及大小
    painter.drawEllipse(QPoint(width()/2, height()/2), side/2 - 3, side/2 - 3); //外边框绘制

    painter.translate(width() / 2, height() / 2); // 设置坐标原点
    painter.scale(side / 200.0, side / 200.0); // 缩放比例

    /* 时针、分针、秒针、表盘、Lcd */
    drawHourHand(&painter);
    drawMinuteHand(&painter);
    drawSecondHand(&painter);
    drawClockDial(&painter);
    drawLcdNumber(&painter);

    /* 中心点 */
    painter.setBrush(Qt::black);
    painter.drawEllipse(QPoint(0, 0), 2, 2);
}

void Clock::drawHourHand(QPainter *painter)
{
    QTime time = QTime::currentTime();
    painter->setBrush(Qt::black);
    painter->setPen(Qt::black);
    painter->save();
    painter->rotate( 30.0 * (time.hour() + time.minute()/60.0) ); //坐标轴旋转
    painter->drawConvexPolygon(hourHand, 4); // 绘制凸多边形,由n个点控制,此处由4个点控制
    painter->restore(); //复位坐标
}

void Clock::drawMinuteHand(QPainter *painter)
{
    QTime time = QTime::currentTime();
    painter->setBrush(Qt::blue);
    painter->setPen(Qt::blue);
    painter->save();
    painter->rotate( 6.0 * (time.minute() + time.second()/60.0) );
    painter->drawConvexPolygon(minuteHand, 4);
    painter->restore(); //复位坐标
}

void Clock::drawSecondHand(QPainter *painter)
{
    QTime time = QTime::currentTime();
    painter->setBrush(Qt::red);
    painter->setPen(Qt::red);
    painter->save();
    painter->rotate( 6.0 * time.second() );
    painter->drawConvexPolygon(secondHand, 4);
    painter->restore();
}

/* Lcd */
void Clock::drawLcdNumber(QPainter *painter)
{
    if (!m_fontList.isEmpty())
    {
        QFont font;
        font.setFamily(m_fontList.at(0));
        font.setPointSize(15);
        // font.setStretch(side / 200.0);
        painter->setFont(font);
    }
    painter->setPen( QColor(0, 0, 0) );
    painter->drawText( -40, 34, 80, 22, Qt::AlignCenter,
                     QTime::currentTime().toString("hh:mm:ss") );

//    painter->setBrush(Qt::NoBrush);
//    painter->drawRect(-40, 34, 80, 22);
//     坐标系统:向右为X轴正方向,向下为Y轴正方向
}

// 表盘和数字
void Clock::drawClockDial(QPainter *painter)
{
    for (int i = 1; i <= 60; i++)
    {
        painter->save();
        painter->rotate(6*i); // 坐标轴旋转6*i度

        if ( (i % 5 == 0) && (i <= 15 || i >= 45) ) // 小时刻度和数字
        {
            painter->setPen(mHourHandPen);
            painter->drawLine(0, -95, 0, -80);
            painter->drawText(-20, -82, 40, 40,
                              Qt::AlignHCenter | Qt::AlignTop,
                              QString::number(i/5) );
            if (i < 15 || i > 45) // 解决下半部分数字倒转问题
            {
                painter->drawLine(0, 80, 0, 95);
                painter->drawText( -20, 41, 40, 40,
                                   Qt::AlignHCenter | Qt::AlignBottom,
                                   QString::number(i<15 ? i/5+6 : i/5-6) );
            }
        }//if
        else // 分钟刻度
        {
            painter->setPen(mMinuteHandPen);
            painter->drawLine(0, 95, 0, 90);
        }

        painter->restore();
    }//for
}

/* 实现窗口拖动 */
void Clock::mousePressEvent(QMouseEvent *event)
{
    mPos = (event->globalPos()) - (this->pos()); //按下点 - 未按下时的点
}
void Clock::mouseMoveEvent(QMouseEvent *event)
{
    if (this->isFullScreen() == false)
    {
        this->move(event->globalPos() - mPos );
    }
}

/* 重写滚轮事件,实现放大缩小 */
void Clock::wheelEvent(QWheelEvent *event)
{
    QRect tmp = this->geometry();
    QPoint centerPoint = tmp.center(); // 储存中心点坐标

    static int adjustSize = 20;

    if (event->delta() > 0) // 放大
    {
        tmp.setWidth(tmp.width() + adjustSize);
        tmp.setHeight(tmp.height() + adjustSize);
    }
    else // 缩小
    {
        tmp.setWidth(tmp.width() - adjustSize);
        tmp.setHeight(tmp.height() - adjustSize);
    }

    if (tmp.width() > 20) // 限制最小尺寸
    {
        tmp.moveCenter(centerPoint); // 从中心缩放而非左上角处
        this->setGeometry(tmp);

        // 设置toolTip
        double percent = (double)tmp.width() / 200.0;
        QString percentStr = QString::number(percent*100) + "%";
        QToolTip::showText(QCursor::pos(), percentStr, this, QRect(), 500);
    }
}

/* 创建右键菜单 */
void Clock::createMenu()
{
    m_pRBMenu = new QMenu(this);

    m_pCloseAct = new QAction(this);
    m_pCloseAct->setIcon( QIcon(":/ico/closeBt.ico") );
    m_pCloseAct->setText( tr("关闭") );

    m_pMaxiMinimizeAct = new QAction(this);
    m_pMaxiMinimizeAct->setText( tr("全屏") );
    m_pMaxiMinimizeAct->setIcon( QIcon(":/ico/fullscreen.ico") );

    m_pRBMenu->addAction(m_pCloseAct);
    m_pRBMenu->addSeparator();
    m_pRBMenu->addAction(m_pMaxiMinimizeAct);

    connect (m_pCloseAct, SIGNAL(triggered(bool)), qApp, SLOT(quit()) );

    connect( m_pMaxiMinimizeAct, SIGNAL(triggered(bool)),
             this, SLOT( slot_showMaxiNormal() ) );

    /* 二级菜单 */
    m_pRBMenu->addSeparator();
    m_pSDMenu = m_pRBMenu->addMenu( QIcon(":/ico/sound.ico"), tr("音效控制") );

    m_pSoundOnAct = new QAction(this);
    m_pSoundOnAct->setText( tr("音效开") );
    m_pSoundOnAct->setIcon( QIcon(":/ico/nocheck.ico") );
    m_pSoundOffAct = new QAction(this);
    m_pSoundOffAct->setText( tr("音效关") );
    m_pSoundOffAct->setIcon( QIcon(":/ico/check.ico") );

    m_pSDMenu->addAction(m_pSoundOnAct);
    m_pSDMenu->addAction(m_pSoundOffAct);

    connect (m_pSoundOnAct, SIGNAL(triggered(bool)),
             this, SLOT( slot_EffectControl() ) );
    connect( m_pSoundOffAct, SIGNAL(triggered(bool)),
             this, SLOT( slot_EffectControl() ) );
}

/* 重写右键菜单响应事件 */
void Clock::contextMenuEvent(QContextMenuEvent *event)
{
    m_pRBMenu->exec(QCursor::pos()); // 在光标处弹出右键菜单
    event->accept();
}

/* 创建系统托盘 */
void Clock::createTray()
{
    /* 托盘图标 */
    systemTray = new QSystemTrayIcon(this);
    systemTray->setToolTip( tr("桌面时钟") );
    systemTray->setIcon(QIcon(":/ico/clock.ico"));
    systemTray->show();

    /* 托盘菜单 */
    systemTray->setContextMenu(m_pRBMenu); // 与右键菜单类似,QMenu类

    /* 托盘消息 */
//    systemTray->showMessage(tr("TiTle"), tr("msg"), QIcon("://msgIco.ico"), 1000); // 标题,消息内容,消息图标,持续时间
}

/* 右键菜单槽函数 */
void Clock::slot_showMaxiNormal()
{
    if (this->isFullScreen())
    {
        this->showNormal();
        m_pMaxiMinimizeAct->setText( tr("全屏") );
        m_pMaxiMinimizeAct->setIcon( QIcon(":/ico/fullscreen.ico") );
        return;
    }
    else
    {
        this->showFullScreen();
        m_pMaxiMinimizeAct->setText( tr("还原") );
        m_pMaxiMinimizeAct->setIcon( QIcon(":/ico/exitfullscreen.ico") );
        return;
    }
}

/* 音效控制槽函数 */
void Clock::slot_EffectControl()
{
    if ( m_pEffect->isPlaying() )
    {
        m_pEffect->stop();
        m_pSoundOffAct->setIcon( QIcon(":/ico/check.ico") );
        m_pSoundOnAct->setIcon( QIcon(":/ico/nocheck.ico") );
        return;
    }
    else
    {
        m_pSoundOnAct->setIcon( QIcon(":/ico/check.ico") );
        m_pSoundOffAct->setIcon( QIcon(":/ico/nocheck.ico") );

        QTime now;
        do{
            now = QTime::currentTime();
        }while(now.msec() >= 50 && now.msec() <= 950); // 音效对时

        m_pEffect->play();
        return;
    }
}

五 相关说明

3.1  Qt5 2D绘图简介

Qt5中QPainter类提供的APIGUIQImage、QOpenGLPaintDevice、QWidget和QPaintDevice显示图形(线、形状、渐变等)、文本和图像。

QPaintDevice不直接绘制物理显示画面,而利用逻辑界面的中间媒介。例如,绘制矩形图形时,为了将对象绘制到QWidget、QGLPixelBuffer、QImage、QPixmap、QPicture等多种界面中间,必须使用QPaintDevice。

QPainter为了在GUI上显示图形,利用光栅化(rasterizer)显示图形,并可以使用OpenGL和OpenGL ES的Back-end系统。基于软件的光栅化通过点显示到屏幕,而OPenGL和OpenGL ES方式则通过点表现图形[2]

Qt5绘图系统中由QPainter完成具体的绘制操作,其中,提供了大量高度优化的函数来完成GUI编程所需要的大部分绘制工作。QPainter可以绘制一切想要的图形,从最简单的一条直线到其他任何复杂的图形。QPainter可以在继承自QPaintDevice类的任何对象上进行绘制操作[3]

 

表3.1  使用QPainter的绘制方式

类 型

说 明

QImage

通过与硬件无关的绘制方式,直接访问像素以显示图形。QPainter利用QImage显示图形时,QImage实例使用基于软件的光栅化方式

QPixmap

在屏幕上显示图像时,提供优化。使用基于软件的光栅化方式显示图像

QOpenGLPaintDevice

使用OpenGL和OpenGL ES等基于硬件的加速器显示图形

QBackingStore

QWindow类,QBackingStore用于在顶层窗口的制图区域显示图形。QBackingStore不仅可以使用基于软件的光栅化方式,还可以使用基于OpenGL的硬件加速器

QWidget

QWidget类在控件区域显示图形


表3.2  QPainter类基本操作

操作内容

说 明

QPainter的基本绘图

可以绘制基本图形(点、线等)的QPen和填充图形内部颜色的QBrush

渐变(Gradients)

使用QBrush显示指定斜率方向的渐变图

转换(Transformation)

扩大和缩小(Scaling)、旋转(Rotation)、远近(Perspective)

组合(Composition)

提供组合各图层的功能图

 

3.2  时钟实现的原理

时钟的时针、分针、秒针位置每秒都在更新,故我们需要一个定时器来定时重绘。Qt5中提供了很方便的QTimer类,Qtimer类中有一非常重要的信号:timeout()Qt5帮助手册是这样描述这一信号的:

[signal] void QTimer::timeout()

This signal is emitted when the timer times out.

Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.

 

可知,当设定时间结束后,timer就会立即发出timeout信号。连接信号与槽后,即可立刻响应槽函数,Qt5重绘有一重要的APIupdate(),将其设置为响应的槽函数。

1  timer = new QTimer(this);

2  connect( timer, SIGNAL(timeout()), this, SLOT(update()) );

3  timer->start(1000);

 

1行代码为定时器timer申请空间,类型指定为QTimer类。第2行代码连接timeout()信号与update()槽函数。第3行代码以1000毫秒间隔启动定时器。

3.1描述了时钟实现的原理。

No.01 桌面时钟_第3张图片

图3.1  时钟走时流程图

3.3  绘制背景与外边框

(一)绘制时钟窗口背景图

Qt提供了4个类来处理图像数据:QImage、QPixmap、QBitmap和QPicture,都是常用的绘图设备。其中,QImage主要用来进行I/O处理,它对I/O处理操作进行了优化,而且也可以用来直接访问和操作像素;QPixmap主要用来在屏幕上显示图像,它对屏幕上显示图像进行了优化;QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演QPainter命令。

这里需要的是在屏幕上显示背景图像,故我们选择QPixmap类来绘制时钟窗口背景图像。QPixmap中的像素在内部底层的窗口系统进行管理,因为QPixmap是QPaintDevice的子类,所以QPainter也可以直接在它上面进行绘制[3]

首先,新建一个QPixmap类对象,图片资源由资源文件加载。再使用QPixmap类的“scaled()”方法将图像大小缩放至屏幕大小,得到一个缩放后的QPixmap类对象。

1  QPixmap *tmp = new QPixmap(":/clockUI/clockUI.jpg");

2  pixmap = tmp->scaled(this->width(),

    3                     this->height(),

    4                     Qt::IgnoreAspectRatio);

 

然后,通过重写QWidget“void paintevent(QPaintevent *event);”函数,调用QPainter类的“drawPixmap()”函数进行图像的绘制。

 

1  void Clock::paintEvent(QPaintEvent *e)

2  {

3    QPainter painter(this);

4    painter.drawPixmap(pixmap.rect(), pixmap);

5  }

 

(二) 绘制时钟表盘背景及外边框

时钟表盘背景为圆形带填充,外边框为圆形不填充。Qt对于绘制圆和椭圆的API是基于QPainter类的“drawEllipse()”方法,填充与否在于QPainter是否设置了笔刷(QBrush)。由于外边框应在表盘背景上层显示,故先绘制表盘背景,再绘制外边框。

1、绘制表盘背景:

1  int side = qMin(this->width(), this->height());

2  painter.save();

3  painter.setPen(Qt::NoPen);

4  painter.setBrush(QColor(112, 128, 144, 100));

5  painter.drawEllipse( QPoint(width()/2, height()/2), side/2, side/2 );

6  painter.restore();

 

2、绘制外边框:

1  painter.save();

2  painter.setPen(QPen( QColor(244, 164, 96, 130), 4 ));

3  painter.drawEllipse(QPoint(width()/2, height()/2), side/2 - 3, side/2 - 3);

4  painter.restore();

 

3.4  绘制时针、分针、秒针

3.4.1  QPainter对坐标系统的操作

Qt对于窗口坐标的设定是:左上角处为坐标原点(00),左为X轴正方向,下为Y轴正方向。

1)对于坐标轴原点移动,Qt的QPainter类提供了“translate()”方法,Qt帮助手册对于此函数的描述为:

void QPainter::translate(const QPointF &offset)

Translates the coordinate system by the given offset; i.e. the given offset is added to points.

那么,给QPainter类的“translate()”函数传入中心点坐标,即可移动坐标轴。

(2)对于坐标轴旋转,QPainter类提供了“rotate()”方法,Qt5帮助手册描述为:

void QPainter::rotate(qreal angle)

Rotates the coordinate system clockwise. The given angle parameter is in degrees.

数据类型qreal是double类型数据的重命名,所以将旋转角度转换为double类型,传入参数,即可将坐标轴旋转。Qt中坐标轴旋转默认是顺时针旋转。

(3)对于坐标轴缩放,QPainter类提供了“scaled()”方法,Qt5帮助手册描述为:

void QPainter::scale(qreal sx, qreal sy)

Scales the coordinate system by (sx, sy).

第一个参数代表X轴缩放比例,第二个参数代表Y轴缩放比例,都为qreal类型。

3.4.2  指针图形坐标设定与角度计算

(一)时针、分针、秒针图形的坐标设定

设定200x200像素为时钟基础长宽,将坐标原点移动至屏幕中央,时针图案、分针图案、秒针图案各由四个点组成,由QPoint数组表示。后面每秒都要重绘,而图案形状不改变,故将数组设定为static const类型,将之存储到寄存器中,提高访问速度[4]

(1)时针图案坐标点:

1  const QPoint Clock::hourHand[4] = {

2    QPoint(3, 5),

3    QPoint(0, 13),

4    QPoint(-3, 5),

5    QPoint(0, -40) };

图案效果如图3.2(a)所示。

 

(2)分针图案坐标点:

1  const QPoint Clock::minuteHand[4] = {

2    QPoint(3, 5),

3    QPoint(0, 16),

4    QPoint(-3, 5),

5    QPoint(0, -60) };

图案效果如图3.2(b)所示。


(3)秒针图案坐标点:

1  const QPoint Clock::secondHand[4] = {

2    QPoint(3, 5),

3    QPoint(0, 18),

4    QPoint(-3, 5),

5    QPoint(0, -80) };

图案效果如图3.2(c)所示。

 No.01 桌面时钟_第4张图片

                                                                    3.2(a)

No.01 桌面时钟_第5张图片

                                                                 3.2(b)

No.01 桌面时钟_第6张图片

                                                                   3.2(c)

(二)时针、分针、秒针旋转角度计算

3.3是一个典型的时钟钟面图,如图3.3所示,指针刻度将圆周分割成60份,相邻刻度线所形成的夹角为6°。时针1小时走过的角度为30°;分针1分钟所走过的角度为60°;秒针同分针。三者的等量关系可以用如下式子来表示:

1h = 60min = 3600sec                          3.1)

 No.01 桌面时钟_第7张图片

                                            图3.3  时钟钟面

 

  因12小时是指针行走的最大周期,故考虑这一范围内的情形。若某一瞬时时间记为a:b:c(例如:01:18:36),其中abc为整数且,,。显然不能单考虑a的值作为计算时针的行走角度,分针亦然,只有秒针可以根据c值来计算。为方便计算,将时间值统一化到单位秒来计算,则有:

            a:b:c→3600a+60b+c         3.2)

1)时针走过的角度为:

         (3600a+60b+c)×(1/120)h° = (30a + b/2 + c/120)h°             (3.3)

2)分针走过的角度为:

        (3600a+60b-c)×(1/10)min° = (360a + 6b + c/10)min°         3.4)

3)秒针走过的角度为:

       (3600a+60b+c)×6sec° = (21600a + 360b + 6c)sec°              3.5)

基于上述计算,忽略掉秒对时针角度的影响,假设时间为a:b:c,记时针旋转角度为h,分针旋转角度为m,秒针旋转角度为s。其中a、b、c为整数且,,;hym为大于0的小数,单位为度(°)。那么有:

        h = [30.0 × (a + b/60.0)]°                   3.6)

        m = [6.0 × (b + c/60.0)]°                    3.7)

         s = (6.0 × c)°                                       3.8)

 

3.4.3  绘制时针、分针、秒针

有了指针的图案坐标和旋转角度,接下来了就可以很方便绘制出指针了。具体步骤如下(以绘制时针为例):

(1)得到当前系统时间:

1  QTime time = QTime::currentTime();

 

(2)设置画笔和笔刷颜色:

1  painter->setPen(Qt::NoPen);

2  painter->setBrush(Qt::black);

 

(3)坐标轴旋转(角度参考3.6式):

1  painter->rotate( 30.0 * (time.hour() + time.minute()/60.0) );

 

(4)绘制凸多边形:

1  painter->drawConvexPolygon(hourHand, 4);

 

(5)还原坐标轴状态:

1  painter->restore();

分针、秒针绘制步骤类似。不同点在于:

(1)绘制分针画刷颜色设置为蓝色(Qt::blue),坐标点传入minuteHand”。

(2)绘制秒针画刷颜色为红色(Qt::red),坐标点传入secondHand”。

3.5  绘制表盘刻度和12小时数字

表盘一共要绘制60条刻度线,其中,从0°开始,每6°应绘制分钟刻度线,每30°应绘制区别于分钟刻度线的小时刻度线。设定一个起始数ii为整型(int)数据且,每绘制一条刻度线i自增1。那么可以设定每次的旋转角度为,当i能够整除5时,即绘制小时刻度线,否则绘制分钟刻度线。

但是坐标轴旋转会导致时钟下半表盘数字颠倒,要解决此问题,加入一个判断,当坐标轴旋转角度在0°至90°或者270°至360°之间,即i<15或者i>45时,同时绘制上半部分和下半部分数字即可。

代码如下:

1  void Clock::drawClockDial(QPainter *painter)

2  {

3    for (int i = 1; i <= 60; i++)

4    {

5      painter->save();

6      painter->rotate(6*i);

7      if ( (i % 5 == 0) && (i <= 15 || i >= 45) )

8      {

9         painter->setPen(QPen(palette().foreground(), 2.0));

10        painter->drawLine(0, -95, 0, -80);

    11        painter->drawText(-20, -82, 40, 40,

    12                          Qt::AlignHCenter | Qt::AlignTop,

    13                          QString::number(i/5) );

    14        if (i < 15 || i > 45)

    15        {

    16          painter->drawLine(0, 80, 0, 95);

    17          painter->drawText( -20, 41, 40, 40,

    18                               Qt::AlignHCenter | Qt::AlignBottom,

    19                               QString::number(i<15 ? i/5+6 : i/5-6) );

    20         }

    21      }//if

    22      else

    23      {

    24         painter->setPen(QPen(palette().foreground(), 1.0));

    25         painter->drawLine(0, 95, 0, 90);

    26      }

    27      painter->restore();

    28      }//for

29  }

 

算法流程如图3.4所示。

 No.01 桌面时钟_第8张图片

3.4  绘制表盘刻度和12小时数字算法流程图

3.6  绘制LCD风格的数字时间

    Qt5字体库并没有LCD风格的字体,为了是受那个更加地形象生动,加载字体文件无疑是最好的选择。Qt提供了QFontDatabase::addApplicationFont来加载字体文件至字体族中,从字体族中可以直接使用加载的外部字体,然后使用QPainter类的drawText方法绘制LCD风格的数字。具体步骤如下:

(1)从资源文件加载LCD字体,并将之存储到QstringList数据中:

1  int lcdFontId = QFontDatabase::addApplicationFont(":/lcdFont/DS-DIGI.ttf");

2  if (lcdFontId != -1)

3  {

4     m_fontList << QFontDatabase::applicationFontFamilies(lcdFontId);

5  }

2)新建QFont类对象,使用“setfamily()”方法设置字体,并给QPainter类对象(painter)设置字体:

1  if (!m_fontList.isEmpty())

2  {

3    QFont font;

4    font.setFamily(m_fontList.at(0));

5    font.setPointSize(15);

6    painter->setFont(font);

7  }

 

3)在相应位置绘制当前时间(格式为hh:mm:ss):

1  painter->drawText( -40, 34, 80, 22, Qt::AlignCenter,

2                  QTime::currentTime().toString("hh:mm:ss") );

效果如下图(图3.5)所示:

 

3.5  LCD风格数字时间绘制示例


源代码下载: http://download.csdn.net/download/wu9797/10196816

你可能感兴趣的:(Qt5实战)