自定义日历(三)
一、回顾
本篇文章之前我想先回顾一下前边我门所讲述的两篇关于自定义日历的文章,文章的链接在后续相关链接中都可以看到,第一篇文章中讲的是使用QLabel控件拼凑的日历,好理解,但是性能较第二种方式差,第二种日历的位置区域和日期文字都是在内存中计算,然后绘制在一个窗口上,性能上肯定没问题。本篇文章依然沿用和自定义日历(二)一样使用内容计算位置和日期文字。
二、效果预览
如图1所示,可以记录住当前所选择的日期的日历控件,没有qss美化,比较丑
图1 自定义日历
三、实现原理
看这一节之前,如果有兴趣的同学可以先下载demo,然后对照着示例代码看这一小节效果更好。
首先我先介绍几个类:
- DrawDateTime:日期内容窗口,主要负责绘制当月日期和左右两个月部分日期
- CalendarWidget:下拉框预览窗口,包含了日期头部和日期内容两部分
- DropDataControl:日历控件类,对外导出类,可以直接被外部使用。包含当前日期和按钮,点击按钮可以弹出下拉框
1、DrawDateTime
关于绘制日期内容窗口请参考自定义日历(二)文章,在此我只简述一下内容有所改动的地方。
a、添加记录选中天的数据项,并修改MatchRealDate函数,具体代码如下:
1 //选中天 2 unsigned short m_wYear; 3 unsigned short m_wMonth; 4 unsigned short m_wDay; 5 6 //计算日历位置 7 unsigned short m_sYear; 8 unsigned short m_sMonth; 9 unsigned short m_sDay; 10 11 bool MatchRealDate(tDayFlag df) 12 { 13 if (df.m_chFlagD == m_wDay && m_sMonth == m_wMonth)//如果当前月份等于选中月份才认为是当前选中天 14 { 15 return true; 16 } 17 return false; 18 }
b、鼠标按下时,更新当前选中天
1 void DrawDateTime::mousePressEvent(QMouseEvent * event) 2 { 3 if (event->button() == Qt::LeftButton) 4 { 5 int cur = GetIndex(event->pos()); 6 if (cur == -1) 7 { 8 return; 9 } 10 tDayFlag & flag = d_ptr->m_aDayFlag[cur]; 11 12 unsigned short year = d_ptr->m_sYear, month = d_ptr->m_sMonth; 13 if (flag.m_chFlagM == -1) 14 { 15 d_ptr->GetPreviousMonth(year, month); 16 } 17 else if (flag.m_chFlagM == 1) 18 { 19 d_ptr->GetNextMonth(year, month); 20 } 21 bool b = (d_ptr->m_wDay != flag.m_chFlagD || month != d_ptr->m_wMonth || year != d_ptr->m_wYear); 22 if (b) 23 { 24 d_ptr->m_wDay = flag.m_chFlagD; 25 d_ptr->m_wMonth = month; 26 d_ptr->m_wYear = year; 27 update(); 28 } 29 30 DataClicked(year, month, d_ptr->m_wDay);//发出信号,用于更新ui界面 31 } 32 }
2、CalendarWidget
下拉框预览窗口,包含日期窗口头,和日期绘制界面。当日期绘制界面发出DataClicked信号时,修改头部当前年、月,并通知DropDataControl窗口修改日期。按下表头的上一月和下一月时,修改表头信息,并通知日期绘制界面修改界面数据,关键代码如下:
1 d_ptr->m_pDataView = new DrawDateTime; 2 connect(d_ptr->m_pDataView, &DrawDateTime::DataClicked, this, [this](unsigned short year, unsigned short month, unsigned short day){ 3 emit DataClicked(year, month, day);//通知DropDataControl窗口修改日期 4 d_ptr->m_pMonth->setText(dataDescribe(year, month));//修改头部当前年、月 5 // setHidden(true); 6 }); 7 8 connect(d_ptr->m_pPrevisou, &QPushButton::clicked, this, [this]{ 9 d_ptr->m_pDataView->PreviousMonth();//并通知日期绘制界面修改界面数据 10 d_ptr->m_pDataView->GetDate(d_ptr->m_wYear, d_ptr->m_wMonth, d_ptr->m_wDay); 11 d_ptr->m_pMonth->setText(dataDescribe(d_ptr->m_wYear, d_ptr->m_wMonth));//修改头部当前年、月 12 }); 13 connect(d_ptr->m_pNext, &QPushButton::clicked, this, [this]{ 14 d_ptr->m_pDataView->NextMonth();//并通知日期绘制界面修改界面数据 15 d_ptr->m_pDataView->GetDate(d_ptr->m_wYear, d_ptr->m_wMonth, d_ptr->m_wDay); 16 d_ptr->m_pMonth->setText(dataDescribe(d_ptr->m_wYear, d_ptr->m_wMonth));//修改头部当前年、月 17 });
3、DropDataControl
日期空间,对外导出类,可以被外部直接使用。关于布局,我就不细说了,贴下关于connect,代码如下:
1 connect(d_ptr->m_pDropButton, &QPushButton::clicked, this, &DropDataControl::DropButtonClicked); 2 connect(d_ptr->m_pDropWidget, &CalendarWidget::DataClicked, this, [this](unsigned short year, unsigned short month, unsigned short day){ 3 d_ptr->m_pText->setText(dataDescribe(year, month, day));//修改QLineEdit文本数据 4 }); 5 6 d_ptr->m_pDropWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);//弹出式菜单 7 8 void DropDataControl::DropButtonClicked() 9 { 10 d_ptr->m_pDropWidget->move(mapToGlobal(rect().bottomLeft())); 11 d_ptr->m_pDropWidget->show(); 12 }
上述代码第6行说明了弹出式窗口是具有Qt::Popup属性,呵呵,具有这个属性的窗口都会阻塞主事件循环,也就这个窗口是一个模态框,具体请看图2关于这个属性的说明,红色矩形框中的文字意思就是:这是模态
图2 Qt::Popup说明
如果不想要这个窗口是模态的,那么就不能设置Qt::Popup属性,具体可以参见Qt之模拟窗口失去焦点隐藏文章,这篇文章里简述了怎么让一个窗口失去焦点隐藏的方法,肯定不完善,需要自己在根据业务去控制。好了,本篇文章到此就结束了。