自定义日历(四)-区间选择控件

目录

  • 一、概述
  • 二、效果展示
  • 三、整体结构
  • 四、分析实现
    • 1、QPickDate
    • 2、QDatePanel
    • 3、QDateWidget、QDateContent
    • 4、 调度绘制
  • 五、相关文章

原文链接:自定义日历(四)-区间选择控件

一、概述

很早很早以前,写过几篇关于日历的文章,不同于Qt原生的控件,这些控件都是博主使用自绘的方式进行完成,因此可定制性更强一些,感兴趣的可以参考自定义日历(一)自定义日历(二)自定义日历(三))

本篇文章还是继续来写我们的日历控件,仍然采用自绘的方式,带来更加炫酷的效果。看本文的标题就应该就能明白,这次实现的是一个可以区间选择的日历控件。

二、效果展示

效果图如下,一个简单的效果展示。

日历控件与Qt原生的QDateEdit一样,是由一个按钮进行触发,弹出如期选择面板。不同的是这个日历选择面板由2个小的日期面板组成,分别是开始和结束日期,规则如下:

  1. 开始日期必须小于结束日期
  2. 顶部有快速返回按钮
  3. 选中的日期段上有高亮背景色
  4. 选中的日期点上有蓝色圆形标识
  5. 点击确定按钮以后日期选择面板关闭

三、整体结构

开始讲解具体内容之前,先来看下整体的结构划分,实现这个日期段选择控件,总共需要以下4个类,下图是工程结构

以下是4个类的说明

  1. QDateContent:单个日历窗口
  2. QDateWidget:包含了年月选择的单个日历窗口
  3. QDatePanel:日期段选择面板
  4. QPickDate:日期选择按钮,用于呼出日期选择面板

其中QPickDate类就是对外使用的类,使用也很简单,可能像下面这样

QPickDate * pickDate = new QPickDate;
pickDate->SetQuickValue(QDatePanel::DAY_ONE);

意思是构造一个日期段选择空间,然后初始时为选择当天。有了一个大致的了解后,下面开始详细的讲解每个类的实现过程

四、分析实现

1、QPickDate

QPickDate类是对外导出类,也是我们使用的时候需要了解的类,他的头文件实现如下

class QPickDate : public QPushButton
{
    Q_OBJECT

public:
    QPickDate(QWidget * parent = nullptr);
    ~QPickDate();

signals:
    void PickSuccess();//选择日期成功时调用

public:
    void SetQuickValue(QDatePanel::QuickPick pick);
    void GetStartDate(unsigned short year, unsigned short month, unsigned short day);
    void GetEndDate(unsigned short year, unsigned short month, unsigned short day);

private slots:
    void OnClicked();

private:
    void InitializeUI();

private:
    QDatePanel::QuickPick m_ePick = QDatePanel::DAY_CUSTOM;
    QDatePanel * m_pPanel = nullptr;
};

接口看起来也比较简单,SetQuickValue接口上一小节使用过,主要是用来初始化日期控件状态。GetStartDate和GetEndDate接口主要就是获取日期段的开始时间和结束时间。其中的实现具体的日期数据是从成员变量QDatePanel中获取。

2、QDatePanel

如下是QDatePanel的头文件声明,由于代码量的问题,其中精简了一部分,QDatePanel这个类就是日期选择面板,垂直方向由三部分组成,分别是快速选择日期选择操作按钮

class QDatePanel : public QFrame
{
    ...
public:
    enum QuickPick
    {
        DAY_ONE,//今天
        DAY_WEEK,//近一周
        DAY_MONTH,//近一月
        DAY_YEAR,//近一年
        DAY_CUSTOM,//自定义
    };

signals:
    void PickSuccess(QuickPick, const QString &);
    ...
public:
    QString GetQuickName(QuickPick pick);
    void SetQuickValue(QuickPick pick);
    void GetStartDate(unsigned short year, unsigned short month, unsigned short day);
    void GetEndDate(unsigned short year, unsigned short month, unsigned short day);

private:
    ...
};

快速选择:可以快速选择一日、一周、一月和一年时间段

日期选择:分为左右结构,左侧时其实日期,右侧时结束日期

操作按钮:选择好日期后,可以通过点击确定或者取消来关闭面板

3、QDateWidget、QDateContent

上边说了QDatePanel是日期选择面板,其中日期选择部分就是由左右两部分组成,其实分别就是一个QDateWidget,如下图所示

QDateContent类是主要的日期计算和绘制类,被QDateWidget包裹了一层,并添加上了年和月的操作按钮。

下面主要介绍下QDateContent类的实现,首先来看下声明文件

由于篇幅原因还是注释了一大部分代码

class QDateContent : public QWidget
{
    ...
signals:
    void DateClicked(unsigned short year, unsigned short month, unsigned short day);

public:
    void SetSelectDate(unsigned short year, unsigned short month, unsigned short day);
    void GetSelectDate(unsigned short & year, unsigned short & month, unsigned short & day);
    void SetDate(unsigned short year, unsigned short month, unsigned short day);
    void GetDate(unsigned short & year, unsigned short & month, unsigned short & day);

    //设置关联日期
    void SetRelationDate(QDateContent * content);

public slots :
    void PreviousMonth();//上一月
    void NextMonth();//下一月
    void PreviousYear();//上一年
    void NextYear();//下一年
    ...
private:
    struct QDateContentPrivate;
    QDateContentPrivate * d_ptr;
};

切换月份和年份的接口代码中已经包含了注释,其他Set和Get接口看名称基本也能明白,QDateContent类的代码量还是比较大的,下面我们主要来看绘制部分,其中有3个比较重要的点绘制头绘制数字绘制选中

绘制头

该绘制模块主要是绘制表头,也就是周日、周一这样的字段,绘制的位置时通过私有函数GetColumnLeft和GetColumnRight获取。

void QDateContent::DrawWeek(QPainter & painter)
{
    //  QString aText[7] = { STR("周日"), STR("周一"), STR("周二"), STR("周三"), STR("周四"), STR("周五"), STR("周六") };
    QString aText[7] = { STR("日"), STR("一"), STR("二"), STR("三"), STR("四"), STR("五"), STR("六") };

    painter.save();
    painter.setFont(d_ptr->weekFont);
    QFontMetrics fm(d_ptr->weekFont);
    int height = fm.height();

    //painter.fillRect(d_ptr->GetColumnLeft(0), d_ptr->topBorder, d_ptr->GetColumnRight(6) - 3, d_ptr->topBorder + height, QColor(20, 22, 23));

    for (int i = 0; i < 7; ++i)
    {
        int left = d_ptr->GetColumnLeft(i);
        int right = d_ptr->GetColumnRight(i);
        QRect rect(left, d_ptr->topBorder, right - left, height);
        painter.setPen(QColor("#838D9E"));

        painter.drawText(rect, Qt::AlignCenter, aText[i]);
    }

    painter.restore();
}

绘制数字

绘制数字和绘制标题原理基本一致,位置信息都是使用GetColumnLeft和GetColumnRight获取,不同的是,绘制数字时还需要绘制额外的选中状态、悬浮状态

由于是绘制函数,因此有一些数据计算是通过整理好的,比如说需要绘制的数字当前行数当月第一天周几等等

由于绘制篇幅原因,还是只保留主要逻辑

void QDateContent::DrawDay(QPainter & painter)
{
    painter.save();

    for (int column = 0; column < d_ptr->m_column_count; ++column)
    {
        int column_left = d_ptr->GetColumnLeft(column);
        int column_right = d_ptr->GetColumnRight(column);
        for (int row = 0; row < d_ptr->m_row_count; ++row)
        {
            int index = row * d_ptr->m_column_count + column;
            QRect & rcTmp = d_ptr->m_aRect[index];
            tDayFlag & flag = d_ptr->m_aDayFlag[index];
            flag.m_chEnable = (column != 0 && column != 6) ? true : false;

            bool selected = d_ptr->MatchRealDate(flag);
            if (selected)
            {
                QPainterPath path;
                path.addEllipse(QRectF(rcTmp).center(), 12, 12);
                painter.fillPath(path, QColor("#218CF2"));
            }

            painter.drawText(rcTmp, Qt::AlignCenter, QString::number(flag.m_chFlagD));

            painter.restore();
        }
    }

    painter.restore();
}

绘制选中

以下代码是绘制选中时的水平背景色,绘制代码比较简单,复杂的地方主要有2个:

  1. 计算当前日期是否在选择日期段当中,返回status
  2. 修正第一步返回的status

由于绘制篇幅原因,还是只保留主要逻辑

以下代码是精简过后的绘制选中背景色,看起来还是很长,不过大体上是分下面这几步

  1. 根据当前年月日返回status,表示当前day是否在选择的开始和选择的结束日期之间
  2. 根据所处列和day修改第一步返回的status
  3. 根据status调整要绘制的形状
  4. 绘制背景色

下面是主要的绘制流程,代码就不细讲了,大家可以自行阅读

void QDateContent::DrawSelectedBackground(QPainter & painter)
{
    painter.save();

    for (int column = 0; column < d_ptr->m_column_count; ++column)
    {
        for (int row = 0; row < d_ptr->m_row_count; ++row)
        {
            tDayFlag & flag = d_ptr->m_aDayFlag[index];

            if (little)
            {
                status = d_ptr->GetSelectedStatus(d_ptr->m_wYear, d_ptr->m_wMonth, d_ptr->m_wDay, d_ptr->m_sYear
                    , d_ptr->m_sMonth, flag.m_chFlagD, year, month, day);
            }
            else
            {
                status = d_ptr->GetSelectedStatus(year, month, day, d_ptr->m_sYear
                    , d_ptr->m_sMonth, flag.m_chFlagD, d_ptr->m_wYear, d_ptr->m_wMonth, d_ptr->m_wDay);
            }

            //修正数据
            CorrentStatus(status, column, flag.m_chFlagD);

            if (status == 0)
            {
                continue;
            }
            QRect rect = rcTmp.adjusted(0, 3, 0, -3);
            if (rect.height() < 15)
            {
                rect.setHeight(15);
                rect.moveCenter(rcTmp.center());
            }
            if (status == 2)
            {
                rect.adjust(-4, 0, 4, 0);
                painter.drawRect(rect);
            }
            else if (status == 1)//只有左半边
            {
                rect.adjust(-4, 0, -4, 0);
                painter.drawRoundedRect(rect, rect.height() / 2, rect.height() / 2);
                painter.drawRect(rect.adjusted(0, 0, -rect.height() / 2, 0));
            }
            else if (status == 3)
            {
                rect.adjust(4, 0, 4, 0);
                painter.drawRoundedRect(rect, rect.height() / 2, rect.height() / 2);
                painter.drawRect(rect.adjusted(rect.height() / 2, 0, 0, 0));
            }
            else if (status == 5)
            {
                rect.adjust(4, 0, -4, 0);
                painter.drawRoundedRect(rect, rect.height() / 2, rect.height() / 2);
            }
        }
    }

    painter.restore();
}

4、 调度绘制

最后就是绘制的顺序,这里一定要注意,一定得线绘制背景色,如果是最后绘制的话会挡住当前绘制的文字和选中状态。

void QDateContent::paintEvent(QPaintEvent * event)
{
    QDate date = QDate::currentDate();
    d_ptr->m_tYear = date.year();
    d_ptr->m_tMonth = date.month();
    d_ptr->m_tDay = date.day();

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

    //painter.drawRect(rect());

    d_ptr->ResetDayFlag();

    DrawSelectedBackground(painter);

    DrawWeek(painter);

    DrawDay(painter);
}

五、相关文章

自定义日历(一)

自定义日历(二)

自定义日历(三))

Qt之模拟窗口失去焦点隐藏



值得一看的优秀文章:

  1. 财联社-产品展示
  2. 广联达-产品展示
  3. Qt定制控件列表
  4. 牛逼哄哄的Qt库

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!



很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


自定义日历(四)-区间选择控件

原文地址:https://www.cnblogs.com/swarmbees/p/11783045.html

时间: 2024-10-04 03:53:10

自定义日历(四)-区间选择控件的相关文章

用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.android实现自绘日期选择控件 目的: 通过一个相对复杂的自定义自绘控件来分享: 1.ios以及android自定义自绘控件的开发流程 2.objc与c/c++混合编程 3.android ndk的环境配置,android studio ndk的编译模式,swig在android ndk开发中的作

Android自定义View(RollWeekView-炫酷的星期日期选择控件)

转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53420889 本文出自:[openXu的博客] 目录: 1分析 2定义控件布局 3定义CustomWeekView 4重写onMeasure 5点击后执行动画 7重置预备控件 源码下载 ??最近收到一个自定义控件的需求,需要做一个日期选择控件,实现图如下: ???? ??一次展示一个星期的5天,中间放大的为当前选中的:如果点击了其中一个日期,比如星期五,那么整体向左滑动,并将星期五慢慢放大

记录 Ext 日历月份选择控件bug解决过程结果

目录 背景 代码 背景 项目使用 Ext.NET 2.2.0.40838 , 对应 Ext JS4.2 版本. 结果 2017/3/31 号的时候偶然间点日历选择控件选择2月,10月等月份突然就跳到3月份,9月份之类. 就是无法选择, 选择谷歌以后发现有同样的问题, 然后各种尝试, 重写了默认属性,如下代码后解决. 收获就是调用平台有时候要知道原因才能找到未知原因并修复. 宝贵的额是Ext框架问题解决思路吧. 现记录. 改动的部分就是 dt.setDate(1); 这一句, 设置为当前月份第一天

双日历日期选择控件

近期,需要在项目里使用日历,经过多方选择,最后决定使用 daterangepicker  (http://www.daterangepicker.com),代码下载地址 https://github.com/dangrossman/bootstrap-daterangepicker 但是,该控件是一个日期范围选择控件,使用singleDatePicker 可以变成单日期选择控件,但是只显示一个日期. 看了一下源代码,主要是在 daterangepicker.js 的有一段代码,注释掉即可:如下

android中选择控件与选择界面自然过度效果的实现--一种新的交互设计

转载请标明出处: http://blog.csdn.net/jianghejie123/article/details/40648931 在安卓中经常遇到须要选择一个东西的功能,比方选择日期.选择文件,选择地点等,通常我们的做法是使用选择对话框.比方选择日期用datepicker,有时候也使用activity,可是这两种方式都有一个缺点,就是用户非常明显的感觉到了界面之间的切换. 有时候.以下这样的选择数据的交互方式应该会更好些: 事实上上面的app效果来自与CapitaineTrain应用,好

数据选择控件

UIDatePicker——方便用户选择日期 作用:方便用户的日期选择,并保证日期格式正确 1. 初始化 // 初始化日期控件,不用设置宽高,因为它的宽高是固定的 UIDatePicker *datePicker = [[UIDatePicker alloc]init]; 2. 常用设置 // 设置日期控件区域为简体中文 [datePicker setLocale:[[NSLocale alloc]initWithLocaleIdentifier:@"zh_Hans_CN"]]; //

asp.net mvc中使用jquery H5省市县三级地区选择控件

地区选择是项目开发中常用的操作,本文讲的控件是在手机端使用的选择控件,不仅可以用于实现地区选择,只要是3个级别的选择都可以实现,比如专业选择.行业选择.职位选择等.效果如下图所示: 附:本实例asp.net mvc中使用jquery H5省市县三级地区选择控件及中国省市县标准地区库下载地址 咨询QQ:806693619 一.实现原理 一般常用输入控件是input,当点击input的时候执行jquery获取焦点事情,然后弹出本地区选择插件,选择完地区后点击确定将选择的值返回给input完成地区输入

[Swift通天遁地]九、拔剑吧-(3)创建多种自定义Segment分段样式的控件

本文将演示创建多种自定义Segment分段样式的控件. 首先确保已经安装了所需的第三方类库.双击查看安装配置文件[Podfile] 1 platform :ios, '12.0' 2 use_frameworks! 3 4 target 'DemoApp' do 5 source 'https://github.com/CocoaPods/Specs.git' 6 pod 'PagingMenuController' 7 end 根据配置文件中的相关设置,安装第三方类库. 安装完成之后,双击打开

js组件开发-移动端地区选择控件mobile-select-area

移动端地区选择控件mobile-select-area 由于之前的[js开源组件开发]js手机联动选择地区仿ios 开源git 很受欢迎,于是我又对其进行了一些优化,包括可选的范围变大了,添加了默认空首地址的功能,也添加了更多api参数,首先我们先来看下这次的效果图. 它的github地址请点击https://github.com/tianxiangbing/mobile-select-area 它的demo演示请点击 http://www.lovewebgames.com/jsmodule/m