日历小工具示例
日历小工具示例展示了QCalendarWidget 的使用。
QCalendarWidget 该日历小工具每次显示一个日历月份,并允许用户选择日期。日历由四个部分组成:一个导航栏,可让用户更改显示的月份;一个网格,每个单元格代表一个月中的一天;以及两个标题,显示工作日名称和周数。
日历小工具示例显示了一个QCalendarWidget ,用户可以使用QComboBoxes、QCheckBoxes 和QDateEdits 配置其外观和行为。此外,用户还可以影响单个日期和标题的格式。
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(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 并不直接支持 数据类型,但它支持 ,C++ 会很乐意将任何枚举值转换为 。Qt::DayOfWeek int
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 并填充颜色("红色"、"蓝色 "等)。
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() 的第二个参数是存储用户数据(在本例中为QColor 对象)的QVariant 。
该函数用于设置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()); }
当用户更改最小日期时,我们会告诉 QCalenderWidget。我们也会更新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
© 2025 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.