日历小部件示例
日历小部件示例展示了QCalendarWidget 的使用方法。

QCalendarWidget 该示例每次显示一个日历月,并允许用户选择日期。日历由四个组件构成:一个允许用户切换显示月份的导航栏、一个每个单元格代表该月某一天的网格,以及两个分别显示星期名称和周数的标题栏。
日历小部件示例显示了一个QCalendarWidget ,并允许用户使用QComboBox、QCheckBox和QDateEdit来配置其外观和行为。此外,用户还可以自定义单个日期和标题的格式。
QCalendarWidget 的属性总结如下表所示。
| 属性 | 描述 |
|---|---|
| selectedDate | 当前选定的日期。 |
| minimumDate | 可选择的最早日期。 |
| maximumDate | 可选的最新日期。 |
| firstDayOfWeek | 显示为一周首日的日期(通常为周日或周一)。 |
| gridVisible | 是否显示网格。 |
| selectionMode | 用户是否可以选择日期。 |
| horizontalHeaderFormat | 横向标题中星期名称的格式(例如,“M”、“Mon”或“Monday”)。 |
| verticalHeaderFormat | 竖向标题的格式。 |
| navigationBarVisible | 是否显示日历控件顶部的导航栏。 |
该示例包含一个类Window ,该类负责创建并布局QCalendarWidget 以及其他允许用户配置QCalendarWidget 的小部件。
窗口类定义
以下是Window 类的定义:
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = nullptr);
private slots:
void localeChanged(int index);
void firstDayChanged(int index);
void selectionModeChanged(int index);
void horizontalHeaderChanged(int index);
void verticalHeaderChanged(int index);
void selectedDateChanged();
void minimumDateChanged(QDate date);
void maximumDateChanged(QDate date);
void weekdayFormatChanged();
void weekendFormatChanged();
void reformatHeaders();
void reformatCalendarPage();
private:
void createPreviewGroupBox();
void createGeneralOptionsGroupBox();
void createDatesGroupBox();
void createTextFormatsGroupBox();
QComboBox *createColorComboBox();
QGroupBox *previewGroupBox;
QGridLayout *previewLayout;
QCalendarWidget *calendar;
QGroupBox *generalOptionsGroupBox;
QLabel *localeLabel;
QLabel *firstDayLabel;
...
QCheckBox *mayFirstCheckBox;
};与代表自包含窗口的类通常的情况一样,大部分 API 都是私有的。我们将在实现过程中遇到这些私有成员时逐一进行说明。
Window 类的实现
现在让我们回顾该类的实现,首先从构造函数开始:
Window::Window(QWidget *parent)
: QWidget(parent)
{
createPreviewGroupBox();
createGeneralOptionsGroupBox();
createDatesGroupBox();
createTextFormatsGroupBox();
QGridLayout *layout = new QGridLayout;
layout->addWidget(previewGroupBox, 0, 0);
layout->addWidget(generalOptionsGroupBox, 0, 1);
layout->addWidget(datesGroupBox, 1, 0);
layout->addWidget(textFormatsGroupBox, 1, 1);
layout->setSizeConstraint(QLayout::SetFixedSize);
setLayout(layout);
previewLayout->setRowMinimumHeight(0, calendar->sizeHint().height());
previewLayout->setColumnMinimumWidth(0, calendar->sizeHint().width());
setWindowTitle(tr("Calendar Widget"));
}首先,我们通过四个私有函数create...GroupBox() (如下所述)创建四个QGroupBox实例及其子控件(包括QCalendarWidget )。然后,我们将这些组框排列在一个QGridLayout 中。
我们将网格布局的调整大小策略设置为QLayout::SetFixedSize ,以防止用户调整窗口大小。在此模式下,QGridLayout 会根据其内容控件的大小提示自动设置窗口大小。
为了确保每次更改QCalendarWidget 的属性(例如隐藏导航栏、垂直标题栏或网格)时,窗口不会自动调整大小,我们将第0行的最小高度和第0列的最小宽度设置为QCalendarWidget 的初始大小。
接下来我们来看createPreviewGroupBox() 函数:
void Window::createPreviewGroupBox()
{
previewGroupBox = new QGroupBox(tr("Preview"));
calendar = new QCalendarWidget;
calendar->setMinimumDate(QDate(1900, 1, 1));
calendar->setMaximumDate(QDate(3000, 1, 1));
calendar->setGridVisible(true);
connect(calendar, &QCalendarWidget::currentPageChanged,
this, &Window::reformatCalendarPage);
previewLayout = new QGridLayout;
previewLayout->addWidget(calendar, 0, 0, Qt::AlignCenter);
previewGroupBox->setLayout(previewLayout);
}Preview 组框中仅包含一个控件:QCalendarWidget 。我们对其进行初始化,并将它的currentPageChanged() 信号连接到我们的reformatCalendarPage() 槽,以确保每个新页面都采用用户指定的格式。
createGeneralOptionsGroupBox() 函数篇幅较长,其中有多个控件的设置方式类似。这里我们将重点探讨其部分实现内容,其余部分则不予赘述:
void Window::createGeneralOptionsGroupBox()
{
generalOptionsGroupBox = new QGroupBox(tr("General Options"));
localeCombo = new QComboBox;
int curLocaleIndex = -1;
int index = 0;
for (int _lang = QLocale::C; _lang <= QLocale::LastLanguage; ++_lang) {
QLocale::Language lang = static_cast<QLocale::Language>(_lang);
const auto locales =
QLocale::matchingLocales(lang, QLocale::AnyScript, QLocale::AnyTerritory);
for (auto loc : locales) {
QString label = QLocale::languageToString(lang);
auto territory = loc.territory();
label += QLatin1Char('/');
label += QLocale::territoryToString(territory);
if (locale().language() == lang && locale().territory() == territory)
curLocaleIndex = index;
localeCombo->addItem(label, loc);
++index;
}
}
if (curLocaleIndex != -1)
localeCombo->setCurrentIndex(curLocaleIndex);
localeLabel = new QLabel(tr("&Locale"));
localeLabel->setBuddy(localeCombo);
firstDayCombo = new QComboBox;
firstDayCombo->addItem(tr("Sunday"), Qt::Sunday);
firstDayCombo->addItem(tr("Monday"), Qt::Monday);
firstDayCombo->addItem(tr("Tuesday"), Qt::Tuesday);
firstDayCombo->addItem(tr("Wednesday"), Qt::Wednesday);
firstDayCombo->addItem(tr("Thursday"), Qt::Thursday);
firstDayCombo->addItem(tr("Friday"), Qt::Friday);
firstDayCombo->addItem(tr("Saturday"), Qt::Saturday);
firstDayLabel = new QLabel(tr("Wee&k starts on:"));
firstDayLabel->setBuddy(firstDayCombo);
...首先从Week starts on 下拉列表的配置开始。该下拉列表用于控制应将哪一天显示为一周的第一天。
QComboBox 类允许我们将用户数据作为QVariant 附加到每个项目上。这些数据稍后可通过QComboBox 的itemData()函数检索。QVariant 虽不直接支持Qt::DayOfWeek 数据类型,但支持int ,而C++会自动将任何枚举值转换为int 。
...
connect(localeCombo, &QComboBox::currentIndexChanged,
this, &Window::localeChanged);
connect(firstDayCombo, &QComboBox::currentIndexChanged,
this, &Window::firstDayChanged);
connect(selectionModeCombo, &QComboBox::currentIndexChanged,
this, &Window::selectionModeChanged);
connect(gridCheckBox, &QCheckBox::toggled,
calendar, &QCalendarWidget::setGridVisible);
connect(navigationCheckBox, &QCheckBox::toggled,
calendar, &QCalendarWidget::setNavigationBarVisible);
connect(horizontalHeaderCombo, &QComboBox::currentIndexChanged,
this, &Window::horizontalHeaderChanged);
connect(verticalHeaderCombo, &QComboBox::currentIndexChanged,
this, &Window::verticalHeaderChanged);
...创建完控件后,我们连接信号和槽。我们将下拉列表框连接到Window 的私有槽,或者连接到QComboBox 提供的公共槽。
...
firstDayChanged(firstDayCombo->currentIndex());
selectionModeChanged(selectionModeCombo->currentIndex());
horizontalHeaderChanged(horizontalHeaderCombo->currentIndex());
verticalHeaderChanged(verticalHeaderCombo->currentIndex());
}在函数结尾处,我们调用用于更新日历的槽函数,以确保在启动时QCalendarWidget 能与其它控件保持同步。
现在让我们来看看 `createDatesGroupBox() ` 的私有函数:
void Window::createDatesGroupBox()
{
datesGroupBox = new QGroupBox(tr("Dates"));
minimumDateEdit = new QDateEdit;
minimumDateEdit->setDisplayFormat("MMM d yyyy");
minimumDateEdit->setDateRange(calendar->minimumDate(),
calendar->maximumDate());
minimumDateEdit->setDate(calendar->minimumDate());
minimumDateLabel = new QLabel(tr("&Minimum Date:"));
minimumDateLabel->setBuddy(minimumDateEdit);
currentDateEdit = new QDateEdit;
currentDateEdit->setDisplayFormat("MMM d yyyy");
currentDateEdit->setDate(calendar->selectedDate());
currentDateEdit->setDateRange(calendar->minimumDate(),
calendar->maximumDate());
currentDateLabel = new QLabel(tr("&Current Date:"));
currentDateLabel->setBuddy(currentDateEdit);
maximumDateEdit = new QDateEdit;
maximumDateEdit->setDisplayFormat("MMM d yyyy");
maximumDateEdit->setDateRange(calendar->minimumDate(),
calendar->maximumDate());
maximumDateEdit->setDate(calendar->maximumDate());
maximumDateLabel = new QLabel(tr("Ma&ximum Date:"));
maximumDateLabel->setBuddy(maximumDateEdit);在此函数中,我们创建了Minimum Date 、Maximum Date 和Current Date 编辑控件,它们分别控制日历的起始日期、截止日期和选中日期。日历的起始和截止日期已在createPrivewGroupBox() 中设定;因此,我们可以将这些控件的默认值设置为日历中的相应值。
connect(currentDateEdit, &QDateEdit::dateChanged,
calendar, &QCalendarWidget::setSelectedDate);
connect(calendar, &QCalendarWidget::selectionChanged,
this, &Window::selectedDateChanged);
connect(minimumDateEdit, &QDateEdit::dateChanged,
this, &Window::minimumDateChanged);
connect(maximumDateEdit, &QDateEdit::dateChanged,
this, &Window::maximumDateChanged);
...
}我们将currentDateEdit 的dateChanged()信号直接连接到日历的setSelectedDate()槽。当日历的选定日期发生变化时(无论是由于用户操作还是程序触发),我们的selectedDateChanged() 槽会更新Current Date 编辑器。此外,当用户更改Minimum Date 和Maximum Date 编辑器时,我们也需要做出相应响应。
以下是createTextFormatsGroup() 函数:
void Window::createTextFormatsGroupBox()
{
textFormatsGroupBox = new QGroupBox(tr("Text Formats"));
weekdayColorCombo = createColorComboBox();
weekdayColorCombo->setCurrentIndex(
weekdayColorCombo->findText(tr("Black")));
weekdayColorLabel = new QLabel(tr("&Weekday color:"));
weekdayColorLabel->setBuddy(weekdayColorCombo);
weekendColorCombo = createColorComboBox();
weekendColorCombo->setCurrentIndex(
weekendColorCombo->findText(tr("Red")));
weekendColorLabel = new QLabel(tr("Week&end color:"));
weekendColorLabel->setBuddy(weekendColorCombo);我们使用createColorCombo() 设置了Weekday Color 和Weekend Color 下拉列表,该方法会实例化一个QComboBox 对象,并为其填充颜色(如“Red”、“Blue”等)。
headerTextFormatCombo = new QComboBox;
headerTextFormatCombo->addItem(tr("Bold"));
headerTextFormatCombo->addItem(tr("Italic"));
headerTextFormatCombo->addItem(tr("Plain"));
headerTextFormatLabel = new QLabel(tr("&Header text:"));
headerTextFormatLabel->setBuddy(headerTextFormatCombo);
firstFridayCheckBox = new QCheckBox(tr("&First Friday in blue"));
mayFirstCheckBox = new QCheckBox(tr("May &1 in red"));Header Text Format 下拉列表允许用户更改水平和垂直标题所使用的文本格式(粗体、斜体或常规)。First Friday in blue 和May 1 in red 复选框则影响特定日期的显示效果。
connect(weekdayColorCombo, &QComboBox::currentIndexChanged,
this, &Window::weekdayFormatChanged);
connect(weekdayColorCombo, &QComboBox::currentIndexChanged,
this, &Window::reformatCalendarPage);
connect(weekendColorCombo, &QComboBox::currentIndexChanged,
this, &Window::weekendFormatChanged);
connect(weekendColorCombo, &QComboBox::currentIndexChanged,
this, &Window::reformatCalendarPage);
connect(headerTextFormatCombo, &QComboBox::currentIndexChanged,
this, &Window::reformatHeaders);
connect(firstFridayCheckBox, &QCheckBox::toggled,
this, &Window::reformatCalendarPage);
connect(mayFirstCheckBox, &QCheckBox::toggled,
this, &Window::reformatCalendarPage);我们将这些复选框和下拉列表连接到各种私有槽。First Friday in blue 和May 1 in red 这两个复选框都连接到reformatCalendarPage() ,当日历切换月份时,该槽也会被调用。
...
reformatHeaders();
reformatCalendarPage();
}在createTextFormatsGroupBox() 函数结尾处,我们调用私有槽来使QCalendarWidget 与其他控件保持同步。
至此,我们已经完成了对create...GroupBox() 中四个函数的回顾。接下来,让我们看看其他私有函数和槽。
QComboBox *Window::createColorComboBox()
{
QComboBox *comboBox = new QComboBox;
comboBox->addItem(tr("Red"), QColor(Qt::red));
comboBox->addItem(tr("Blue"), QColor(Qt::blue));
comboBox->addItem(tr("Black"), QColor(Qt::black));
comboBox->addItem(tr("Magenta"), QColor(Qt::magenta));
return comboBox;
}在createColorCombo() 中,我们创建了一个下拉列表框,并将其填充为标准颜色。QComboBox::addItem()的第二个参数是一个QVariant ,用于存储用户数据(在本例中是QColor 对象)。
该函数用于设置Weekday Color 和Weekend Color 中的下拉列表。
void Window::firstDayChanged(int index)
{
calendar->setFirstDayOfWeek(Qt::DayOfWeek(
firstDayCombo->itemData(index).toInt()));
}当用户更改Week starts on 下拉框的值时,会调用firstDayChanged() ,并传入该下拉框新值的索引。我们通过itemData()获取与当前新项关联的自定义数据项,并将其强制转换为Qt::DayOfWeek 。
selectionModeChanged()、horizontalHeaderChanged() 和verticalHeaderChanged() 与firstDayChanged() 非常相似,因此在此省略。
void Window::selectedDateChanged()
{
currentDateEdit->setDate(calendar->selectedDate());
}selectedDateChanged() 会更新Current Date 编辑器,以反映QCalendarWidget 的当前状态。
void Window::minimumDateChanged(QDate date)
{
calendar->setMinimumDate(date);
maximumDateEdit->setDate(calendar->maximumDate());
}当用户更改最小日期时,我们会通知 QCalendarWidget。同时,我们还会更新Maximum Date 编辑器,因为如果新的最小日期晚于当前的最大日期,QCalendarWidget 将自动调整其最大日期以避免出现矛盾状态。
void Window::maximumDateChanged(QDate date)
{
calendar->setMaximumDate(date);
minimumDateEdit->setDate(calendar->minimumDate());
}maximumDateChanged() 其实现方式与 `minimumDateChanged()` 类似。
void Window::weekdayFormatChanged()
{
QTextCharFormat format;
format.setForeground(qvariant_cast<QColor>(
weekdayColorCombo->itemData(weekdayColorCombo->currentIndex())));
calendar->setWeekdayTextFormat(Qt::Monday, format);
calendar->setWeekdayTextFormat(Qt::Tuesday, format);
calendar->setWeekdayTextFormat(Qt::Wednesday, format);
calendar->setWeekdayTextFormat(Qt::Thursday, format);
calendar->setWeekdayTextFormat(Qt::Friday, format);
}每个下拉列表项都有一个QColor 对象作为用户数据,对应该项的文本。从下拉列表中获取颜色后,我们会设置每周每一天的文本格式。
日历中某列的文本格式以QTextCharFormat 形式给出,除了前景色外,它还允许我们指定各种字符格式信息。在此示例中,我们仅展示了部分可能的格式。
void Window::weekendFormatChanged()
{
QTextCharFormat format;
format.setForeground(qvariant_cast<QColor>(
weekendColorCombo->itemData(weekendColorCombo->currentIndex())));
calendar->setWeekdayTextFormat(Qt::Saturday, format);
calendar->setWeekdayTextFormat(Qt::Sunday, format);
}weekendFormatChanged() 与 `weekdayFormatChanged()` 相同,区别在于它影响的是周六和周日,而不是周一至周五。
void Window::reformatHeaders()
{
QString text = headerTextFormatCombo->currentText();
QTextCharFormat format;
if (text == tr("Bold"))
format.setFontWeight(QFont::Bold);
else if (text == tr("Italic"))
format.setFontItalic(true);
else if (text == tr("Green"))
format.setForeground(Qt::green);
calendar->setHeaderTextFormat(format);
}当用户更改标题的文本格式时,会调用reformatHeaders() 方法。我们通过比较Header Text Format 下拉列表的当前文本,来确定应应用哪种格式。(另一种方法是将QTextCharFormat 的值与下拉列表项一同存储。)
void Window::reformatCalendarPage()
{
QTextCharFormat mayFirstFormat;
const QDate mayFirst(calendar->yearShown(), 5, 1);
QTextCharFormat firstFridayFormat;
QDate firstFriday(calendar->yearShown(), calendar->monthShown(), 1);
while (firstFriday.dayOfWeek() != Qt::Friday)
firstFriday = firstFriday.addDays(1);
if (firstFridayCheckBox->isChecked()) {
firstFridayFormat.setForeground(Qt::blue);
} else { // Revert to regular colour for this day of the week.
Qt::DayOfWeek dayOfWeek(static_cast<Qt::DayOfWeek>(firstFriday.dayOfWeek()));
firstFridayFormat.setForeground(calendar->weekdayTextFormat(dayOfWeek).foreground());
}
calendar->setDateTextFormat(firstFriday, firstFridayFormat);
// When it is checked, "May First in Red" always takes precedence over "First Friday in Blue".
if (mayFirstCheckBox->isChecked()) {
mayFirstFormat.setForeground(Qt::red);
} else if (!firstFridayCheckBox->isChecked() || firstFriday != mayFirst) {
// We can now be certain we won't be resetting "May First in Red" when we restore
// may 1st's regular colour for this day of the week.
Qt::DayOfWeek dayOfWeek(static_cast<Qt::DayOfWeek>(mayFirst.dayOfWeek()));
calendar->setDateTextFormat(mayFirst, calendar->weekdayTextFormat(dayOfWeek));
}
calendar->setDateTextFormat(mayFirst, mayFirstFormat);
}在reformatCalendarPage() 中,我们设置了本月第一个星期五和本年5月1日的文本格式。实际使用的文本格式取决于哪些复选框被选中,以及工作日/周末的格式设置。
QCalendarWidget setDateTextFormat() 允许我们为单个日期设置文本格式。我们选择在日历页面切换(即显示新月份)以及工作日/周末格式发生变化时设置日期格式。我们会检查 和 中哪些选项(如有)被选中,并据此设置相应的文本格式。mayFirstCheckBox firstDayCheckBox
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.