【QT】实现桌面透明时钟盘--QPainter

效果图录屏

桌面时钟

在这里插入图片描述

画盘原理

通过效果视频可以看到一直在转动的是秒盘,其他盘如分盘只有在秒盘转慢一圈时才会转一下,那时盘只会在分盘转一圈时转一下,所以为了秒盘转动的流畅性,我把除秒盘之外的其他盘预先画在一个画布上(QPixmap),那秒盘转圈时我只用每次在预先画好其他盘的画布上画秒盘即可,当秒盘转一圈时,再更新这个底层的画布。

先来获取当前的系统时间,这个是每更新一次画面就会去获取一次这个时间。

	QDateTime current_date_time = QDateTime::currentDateTime();
    QString current_tim = current_date_time.toString("zzz");
    QString current_date = current_date_time.toString("yyyy-MM-dd-ddd");
    current_tim = current_date_time.toString("ss");
    int sec = current_tim.toInt();
    current_tim = current_date_time.toString("zzz");
    int ms = current_tim.toInt();

预设一下码盘的内容:

QStringList second;
second << "零"<< "一"<< "二"<< "三"<< "四"<< "五"<< "六"<< "七"<< "八"<< "九" << "十"<< "十一"<< "十二"
 << "十三"<< "十四"<< "十五" << "十六" << "十七"<< "十八" << "十九" << "二十" << "二十一" << "二十二"
<< "二十三" << "二十四" << "二十五" << "二十六"<< "二十七"<< "二十八" << "二十九" << "三十"<< "三十一"
 << "三十二" << "三十三" << "三十四" << "三十五"<< "三十六" << "三十七"<< "三十八"<< "三十九"<< "四十"
<< "四十一" << "四十二"<< "四十三" << "四十四" << "四十五" << "四十六" << "四十七"<< "四十八" << "四十九" 
<< "五十" << "五十一"<<"五十二"<< "五十三"<<"五十四"<<"五十五"<<"五十六"<< "五十七"<< "五十八"<< "五十九";

设置一下内容的字体格式:

	QFont font("黑体", 9, QFont::Black, false);
    font.setBold(true);
    //设置字符间的间距
    font.setLetterSpacing(QFont::AbsoluteSpacing, 5);

我们先创建一个底层的画布,这个画布会预先画好除秒盘之外的码盘:

QPixmap pix; //总画布
pix = QPixmap(UI_WIDTH, UI_HIGH);//这个是画布的长和宽

然后来在这个画布上画秒盘之外的盘,但是这写盘只会在分钟变化时重新画一次:

	current_tim = current_date_time.toString("mm");
    int min = current_tim.toInt();
    /* 当分改变时才重新画底布 */
    if (minute != min) {
        //使用字体
        QPixmap tmp_pix = QPixmap(UI_WIDTH, UI_HIGH); //临时画布,先在临时画布上画,防止页面卡顿
        QPainter pain(&tmp_pix);
        pain.setFont(font);
        pain.setPen(Qt::blue);
        minute = min;
        for (uint8_t i = 0; i < second.count(); i++) {
            pain.save();
            pain.translate(min_x[i], min_y[i]);
            pain.rotate(-i * 6);
            pain.translate(-min_x[i], -min_y[i]);
            if (i == 0) {
                pain.setOpacity(1);
            } else {
                pain.setOpacity(0.5);
            }
            QString str = second[(min + i) % 60] + "分";
            pain.drawText(min_x[i], min_y[i], str);
            pain.restore();
        }
        pix = tmp_pix;//画完之后就把临时画布上的内容转移到底层画布
     }

以上是除秒盘之外的更新方法和画法,具体实现我是分盘、时盘、日盘、月盘一起画的,这里代码太长,没有贴出来
我再来解释下画分盘代码是的min_x和min_y变量的由来,这个是把分盘的没一个成员放到合适的位置上组成一个圆形:

	for (uint8_t i = 0; i < 60; i++) {
        min_x[i] = cos((-i * 0.104719753)) * (UI_MID_WIDTH - ONE_CIRCLE_WIDTH * 2) + UI_MID_WIDTH;
        min_y[i] = sin(-i * 0.104719753) * (UI_MID_HIGH - ONE_CIRCLE_WIDTH * 2) + UI_MID_HIGH;
    }

UI_MID_WIDTH 和 UI_MID_HIGH 这两个宏定义是整个画布长宽的中点,组合起来就是整个画布的中心坐标
ONE_CIRCLE_WIDTH 是预留给每个盘的字体的宽度,你可以认为有一个内环和一个外环,这个内环和外环之间的距离就是这个宽度。
0.104719753这个是计算倾斜角度的,为了让每个成员在各自的位置上有一个倾斜角,使得每个成员都的位置都指向圆心。
时盘、日盘、月盘都会计算这个,就是为了能让每个盘放在合适的位置上。
那现在我们来画秒盘:

/* 画秒盘 */
    QPixmap sec_pix = pix; //临时画布  将底层画布铺在临时画布上
    QPainter pain_sec(&sec_pix);
    pain_sec.setFont(font);
    pain_sec.setPen(Qt::red);
    for (uint8_t i = 0; i < second.count(); i++) {
        //使用字体
        int32_t x = 0;
        int32_t y = 0;
        x = cos((-i * 0.104719753) - (0.104719753 - ms * 0.000104719753)) * (UI_MID_WIDTH - ONE_CIRCLE_WIDTH) + UI_MID_WIDTH;//这里为了让旋转动画更顺滑  根据ms数据来进行调整角度
        y = sin(-i * 0.104719753 - (0.104719753 - ms * 0.000104719753)) * (UI_MID_HIGH - ONE_CIRCLE_WIDTH) + UI_MID_HIGH;
        pain_sec.save();
        pain_sec.translate(x, y);
        pain_sec.rotate((-i) * 6);//旋转角度
        pain_sec.translate(-x, -y);//回到原点  这个是必要的  不然画笔会乱跑
        QString str = second[(sec + i) % 60] + "秒";
        float opacity = 0.3;
        if (i == 0) {
            opacity = ((ms * 0.001)) < 0.3 ? 0.3 : ((ms * 0.001));
            pain_sec.setOpacity(opacity);
        } else if (i == (second.count() - 1)) {
            opacity = (1 - (ms * 0.001)) < 0.3 ? 0.3 : (1 - (ms * 0.001));
            pain_sec.setOpacity(opacity);
        } else {
            pain_sec.setOpacity(0.3);
        }
        pain_sec.drawText(x, y, str);
        pain_sec.restore();
    }

然后将我们画的内容显示在窗口上

	QSize picSize(window_width, window_high);
    QPixmap tmp_sec_pix = sec_pix.scaled(picSize);
    QPainter painter_play(this);
    painter_play.drawPixmap(0, 0, tmp_sec_pix);

那如果想让这个盘动起来,就需要持续不断的刷新页面:

	QTimer* pTimer = new QTimer(this);
    pTimer->setTimerType(Qt::PreciseTimer);//精准定时
    pTimer->start(50); /* 定时 50ms;每50ms刷新一次界面 */
    connect(pTimer, SIGNAL(timeout()), this, SLOT(update()));

那有了显示的旋转在动的盘,那我们想把他放在合适的地方,缩放成合适的大小,甚至开机自启动,那该怎么做呢?
我们先把整个盘变透明并且能够缩放:

	this->setAttribute(Qt::WA_TranslucentBackground, true);
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setWindowModality(Qt::WindowModal);

那再来移动,为了不影响使用人的正常办公,我使用鼠标中键来触发移动:

void my_time::mousePressEvent(QMouseEvent* event)
{
    QMainWindow::mousePressEvent(event);
    if (nullptr == event)
        return;
    if (event->button() == Qt::MidButton) {
        isDragging = true;
        mouse_startPoint = event->globalPos();
        window_top_left_point = this->frameGeometry().topLeft();
    }
}

void my_time::mouseMoveEvent(QMouseEvent* event)
{
    QMainWindow::mouseMoveEvent(event);
    if (nullptr == event) {
        return;
    }
    if (isDragging) {
        QPoint distence = event->globalPos() - mouse_startPoint;
        this->move(window_top_left_point + distence);
    }
}

void my_time::mouseReleaseEvent(QMouseEvent* event)
{
    QMainWindow::mouseReleaseEvent(event);
    if (nullptr == event)
        return;
    if (event->button() == Qt::LeftButton)
        isDragging = false;
}

那再来个开机自启动,这个记得慎用,因为这个会放在电脑的自启动列表里,每次电脑开机时都会自动启动:

void my_time::setProcessAutoRun(const QString& appPath, bool flag)
{
    QSettings settings(AUTO_RUN, QSettings::NativeFormat);

    //以程序名称作为注册表中的键,根据键获取对应的值(程序路径)
    QFileInfo fInfo(appPath);
    QString name = fInfo.baseName(); //键-名称

    //如果注册表中的路径和当前程序路径不一样,则表示没有设置自启动或本自启动程序已经更换了路径
    QString oldPath = settings.value(name).toString(); //获取目前的值-绝对路劲
    QString newPath = QDir::toNativeSeparators(appPath); //toNativeSeparators函数将"/"替换为"\"
    if (flag) {
        if (oldPath != newPath)
            settings.setValue(name, newPath);
    } else
        settings.remove(name);
}

使用这个函数:

setProcessAutoRun(QApplication::applicationFilePath(), 1);

至此功能完善
源代码:https://download.csdn.net/download/SXD_SJJ/87625480

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值