原文连接:自定义日历(四)-区间选择控件函数
很早很早之前,写过几篇关于日历的文章,不一样于Qt原生的控件,这些控件都是博主使用自绘的方式进行完成,所以可定制性更强一些,感兴趣的能够参考自定义日历(一)、自定义日历(二)和自定义日历(三))。ui
本篇文章仍是继续来写咱们的日历控件,仍然采用自绘的方式,带来更加炫酷的效果。看本文的标题就应该就能明白,此次实现的是一个能够区间选择的日历控件。this
效果图以下,一个简单的效果展现。spa
日历控件与Qt原生的QDateEdit同样,是由一个按钮进行触发,弹出如期选择面板。不一样的是这个日历选择面板由2个小的日期面板组成,分别是开始和结束日期,规则以下:.net
开始讲解具体内容以前,先来看下总体的结构划分,实现这个日期段选择控件,总共须要如下4个类,下图是工程结构rest
如下是4个类的说明code
其中QPickDate类就是对外使用的类,使用也很简单,可能像下面这样htm
QPickDate * pickDate = new QPickDate; pickDate->SetQuickValue(QDatePanel::DAY_ONE);
意思是构造一个日期段选择空间,而后初始时为选择当天。有了一个大体的了解后,下面开始详细的讲解每一个类的实现过程blog
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中获取。
以下是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: ... };
快速选择:能够快速选择一日、一周、一月和一年时间段
日期选择:分为左右结构,左侧时其实日期,右侧时结束日期
操做按钮:选择好日期后,能够经过点击肯定或者取消来关闭面板
上边说了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个:
因为绘制篇幅缘由,仍是只保留主要逻辑
如下代码是精简事后的绘制选中背景色,看起来仍是很长,不过大致上是分下面这几步
下面是主要的绘制流程,代码就不细讲了,你们能够自行阅读
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(); }
最后就是绘制的顺序,这里必定要注意,必定得线绘制背景色,若是是最后绘制的话会挡住当前绘制的文字和选中状态。
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); }
值得一看的优秀文章:
![]() |
![]() |
很重要--转载声明