一、回顧
本篇文章之前我想先回顧一下前邊我門所講述的兩篇關於自定義日歷的文章,文章的鏈接在后續相關鏈接中都可以看到,第一篇文章中講的是使用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之模擬窗口失去焦點隱藏文章,這篇文章里簡述了怎么讓一個窗口失去焦點隱藏的方法,肯定不完善,需要自己在根據業務去控制。好了,本篇文章到此就結束了。