QT-自定义滑动式日期选择

该文介绍了一个使用QT5.13.2在VS2017环境下开发的滑动式日期时间选择器,用户可通过滑动选择年、月、日、小时、分钟和秒。选择器通过cSliderDateTime和cSlider类实现,具备触摸屏操作友好、样式舒适的特点。代码示例展示了如何创建和更新日期时间选择器,并提供了调整日期时自动更新最大天数的功能。
摘要由CSDN通过智能技术生成


前言

1、使用鼠标滑动的方式选择指定的日期时间,并且获取当前选中的时间,整体样式看来十分舒服,更加适用触摸屏的方式。

2、本次demo使用的开发环境是VS2017+QT5.13.2开发的方式,如果你是使用creator的IDE,那需要自己将文件拷贝到自己的工程里面了。


一、效果演示

请添加图片描述

二、注意说明

这里用到了控件说明,需要注意的地方就是属性配置,见下图。
请添加图片描述

在这里插入图片描述

二、关键程序

1.SliderDateTime.cpp


#include "SliderDateTime.h"
#include <QBoxLayout>
#include "Slider.h"
#include <QDebug>
#pragma execution_character_set("utf-8")
cSliderDateTime::cSliderDateTime(QWidget *parent) 
	: QWidget(parent)
{

}

void cSliderDateTime::createDateSlider()
{
	// Year
	sliderYear = new cSlider(this);
	QStringList listYear;
	for (int i = 2000; i <= 2100; i++)
	{
		listYear << QString("%1").arg(i);
	}
	sliderYear->setListValue(listYear);

	// Month
	sliderMonth = new cSlider(this);
	QStringList listMonth;
	for (int i = 1; i <= 12; i++)
	{
		listMonth << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderMonth->setListValue(listMonth);

	// Day
	sliderDay = new cSlider(this);
	QStringList listDay;
	for (int i = 1; i <= 31; i++)
	{
		listDay << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderDay->setListValue(listDay);


	// 将选择器添加到布局
	QHBoxLayout *layout = new QHBoxLayout(this);
	layout->setMargin(0);
	layout->setSpacing(0);
	layout->addWidget(sliderYear);
	layout->addWidget(sliderMonth);
	layout->addWidget(sliderDay);

	connect(sliderYear, SIGNAL(currentValueChanged(QString)), this, SLOT(currentValueChanged(QString)));
	connect(sliderMonth, SIGNAL(currentValueChanged(QString)), this, SLOT(currentValueChanged(QString)));
}

void cSliderDateTime::createTimeSlider()
{
	// Hour
	sliderHour = new cSlider(this);
	QStringList listHour;
	for (int i = 0; i <= 23; i++)
	{
		listHour << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderHour->setListValue(listHour);

	// Min
	sliderMin = new cSlider(this);
	QStringList listMin;
	for (int i = 0; i <= 59; i++)
	{
		listMin << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderMin->setListValue(listMin);

	// Sec
	sliderSec = new cSlider(this);
	QStringList listSec;
	for (int i = 0; i <= 59; i++)
	{
		listSec << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderSec->setListValue(listSec);

	QHBoxLayout *layout = new QHBoxLayout(this);
	layout->setMargin(0);
	layout->setSpacing(0);
	layout->addWidget(sliderHour);
	layout->addWidget(sliderMin);
	layout->addWidget(sliderSec);
}

void cSliderDateTime::createDateTimeSlider()
{
	// Year
	sliderYear = new cSlider(this);
	QStringList listYear;
	for (int i = 2015; i <= 2030; i++)
	{
		listYear << QString("%1").arg(i);
	}
	sliderYear->setListValue(listYear);

	// Month
	sliderMonth = new cSlider(this);
	QStringList listMonth;
	for (int i = 1; i <= 12; i++)
	{
		listMonth << QString("%1 月").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderMonth->setListValue(listMonth);

	// Day
	sliderDay = new cSlider(this);
	QStringList listDay;
	for (int i = 1; i <= 31; i++)
	{
		listDay << QString("%1 日").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderDay->setListValue(listDay);

	// Hour
	sliderHour = new cSlider(this);
	QStringList listHour;
	for (int i = 0; i <= 23; i++)
	{
		listHour << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderHour->setListValue(listHour);

	// Min
	sliderMin = new cSlider(this);
	QStringList listMin;
	for (int i = 0; i <= 59; i++)
	{
		listMin << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderMin->setListValue(listMin);

	// Sec
	sliderSec = new cSlider(this);
	QStringList listSec;
	for (int i = 0; i <= 59; i++)
	{
		listSec << QString("%1").arg(i, 2, 10, QLatin1Char('0'));
	}
	sliderSec->setListValue(listSec);

	QHBoxLayout *layout = new QHBoxLayout(this);
	layout->setMargin(0);
	layout->setSpacing(0);
	layout->addWidget(sliderYear);
	layout->addWidget(sliderMonth);
	layout->addWidget(sliderDay);
	layout->addWidget(sliderHour);
	layout->addWidget(sliderMin);
	layout->addWidget(sliderSec);

	connect(sliderYear, SIGNAL(currentValueChanged(QString)), this, SLOT(currentValueChanged(QString)));
	connect(sliderMonth, SIGNAL(currentValueChanged(QString)), this, SLOT(currentValueChanged(QString)));
}

void cSliderDateTime::currentValueChanged(const QString &)
{
    int month = sliderMonth->getCurrentValue().left(2).toInt();

    // 记住之前的日期
    int day = sliderDay->getCurrentValue().left(2).toInt();

    // 计算该月最大日期
    int maxDay = 30;
    if (month == 2) 
	{
        // 平年28天 闰年29天
        int year = sliderYear->getCurrentValue().left(4).toInt();
        bool isLoopYear = (((0 == (year % 4)) && (0 != (year % 100))) || (0 == (year % 400)));
        if (isLoopYear) 
		{
            maxDay = 29;
        } 
		else 
		{
            maxDay = 28;
        }
    } 
	else if (month == 1 
		|| month == 3 
		|| month == 5 
		|| month == 7 
		|| month == 8 
		|| month == 10 
		|| month == 12) 
	{
        maxDay = 31;
    }

    QStringList listDay;
    for (int i = 1; i <= maxDay; i++) 
	{
        listDay << QString("%1 日").arg(i, 2, 10, QLatin1Char('0'));
    }
    sliderDay->setListValue(listDay);

    // 如果上次的日期大于最大的日期则设置为最大的日期
    if (day > maxDay) 
	{
        sliderDay->setCurrentIndex(maxDay - 1);
    } 
	else 
	{
        sliderDay->setCurrentIndex(day - 1);
    }
}

int cSliderDateTime::getYear() const
{
    return sliderYear->getCurrentValue().toInt();
}

int cSliderDateTime::getMonth() const
{
    return sliderMonth->getCurrentValue().left(2).toInt();
}

int cSliderDateTime::getDay() const
{
    return sliderDay->getCurrentValue().left(2).toInt();
}

int cSliderDateTime::getHour() const
{
    return sliderHour->getCurrentValue().toInt();
}

int cSliderDateTime::getMin() const
{
    return sliderMin->getCurrentValue().toInt();
}

int cSliderDateTime::getSec() const
{
    return sliderSec->getCurrentValue().toInt();
}

void cSliderDateTime::setYear(int year)
{
    sliderYear->setCurrentValue(QString("%1").arg(year));
}

void cSliderDateTime::setMonth(int month)
{
    sliderMonth->setCurrentValue(QString("%1 月").arg(month, 2, 10, QLatin1Char('0')));
}

void cSliderDateTime::setDay(int day)
{
    sliderDay->setCurrentValue(QString("%1 日").arg(day, 2, 10, QLatin1Char('0')));
}

void cSliderDateTime::setHour(int hour)
{
    sliderHour->setCurrentValue(QString("%1").arg(hour, 2, 10, QLatin1Char('0')));
}

void cSliderDateTime::setMin(int min)
{
    sliderMin->setCurrentValue(QString("%1").arg(min, 2, 10, QLatin1Char('0')));
}

void cSliderDateTime::setSec(int sec)
{
    sliderSec->setCurrentValue(QString("%1").arg(sec, 2, 10, QLatin1Char('0')));
}

void cSliderDateTime::setDateTime(int year, int month, int day, int hour, int min, int sec)
{
    setYear(year);
    setMonth(month);
    setDay(day);
    setHour(hour);
    setMin(min);
    setSec(sec);
}

2.Slider.cpp


#include "Slider.h"
#include <QDebug>
#pragma execution_character_set("utf-8")
cSlider::cSlider(QWidget *parent) 
	: QWidget(parent)
{
    currentIndex = 0;
    currentValue = "1";

    for (int i = 1; i <= 12; i++) 
	{
        listValue.append(QString::number(i));
    }

	// 初始化一些颜色
    foreground = QColor(140, 140, 140);
	background = Qt::white;
    lineColor = QColor(140, 140, 140);
	textColor = Qt::black;

    horizontal = false;

    percent = 3;
    offset = 0;
    pressed = 0;
    pressedPos = 0;
    currentPos = 0;

    setFont(QFont("Microsoft YaHei", 12));
}

void cSlider::wheelEvent(QWheelEvent *e)
{
    // 滚动的角度,*8就是鼠标滚动的距离
    int degrees = e->delta() / 8;

    // 滚动的步数,*15就是鼠标滚动的角度
    int steps = degrees / 15;

    // 如果是正数代表为左边移动,负数代表为右边移动
    if (e->orientation() == Qt::Vertical) 
	{
        int index = currentIndex - steps;

        if (steps > 0) 
		{
            if (index > 0) 
			{
                setCurrentIndex(index);
            } 
			else 
			{
                setCurrentIndex(0);
            }
        } 
		else 
		{
            if (index < listValue.count() - 1) 
			{
                setCurrentIndex(index);
            } 
			else 
			{
                setCurrentIndex(listValue.count() - 1);
            }
        }
    }
}

void cSlider::mousePressEvent(QMouseEvent *e)
{
    pressed = true;
    int target = e->pos().x();

    if (!horizontal) 
	{
        target = e->pos().y();
    }

    pressedPos = target;
}

void cSlider::mouseMoveEvent(QMouseEvent *e)
{
    int count = listValue.count();

    if (count <= 1) 
	{
        return;
    }

    int pos = e->pos().x();
    int target = this->width();

    if (!horizontal) 
	{
        pos = e->pos().y();
        target = this->height();
    }

    int index = listValue.indexOf(currentValue);

    if (pressed) 
	{
        // 数值到边界时, 阻止继续往对应方向移动
        if ((index == 0 && pos >= pressedPos) || (index == count - 1 && pos <= pressedPos)) 
            return;

        offset = pos - pressedPos;

        // 若移动速度过快时进行限制
        if (offset > target / percent) 
		{
            offset = target / percent;
        } 
		else if (offset < -target / percent) 
		{
            offset = -target / percent;
        }

        static int oldIndex = -1;

        if (oldIndex != index) 
		{
            emit currentIndexChanged(index);
            emit currentValueChanged(listValue.at(index));
            oldIndex = index;
        }

        update();
    }
}

void cSlider::mouseReleaseEvent(QMouseEvent *)
{
    if (pressed) 
	{
        pressed = false;
        // 矫正到居中位置
        checkPosition();
    }
}

void cSlider::paintEvent(QPaintEvent *)
{
    // 绘制准备工作,启用反锯齿
    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

    int count = listValue.count();

    if (count <= 1) 
        return;

    int target = this->width();

    if (!horizontal) 
	{
        target = this->height();
    }

    int index = listValue.indexOf(currentValue);

    // 当右移偏移量大于比例且当前值不是第一个则索引-1
    if (offset >= target / percent && index > 0) 
	{
        pressedPos += target / percent;
        offset -= target / percent;
        index -= 1;
    }

    // 当左移偏移量小于比例且当前值不是末一个则索引+1
    if (offset <= -target / percent && index < count - 1) 
	{
        pressedPos -= target / percent;
        offset += target / percent;
        index += 1;
    }

    currentIndex = index;
    currentValue = listValue.at(index);

    // 绘制背景
    drawBg(&painter);

    // 绘制线条
    drawLine(&painter);

    // 绘制中间值
    painter.setPen(textColor);
    drawText(&painter, index, offset);
    painter.setPen(foreground);

    // 绘制左侧值
    if (index != 0) 
	{
        drawText(&painter, index - 1, offset - target / percent);
    }

    // 绘制右侧值
    if (index != count - 1) 
	{
        drawText(&painter, index + 1, offset + target / percent);
    }
}

void cSlider::drawBg(QPainter *painter)
{
    painter->save();
    painter->setPen(Qt::NoPen);
    painter->setBrush(background);
    painter->drawRect(rect());
    painter->restore();
}

void cSlider::drawLine(QPainter *painter)
{
    // 上下部分偏移量
    int offset = 10;
    int width = this->width();
    int height = this->height();

    painter->save();
    painter->setBrush(Qt::NoBrush);

    QPen pen;
    pen.setWidth(3);
    pen.setColor(lineColor);
    pen.setCapStyle(Qt::RoundCap);
    painter->setPen(pen);

    // 每次同时存在三个元素
    if (horizontal) 
	{
        painter->drawLine(width / 3 * 1, offset, width / 3 * 1, height - offset);
        painter->drawLine(width / 3 * 2, offset, width / 3 * 2, height - offset);
    } 
	else 
	{
        painter->drawLine(offset, height / 3 * 1, width - offset,  height / 3 * 1);
        painter->drawLine(offset, height / 3 * 2, width - offset,  height / 3 * 2);
    }

    painter->restore();
}

void cSlider::drawText(QPainter *painter, int index, int offset)
{
    painter->save();

    int width = this->width();
    int height = this->height();
    QString strValue = listValue.at(index);

    int target = width;

    if (!horizontal) 
	{
        target = height;
    }

    QFont font = painter->font();
    font.setPixelSize((target - qAbs(offset)) / 8);
    painter->setFont(font);

    if (horizontal) 
	{
        int textWidth = painter->fontMetrics().width(strValue);
        int initX = width / 2 + offset - textWidth / 2;
        painter->drawText(QRect(initX, 0, textWidth, height), Qt::AlignCenter, strValue);

        // 计算最后中间值停留的起始坐标,以便鼠标松开时矫正居中
        if (index == currentIndex) 
		{
            currentPos = initX;
        }
    } 
	else 
	{
        int textHeight = painter->fontMetrics().height();
        int initY = height / 2 + offset - textHeight / 2;
        painter->drawText(QRect(0, initY, width, textHeight), Qt::AlignCenter, strValue);

        // 计算最后中间值停留的起始坐标,以便鼠标松开时矫正居中
        if (index == currentIndex) 
		{
            currentPos = initY;
        }
    }

    painter->restore();
}

void cSlider::checkPosition()
{
    int target = this->width();

    if (!horizontal) 
	{
        target = this->height();
    }

    // 左右滑动样式,往左滑动时,offset为负数,当前值所在X轴坐标小于宽度的一半,则将当前值设置为下一个值
    // 左右滑动样式,往右滑动时,offset为正数,当前值所在X轴坐标大于宽度的一半,则将当前值设置为上一个值
    // 上下滑动样式,往上滑动时,offset为负数,当前值所在Y轴坐标小于高度的一半,则将当前值设置为下一个值
    // 上下滑动样式,往下滑动时,offset为正数,当前值所在Y轴坐标大于高度的一半,则将当前值设置为上一个值
    if (offset < 0) 
	{
        if (currentPos < target / 2) 
		{
            offset = 0;
            setCurrentIndex(currentIndex + 1);
        }
    } 
	else 
	{
        if (currentPos > target / 2) 
		{
            offset = 0;
            setCurrentIndex(currentIndex - 1);
        }
    }
}

QStringList cSlider::getListValue() const
{
    return this->listValue;
}

int cSlider::getCurrentIndex() const
{
    return this->currentIndex;
}

QString cSlider::getCurrentValue() const
{
    return this->currentValue;
}

bool cSlider::getHorizontal() const
{
    return this->horizontal;
}

QColor cSlider::getForeground() const
{
    return this->foreground;
}

QColor cSlider::getBackground() const
{
    return this->background;
}

QColor cSlider::getLineColor() const
{
    return this->lineColor;
}

QColor cSlider::getTextColor() const
{
    return this->textColor;
}

QSize cSlider::sizeHint() const
{
    return QSize(50, 150);
}

QSize cSlider::minimumSizeHint() const
{
    return QSize(10, 10);
}

void cSlider::setListValue(const QStringList &listValue)
{
    if (listValue.count() > 0) 
	{
        this->listValue = listValue;
        setCurrentIndex(0);
        setCurrentValue(listValue.at(0));
        update();
    }
}

void cSlider::setCurrentIndex(int currentIndex)
{
    if (currentIndex >= 0) 
	{
        this->currentIndex = currentIndex;
        this->currentValue = listValue.at(currentIndex);
        emit currentIndexChanged(this->currentIndex);
        emit currentValueChanged(this->currentValue);
        update();
    }
}

void cSlider::setCurrentValue(const QString &currentValue)
{
    if (listValue.contains(currentValue)) 
	{
        this->currentValue = currentValue;
        this->currentIndex = listValue.indexOf(currentValue);
        emit currentIndexChanged(this->currentIndex);
        emit currentValueChanged(this->currentValue);
        update();
    }
}

void cSlider::setHorizontal(bool horizontal)
{
    if (this->horizontal != horizontal) 
	{
        this->horizontal = horizontal;
        update();
    }
}

void cSlider::setForeground(const QColor &foreground)
{
    if (this->foreground != foreground) 
	{
        this->foreground = foreground;
        update();
    }
}

void cSlider::setBackground(const QColor &background)
{
    if (this->background != background) 
	{
        this->background = background;
        update();
    }
}

void cSlider::setLineColor(const QColor &lineColor)
{
    if (this->lineColor != lineColor) 
	{
        this->lineColor = lineColor;
        update();
    }
}

void cSlider::setTextColor(const QColor &textColor)
{
    if (this->textColor != textColor) 
	{
        this->textColor = textColor;
        update();
    }
}

四、程序链接

https://download.csdn.net/download/u013083044/87513082?spm=1001.2014.3001.5503

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的大海贼

联系博主,为您提供有价值的资源

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值