通俗易懂玩Qt:时间滑动选择器实现(内附主要源码)

news2024/9/22 5:25:44

时间滑动选择器实现

组件说明:

  1. 本组件命名为时间滑动选择器,主要运用于 arm 平台下的触摸屏上,虽然 QT 自带有时间选择组件,但是对触摸屏的使用并不友好,为了提升项目界面的交互性,于是就有了时间滑动选择器,主要靠 QWidget 的 paintEvent 事件来完成的。
  2. 因为需求,本组件仅做了鼠标左键的拖滚事件,未做滑轮事件处理,有需要的可以自行添加。
  3. 代码非搬运,文章是原创。

时间滑动选择器的效果如下方 GIF 图片所示:
请添加图片描述
废话不多说,直接上代码:

//slidingbox.h
#ifndef SLIDINGBOX_H
#define SLIDINGBOX_H

#include <QWidget>

class QPropertyAnimation;

class SlidingBox : public QWidget {
    Q_OBJECT
    Q_PROPERTY(int offset READ getOffset WRITE setOffset)

public:
    explicit SlidingBox(QWidget *parent = nullptr);

    void setRange(int min, int max);
    void setCurrentVal(int val);
    int curentVal();

signals:
    void valueChanged(int val);

protected:
    void paintEvent(QPaintEvent *e) override;
    void mouseMoveEvent(QMouseEvent *e) override;
    void mousePressEvent(QMouseEvent *e) override;
    void mouseReleaseEvent(QMouseEvent *e) override;

private:
    int source_y,
        offset_y,
        max_val,
        min_val,
        current_val,
        val_len;
    bool is_press;
    QPropertyAnimation *prop_amt;

    void drawText(QPainter *paint, QString text, int offset);
    void setOffset(int val);
    int getOffset();
};

#endif // SLIDINGBOX_H
//slidingbox.cpp
#include "slidingbox.h"

#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>

#define WID_UNIT    20
#define FONT_SIZE   27
#define HIGT_UNIT   40

SlidingBox::SlidingBox(QWidget *parent) :
    QWidget(parent),
    source_y(0),
    offset_y(0),
    max_val(0),
    min_val(0),
    current_val(0),
    val_len(2),
    is_press(false) {
    prop_amt = new QPropertyAnimation(this, "offset");
    prop_amt->setDuration(300);
    prop_amt->setEasingCurve(QEasingCurve::OutQuad);
    setMinimumWidth(val_len * WID_UNIT);
}

void SlidingBox::setRange(int min, int max) {
    min_val = min;
    max_val = max;

    current_val = current_val > max ? max : current_val;
    current_val = current_val < min ? min : current_val;

    val_len = 0;
    while(max > 0) {
        max /= 10;
        val_len ++;
    }

    setMinimumWidth(val_len * WID_UNIT);

    update();//repaint();
}

void SlidingBox::setCurrentVal(int val) {
    current_val = val;

    current_val = current_val > max_val ? max_val : current_val;
    current_val = current_val < min_val ? min_val : current_val;

    update();//repaint();
}

int SlidingBox::curentVal() {
    return current_val;
}

void SlidingBox::paintEvent(QPaintEvent *e) {
    Q_UNUSED(e);

    QPainter paint(this);
    paint.setRenderHint(QPainter::Antialiasing, true);

    if(current_val - min_val >= 2) {
        drawText(&paint, QString::number(current_val - 2), offset_y + HIGT_UNIT * 0);
    }
    if(current_val > min_val) {
        drawText(&paint, QString::number(current_val - 1), offset_y + HIGT_UNIT * 1);
    }
    drawText(&paint, QString::number(current_val + 0), offset_y + HIGT_UNIT * 2);
    if(current_val < max_val) {
        drawText(&paint, QString::number(current_val + 1), offset_y + HIGT_UNIT * 3);
    }
    if(max_val - current_val >= 2) {
        drawText(&paint, QString::number(current_val + 2), offset_y + HIGT_UNIT * 4);
    }

    paint.setPen(QColor(121, 121, 121, 232));
    paint.drawLine(QLineF(0, HIGT_UNIT * 2, val_len * WID_UNIT, HIGT_UNIT * 2));
    paint.drawLine(QLineF(0, HIGT_UNIT * 3, val_len * WID_UNIT, HIGT_UNIT * 3));
}

void SlidingBox::mouseMoveEvent(QMouseEvent *e) {
    // 判别鼠标左键是否被按下
    if(is_press) {
        // 禁止越界
        if((current_val == max_val && e->y() <= source_y) ||
                (current_val == min_val && e->y() >= source_y)) { return; }

        // 更新鼠标 y 坐标的偏移
        offset_y = e->y() - source_y;

        // 下滑降值
        if(offset_y > HIGT_UNIT) {
            source_y += HIGT_UNIT;
            offset_y %= HIGT_UNIT;
            current_val --;
            repaint();
            return;
        }

        // 上滑增值
        if(offset_y < -HIGT_UNIT) {
            source_y -= HIGT_UNIT;
            offset_y %= HIGT_UNIT;
            current_val ++;
            repaint();
            return;
        }

        update();//repaint();
    }
}

void SlidingBox::mousePressEvent(QMouseEvent *e) {
    // 判别鼠标左键
    if(e->button() == Qt::LeftButton) {
        //
        prop_amt->stop();
        // 记录鼠标起始 y 坐标
        source_y = e->y();
        // 按下鼠标左键
        is_press = true;
    }
}

void SlidingBox::mouseReleaseEvent(QMouseEvent *e) {
    // 判别鼠标左键
    if(e->button() == Qt::LeftButton) {
        if(is_press) {
            if(offset_y > HIGT_UNIT / 2) {
                current_val - 1 < min_val ? prop_amt->setStartValue(offset_y) :
                                            prop_amt->setStartValue(HIGT_UNIT / 2 - offset_y);
                prop_amt->setEndValue(0);
                current_val = current_val - 1 < min_val ? min_val : current_val - 1;
            }
            else if(offset_y > -HIGT_UNIT / 2) {
                prop_amt->setStartValue(offset_y);
                prop_amt->setEndValue(0);
            }
            else {
                current_val + 1 > max_val ? prop_amt->setStartValue(offset_y) :
                                            prop_amt->setStartValue(- offset_y - HIGT_UNIT / 2);
                prop_amt->setEndValue(0);
                current_val = current_val + 1 > max_val ? max_val : current_val + 1;
            }

            emit valueChanged(current_val);
            prop_amt->start();
        }

        // 释放鼠标左键
        is_press = false;
    }
}

void SlidingBox::drawText(QPainter *paint, QString text, int offset) {
    int size = FONT_SIZE - qAbs(offset - HIGT_UNIT * 2) / 10,
        transparent = 255 - 255 * qAbs(offset - HIGT_UNIT * 2) * 2 / HIGT_UNIT / 5;
    transparent = transparent < 0 ? 0 : transparent;
    QFont font;
    font.setPixelSize(size);
    paint->setFont(font);
    paint->setPen(QColor(0, 0, 0, transparent));
    while(text.length() < val_len) {
        text = QString("0%1").arg(text);
    }
    paint->drawText(QRectF(0, offset, val_len * WID_UNIT, HIGT_UNIT), Qt::AlignCenter, text);
}

void SlidingBox::setOffset(int val) {
    offset_y = val;
    repaint();
}

int SlidingBox::getOffset() {
    return offset_y;
}
//timeselector.h
#ifndef TIMESELECTOR_H
#define TIMESELECTOR_H

#include <QWidget>

class SlidingBox;

class TimeSelector : public QWidget {
    Q_OBJECT
public:
    explicit TimeSelector(QWidget *parent = nullptr);
    ~TimeSelector();

    void setTime(int y, int m, int d, int h = 0, int min = 0, int s = 0);
    void setTime(QString time);
    void setTime(QDateTime dt);
    QString getTime();

signals:
    void timeNew(QString time);

private:
    QList<SlidingBox *> sdbl;
    QStringList sdb_strl;
    QList<int> li_sdb_grange,
               li_md;

    void resetFebDays(int year, int month);
    SlidingBox *sdb(QString sdb_str);
    void showCurrentTime();
};
#endif // TIMESELECTOR_H
//timeselector.cpp
#include "timeselector.h"
#include "slidingbox.h"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QDateTime>

TimeSelector::TimeSelector(QWidget *parent) :
    QWidget(parent) {
    sdb_strl << "年" << "月" << "日" << "时" << "分" << "秒";
    li_sdb_grange << 2000 << 2500 << 1 << 12 << 1 << 31 << 0 << 23 << 0 << 59 << 0 << 59;
    li_md << 31 << 28 << 31 << 30 << 31 << 30 << 31 << 31 << 30 << 31 << 30 << 31;

    QHBoxLayout *hup_layout = new QHBoxLayout;
    hup_layout->setSpacing(5);
    hup_layout->setContentsMargins(0, 0, 0, 0);

    hup_layout->addStretch();
    for(int i = 0; i < sdb_strl.length(); i ++) {
        SlidingBox *sdb = new SlidingBox;
        sdb->setRange(li_sdb_grange.at(i * 2), li_sdb_grange.at(i * 2 + 1));
        hup_layout->addWidget(sdb);
        sdbl << sdb;

        QLabel *l_tip = new QLabel(sdb_strl.at(i));
        l_tip->setStyleSheet("font-size: 23px;");
        hup_layout->addWidget(l_tip);
    }
    hup_layout->addStretch();

    QWidget *w_up = new QWidget;
    w_up->setLayout(hup_layout);
    w_up->setStyleSheet("min-height: 200px;max-height: 200px;");

    QHBoxLayout *hdown_layout = new QHBoxLayout;
    hdown_layout->setSpacing(5);
    hdown_layout->setContentsMargins(0, 0, 0, 0);

    QPushButton *btn_now = new QPushButton("现在");
    btn_now->setStyleSheet("min-height: 34px;max-height: 34px;font-size: 19px;");
    hdown_layout->addWidget(btn_now);
    hdown_layout->addStretch();

    QPushButton *btn_cancel = new QPushButton("取消");
    btn_cancel->setStyleSheet("min-height: 34px;max-height: 34px;font-size: 19px;");
    hdown_layout->addWidget(btn_cancel);

    QPushButton *btn_sure = new QPushButton("确定");
    btn_sure->setStyleSheet("min-height: 34px;max-height: 34px;font-size: 19px;");
    hdown_layout->addWidget(btn_sure);

    QWidget *w_down = new QWidget;
    w_down->setLayout(hdown_layout);

    QVBoxLayout *v_layout = new QVBoxLayout;
    v_layout->setSpacing(0);
    v_layout->addWidget(w_up);
    v_layout->addWidget(w_down);

    setLayout(v_layout);
    setMinimumSize(500, 280);
    setMaximumSize(500, 280);
    setWindowFlags(Qt::FramelessWindowHint);
    setStyleSheet("background-color: #fff;");

    resetFebDays(li_sdb_grange.at(sdb_strl.indexOf("年") * 2),
                 li_sdb_grange.at(sdb_strl.indexOf("月") * 2));

    connect(sdb("年"), &SlidingBox::valueChanged, [=](int year) {
        resetFebDays(year, sdb("月")->curentVal());
    });

    connect(sdb("月"), &SlidingBox::valueChanged, [=](int month) {
        sdb("日")->setRange(1, li_md.at(month - 1));
    });

    connect(btn_now, &QPushButton::clicked, [=] {
        showCurrentTime();
    });

    connect(btn_cancel, &QPushButton::clicked, [=] {
        close();
    });

    connect(btn_sure, &QPushButton::clicked, [=] {
        emit timeNew(getTime());
        close();
    });

    setTime(2024, 8, 1, 15, 4, 32);
//    setTime("2024-08-01 20:47:15");
}

TimeSelector::~TimeSelector() {
}

void TimeSelector::setTime(int y, int m, int d, int h, int min, int s) {
    sdb("年")->setCurrentVal(y);
    sdb("月")->setCurrentVal(m);
    sdb("日")->setCurrentVal(d);
    sdb("时")->setCurrentVal(h);
    sdb("分")->setCurrentVal(min);
    sdb("秒")->setCurrentVal(s);
}

void TimeSelector::setTime(QString time) {
    QRegExp regexp("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$");
    if(regexp.exactMatch(time)) {
        QDateTime dt = QDateTime::fromString(time, "yyyy-MM-dd hh:mm:ss");
        setTime(dt.date().year(), dt.date().month(), dt.date().day(),
                dt.time().hour(), dt.time().minute(), dt.time().second());
    }
}

void TimeSelector::setTime(QDateTime dt) {
    if(!dt.isValid()) {
        setTime(dt.date().year(), dt.date().month(), dt.date().day(),
                dt.time().hour(), dt.time().minute(), dt.time().second());
    }
}

QString TimeSelector::getTime() {
    QString y = QString::number(sdb("年")->curentVal()),
            m = sdb("月")->curentVal() < 10 ? "0" + QString::number(sdb("月")->curentVal()) :
                                             QString::number(sdb("月")->curentVal()),
            d = sdb("日")->curentVal() < 10 ? "0" + QString::number(sdb("日")->curentVal()) :
                                             QString::number(sdb("日")->curentVal()),
            h = sdb("时")->curentVal() < 10 ? "0" + QString::number(sdb("时")->curentVal()) :
                                             QString::number(sdb("时")->curentVal()),
            min = sdb("分")->curentVal() < 10 ? "0" + QString::number(sdb("分")->curentVal()) :
                                               QString::number(sdb("分")->curentVal()),
            s = sdb("秒")->curentVal() < 10 ? "0" + QString::number(sdb("秒")->curentVal()) :
                                             QString::number(sdb("秒")->curentVal());
    return QString("%1-%2-%3 %4:%5:%6").arg(y, m, d, h, min, s);
}

void TimeSelector::resetFebDays(int year, int month) {
    if(month == 2) {
        sdb("日")->setRange(1, ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 29 : 28);
    }
    else {
        li_md.replace(1, ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 29 : 28);
    }
}

SlidingBox *TimeSelector::sdb(QString sdb_str) {
    return sdbl.at(sdb_strl.indexOf(sdb_str));
}

void TimeSelector::showCurrentTime() {
    QDateTime dt = QDateTime::currentDateTime();
    setTime(dt.date().year(), dt.date().month(), dt.date().day(),
            dt.time().hour(), dt.time().minute(), dt.time().second());
}

使用参考:

TimeSelector *tsor = new TimeSelector;
tsor->show();
connect(tsor, &TimeSelector::timeNew, [=](QString time) {
	//
});

代码注释就不想加了,太麻烦了,确实想学习逻辑的话,可以参考下面的文章,我也是参考别人写的来实现的,逻辑大致相同,只是代码实现不一样。

借鉴文章:
《QT自定义控件–滑动选择器》
《QT日期时间选择器自定义滚动选择》
《Qt滚动日期选择器》

学习分享,一起成长!以上为小编的经验分享,若存在不当之处,请批评指正!
欢迎加入学习交流群:嵌入式技术交流群

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1972454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深海王国】初中生也能画的电路板?番外1:Arduino其他家族成员的拓展板开发(1)

Hi~ (o^^o)♪, 各位深海王国的同志们&#xff0c;早上下午晚上凌晨好呀~ 辛苦工作的你今天也辛苦啦(/≧ω) 今天大都督为大家带来电路板的番外系列——初中生也能画的电路板&#xff1f;番外1&#xff1a;Arduino其他家族成员的拓展板开发&#xff0c;带你给其他Arduino家族成…

数据库漫游记:表、视图、函数、存储过程及触发器之跨平台兼容性分析(上)

先言之 &#x1f31f;余撰此文&#xff0c;乃为导引初窥数据库之学人&#xff0c;俾其明了表、视图、函数、存储过程及触发器之义理&#xff0c;及其于诸般平台之上创建、修改与废弃之法式。盖初学之人&#xff0c;常陷于迷雾之中&#xff0c;难辨东西&#xff0c;故须详述而明…

lombok使用@slf4j 运行时提示找不到符号log(Missing POM for org.projectors:lombok:jar)

1.问题表现 原本是之前搭建好的工程&#xff0c;只是换了个开发环境重新启动就不行了。一直编译不通过&#xff01; 可以看到IDEA其实是引入了依赖的 都没有出现红色波浪线 <mapstruct.version>1.5.5.Final</mapstruct.version> <lombok.version>1.18.30<…

鸿蒙(API 12 Beta2版)NDK开发【JSVM-API使用规范】

JSVM-API使用规范 生命周期管理 【规则】 合理使用OH_JSVM_OpenHandleScope和OH_JSVM_CloseHandleScope管理JSVM_Value的生命周期&#xff0c;做到生命周期最小化&#xff0c;避免发生内存泄漏问题。 每个JSVM_Value属于特定的HandleScope&#xff0c;HandleScope通过OH_JSV…

【Python实战因果推断】71_图因果模型6

目录 Positivity Assumption An Identification Example with Data Confounding Bias Positivity Assumption 调整公式同样强调了正则性&#xff08;positivity&#xff09;的重要性。因为你正在对治疗和结果之间的差异在X的条件下求平均&#xff0c;你必须确保对于所有X的…

【32单片机篇】项目:WIFI天气预报

一、项目需求 使用 ESP8266 通过 HTTP 获取天气数据&#xff08;心知天气&#xff09;&#xff0c;并显示在 OLED 屏幕上。 按键 1 &#xff1a;循环切换今天/明天/后天天气数据&#xff1b; 按键 2 &#xff1a;更新天气 二、项目框图 三、硬件部分 四、项目源码及实现 1.项…

MySQL是怎样运行的——第1章 初识MySQL

文章目录 1. 1 MySQL的客户端/服务器架构1.2 安装MySQL&#xff08;略&#xff09;1.3 启动MySQL服务器程序1.4 启动MySQL客户端程序1.5 客户端与服务器连接的过程1.6 服务器处理客户端请求 1. 1 MySQL的客户端/服务器架构 MySQL的运行过程就是C/S架构。多个客户端程序连接到服…

洛谷 P1868 饥饿的奶牛

原题 题目描述 有一条奶牛冲出了围栏&#xff0c;来到了一处圣地&#xff08;对于奶牛来说&#xff09;&#xff0c;上面用牛语写着一段文字。 现用汉语翻译为&#xff1a; 有 N 个区间&#xff0c;每个区间x,y 表示提供的x∼y 共y−x1 堆优质牧草。你可以选择任意区间但不…

dockerfile定制镜像 docker-compose编排容器

1 dockerfile dockerfile本质上是利用了Linux系统的挂载&#xff08;UnionFS&#xff09;&#xff0c;将多个目录挂载到同一目录下&#xff0c;实现镜像的层叠式结构&#xff0c;从而实现功能聚合。 1.1 一个最简单的程序 package mainimport "fmt"func main() {f…

【leetcode详解】覆盖所有点的最少矩形数目(C++思路详解)

思路详解&#xff1a; 0. 题目情境并未限制矩形高度&#xff0c;故矩形数目的判断只和点的横坐标有关 1. 为了不重不漏地考虑到所有点&#xff0c;故笔者选择首先将二维数组中的点按横坐标的大小排序 //说明&#xff1a;本来笔者以为需要自定义sort排序&#xff0c;后来发现…

智慧水务项目(三)django(drf)+angular 18 创建系统管理的用户、角色、部门、权限管理等model

一、说明 添加各model 添加requirement.txt中的库 添加env.py中的动态配置 二、env.py全文 import os from smartwater.settings import BASE_DIR# # # ************** mysql数据库 配置 ************** # # # # 数据库地址 DATABASE_ENGINE "django.db.backends.…

SQL查询注意事项

判断字符串长度要用函数CHAR_LENGTH(str)&#xff0c;他会返回字符串的长度&#xff0c;如果使用length(str)函数&#xff0c;在对中文字符或特殊字符时&#xff0c;返回的是在当前编码下该字符的字节数。如在mysql中的utf-8编码情况下&#xff0c;length(&#xffe5;)返回结果…

ASUS/华硕幻14 2021 GA401Q系列 原厂win10系统 工厂文件 带F12 ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;windows10 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-62 - 判断元素是否可操作

软件测试微信群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 1.简介 有些页面元素的生命周期如同流星一闪&#xff0c;昙花一现。我们也不知道这个元素在没在页面中出现过&#xff0c;为了捕获这一美好瞬间&#xff0c;让其成为永恒。我们就来判…

HiFi桌搭的新选择,更符合发烧友使用习惯的飞傲K19

飞傲的便携设备很多人都用过&#xff0c;性价比很高&#xff0c;设计也很精致&#xff0c;并且功能上针对不同的需求也有丰富的选择&#xff0c;而在针对耳机的台式设备方面&#xff0c;飞傲同样非常有实力&#xff0c;我最近用了一款飞傲K19&#xff0c;这台设备不仅功能完备&…

ZYNQ SDK/Vitis undefined reference to `sin‘ wave.c

问题 ZYNQ SDK或者VItis在使用到sin函数时编译报错&#xff1a; undefined reference to sin wave.c问题解决&#xff1a; 右键点开C/C Build Settings 选项 在Settings 选项的Libraries 添加 m&#xff0c;点击OK

用Java手写jvm之模拟运行时数据区的虚拟机栈,局部变量表,操作数栈等

写在前面 下图是jvm的运行时数据区内存图&#xff1a; &#xff0c;本文要模拟的是虚拟机栈的相关内存结构的交互过程。 1&#xff1a;正文 因为我们这里模拟的是线程执行方法调用的过程&#xff0c;所以这里先来定义一个线程对象&#xff1a; public class Thread {// 程…

卸载Windows软件的正确姿势,你做对了吗?

前言 今天有小伙伴突然问我&#xff1a;她把软件都卸载了&#xff0c;但是怎么软件都还在运行&#xff1f; 这个问题估计很多小伙伴都是遇到过的&#xff0c;对于电脑小白来说&#xff0c;卸载Windows软件真的真的真的是一件很难的事情。所以&#xff0c;今天咱们就来讲讲&am…

springboot贫困生认定和资助管理系统-计算机毕业设计源码71367

摘要 本文介绍了一个基于Java的贫困生认定和资助管理系统的设计与实现。该系统旨在通过自动化、智能化的管理方式&#xff0c;提高贫困生认定和资助工作的效率与准确性。系统采用Java作为主要开发语言&#xff0c;结合了数据库技术和Web开发技术&#xff0c;实现了贫困生信息的…