Building a Custom Calendar Widget in Qt: Date Selection and Dropdown Integration

1. Background

Before diving into this article, let me briefly review the previous two articles on custom calendar implementation. The link are provided in the related links section at the end. The first article demonstrated using QLabel contrlos to construct a calendar—easy to understand but with suboptimal performance. The second approach rendered the date grid and text by calculating positions in memory and drawing everything on a single widget, offering much better performance. This article continues using the memory-based calculation approach from the second article.

2. Preview

Figure 1 shows a calendar control that can track the currently selected date. The screenshot is without any QSS styling, so it appears rather plain.

图1 自定义日历

3. Implementation Details

For the best understanding, I recommend downloading the demo first and following along with the code examples in this section.

The implementation consists of three main classes:

  • DrawDateTime: The date content widget responsible for rendering the current month's dates along with partial dates from the previous and next months
  • CalendarWidget: The dropdown preview window containing the date header and date content area
  • DropDataControl: The main calendar control class exposed to external usage, featuring the current date display and a button to trigger the dropdown

3.1 DrawDateTime Class

For detailed information about the date rendering widget, please refer to the second article. Here, I'll only highlight the changes made.

a) Added data members for tracking the selected day and updated the MatchRealDate function:

 1 // Selected date
 2 unsigned short m_wYear;
 3 unsigned short m_wMonth;
 4 unsigned short m_wDay;
 5 
 6 // Calendar position date
 7 unsigned short m_sYear;
 8 unsigned short m_sMonth;
 9 unsigned short m_sDay;
10 
11 bool MatchRealDate(tDayFlag df)
12 {
13     // Only consider it as the selected day if the current month matches the selected month
14     if (df.m_chFlagD == m_wDay && m_sMonth == m_wMonth)
15     {
16         return true;
17     }
18     return false;
19 }

b) Updated the selected day when the mouse is pressed:

 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 needsUpdate = (d_ptr->m_wDay != flag.m_chFlagD || month != d_ptr->m_wMonth || year != d_ptr->m_wYear);
22         if (needsUpdate)
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); // Emit signal to update UI
31     }
32 }

3.2 CalendarWidget Class

The dropdown preview window contains both the date header and the date drawing interface. When the date drawing interface emits the DataClicked signal, the header is updated with the current year and month, and DropDataControl is notified to modify the date. When the previous or next month buttons are clicked in the header, the header information is updated and the date drawing interface is notified to refresh the data. The key implementation is shown below:

 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); // Notify DropDataControl to update date
 4     d_ptr->m_pMonth->setText(dataDescribe(year, month)); // Update header with current year and month
 5 //    setHidden(true);
 6 });
 7 
 8 connect(d_ptr->m_pPrevisou, &QPushButton::clicked, this, [this]{
 9     d_ptr->m_pDataView->PreviousMonth(); // Notify date drawing interface to update data
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)); // Update header with current year and month
12 });
13 connect(d_ptr->m_pNext, &QPushButton::clicked, this, [this]{
14     d_ptr->m_pDataView->NextMonth(); // Notify date drawing interface to update data
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)); // Update header with current year and month
17 });

3.3 DropDataControl Class

The date control is the externally accessible class that can be used directly by other components. Regarding the layout, I won't go into detail here—just the connection code:

 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)); // Update QLineEdit text content
 4 });
 5 
 6 d_ptr->m_pDropWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::Popup); // Popup menu style
 7 
 8 void DropDataControl::DropButtonClicked()
 9 {
10     d_ptr->m_pDropWidget->move(mapToGlobal(rect().bottomLeft()));
11     d_ptr->m_pDropWidget->show();
12 }

Line 6 in the code above sets the popup window flags. Windows with the Qt::Popup attribute block the main event loop, making them modal dialogs. Refer to Figure 2 for documentation on this attribute. The highlighted text indicates that this property creates a modal-style window.

图2 Qt::Popup说明

4. Alternative Implementation

If you don't want the window to be modal, you can omit the Qt::Popup flag. For implementing custom focus-out behavior to hide the dropdown, refer to the article "Simulating Window Focus-Out Hide" linked below. That approach may need adjustments based on your specific use case.

5. Demo Download

Qt Custom Calendar Widget (Part 3)

6. Related Links

Custom Calendar (Part 1)

Custom Calendar (Part 2)

Simulating Window Focus-Out Hide

Tags: Qt C++ custom-widgets Calendar gui

Posted on Tue, 14 Jul 2026 16:00:52 +0000 by sarahk