系列总链接:
QT5.14.2 官方例子 - 学习系列
https://blog.csdn.net/qq_22122811/article/details/108007519
日历小部件示例展示了QCalendarWidget的使用。(Calendar => 英 /ˈkælɪndə(r)/)
QCalendarWidget每次显示一个日历月,并让用户选择一个日期。日历由四个组件组成:一个导航栏,允许用户更改所显示的月份;一个网格,其中每个单元格表示该月中的一天;以及两个标题,显示工作日名称和周号。
日历小部件示例显示了一个QCalendarWidget,并允许用户使用qcombobox、qcheckbox和QDateEdits配置它的外观和行为。此外,用户可以影响单个日期和标题的格式。
详细介绍链接:
https://doc.qt.io/qt-5/qtwidgets-widgets-calendarwidget-example.html
大概思路介绍:
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 QCalendarWidget的初始大小。
.......
.......
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(_lang);
QList countries = QLocale::countriesForLanguage(lang);
for (int i = 0; i < countries.count(); ++i) {
QLocale::Country country = countries.at(i);
QString label = QLocale::languageToString(lang);
label += QLatin1Char('/');
label += QLocale::countryToString(country);
QLocale locale(lang, country);
if (this->locale().language() == lang && this->locale().country() == country)
curLocaleIndex = index;
localeCombo->addItem(label, locale);
++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);
...
补充:
qt 字符串中"&"的用法:
https://segmentfault.com/q/1010000004987218
我们从combobox上的设置开始。这个组合框控制哪一天应该显示为一周的第一天。
QComboBox类允许我们将用户数据作为一个QVariant附加到每个项上。稍后可以使用QComboBox的itemData()函数检索数据。QVariant不直接支持Qt::DayOfWeek数据类型,但它支持int,而c++很乐意将任何枚举值转换为int。
...
connect(localeCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::localeChanged);
connect(firstDayCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::firstDayChanged);
connect(selectionModeCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::selectionModeChanged);
connect(gridCheckBox, &QCheckBox::toggled,
calendar, &QCalendarWidget::setGridVisible);
connect(navigationCheckBox, &QCheckBox::toggled,
calendar, &QCalendarWidget::setNavigationBarVisible);
connect(horizontalHeaderCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::horizontalHeaderChanged);
connect(verticalHeaderCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::verticalHeaderChanged);
...
创建小部件之后,我们将信号和插槽连接起来。我们将组合盒连接到窗口的私有插槽或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);
在此函数中,我们创建最小日期、最大日期和当前日期编辑器小部件,这些小部件控制日历的最小、最大和所选日期。日历的最小和最大日期已经在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()插槽将更新当前的日期编辑器。我们还需要在用户更改最小日期和最大日期编辑器时做出反应。
Here is the createTextFormatsGroup() function:
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()设置工作日颜色和周末颜色组合框,它实例化一个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"));
标题文本格式组合框允许用户更改用于水平和垂直标题的文本格式(粗体、斜体或纯文本)。第一个蓝色的星期五和红色的5月1号复选框会影响特定日期的呈现。
connect(weekdayColorCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::weekdayFormatChanged);
connect(weekdayColorCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::reformatCalendarPage);
connect(weekendColorCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::weekendFormatChanged);
connect(weekendColorCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::reformatCalendarPage);
connect(headerTextFormatCombo, QOverload::of(&QComboBox::currentIndexChanged),
this, &Window::reformatHeaders);
connect(firstFridayCheckBox, &QCheckBox::toggled,
this, &Window::reformatCalendarPage);
connect(mayFirstCheckBox, &QCheckBox::toggled,
this, &Window::reformatCalendarPage);
我们将复选框和组合框连接到各种私有插槽。蓝色复选框中的第一个星期五和红色复选框中的5月1日都连接到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对象)。
该函数用于设置工作日颜色和周末颜色组合框。
void Window::firstDayChanged(int index)
{
calendar->setFirstDayOfWeek(Qt::DayOfWeek(
firstDayCombo->itemData(index).toInt()));
}
当用户在combobox的值上更改一周时,使用combobox新值的索引调用firstDayChanged()。我们使用itemData()检索与新当前项关联的自定义数据项,并将其转换为Qt::DayOfWeek。
selectionModeChanged()、horizontalHeaderChanged()和verticalHeaderChanged()非常类似于firstDayChanged(),因此它们被省略。
void Window::selectedDateChanged()
{
currentDateEdit->setDate(calendar->selectedDate());
}
selectedDateChanged()更新当前日期编辑器,以反映QCalendarWidget的当前状态。
void Window::minimumDateChanged(QDate date)
{
calendar->setMinimumDate(date);
maximumDateEdit->setDate(calendar->maximumDate());
}
当用户更改最小日期时,我们告诉QCalenderWidget。我们还更新了最大日期编辑器,因为如果新的最小日期晚于当前最大日期,QCalendarWidget将自动调整其最大日期,以避免出现矛盾状态。
void Window::maximumDateChanged(QDate date)
{
calendar->setMaximumDate(date);
minimumDateEdit->setDate(calendar->minimumDate());
}
maximumDateChanged()的实现类似于minimumDateChanged()。
void Window::weekdayFormatChanged()
{
QTextCharFormat format;
format.setForeground(qvariant_cast(
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);
}
每个combobox项目都有一个QColor对象作为与项目文本对应的用户数据。从组合框中获取颜色后,我们设置每周每天的文本格式。
日历中列的文本格式是QTextCharFormat,除了前景色外,它还允许我们指定各种字符格式信息。在本例中,我们只展示了可能性的一个子集。
void Window::weekendFormatChanged()
{
QTextCharFormat format;
format.setForeground(qvariant_cast(
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()插槽。我们比较标题文本格式组合框的当前文本以确定应用哪种格式。(另一种方法是将QTextCharFormat值存储在combobox项旁边。)
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(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(mayFirst.dayOfWeek()));
calendar->setDateTextFormat(mayFirst, calendar->weekdayTextFormat(dayOfWeek));
}
calendar->setDateTextFormat(mayFirst, mayFirstFormat);
}
在reformatCalendarPage()中,我们设置当月的第一个星期五和当前年份的5月1日的文本格式。实际使用的文本格式取决于选中的复选框和工作日/周末格式。
QCalendarWidget允许我们使用setDateTextFormat()设置单个日期的文本格式。我们选择在日历页面更改时设置日期格式——即显示一个新的月份——以及在工作日/周末格式更改时设置日期格式。我们检查mayFirstCheckBox和firstDayCheckBox的哪个(如果有的话)被选中,并相应地设置文本格式。