QT-自定义滑动式日期选择
- 前言
- 一、效果演示
- 二、注意说明
- 二、关键程序
- 1.SliderDateTime.cpp
- 2.Slider.cpp
- 四、程序链接
前言
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 ¤tValue)
{
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