カレンダーウィジェットの例

カレンダー・ウィジェットの例は、QCalendarWidget.

QCalendarWidget は、一度に1つのカレンダー月を表示し、ユーザに日付を選択させます。カレンダーは、表示される月を変更できるナビゲーション・バー、各セルが月の1日を表すグリッド、曜日名と週番号を表示する2つのヘッダーという4つのコンポーネントで構成されています。

Calendar Widgetの例では、QCalendarWidget を表示し、QComboBoxes、QCheckBoxes、QDateEditsを使用して、ユーザーがその外観と動作を設定できます。さらに、ユーザーは個々の日付とヘッダーのフォーマットに影響を与えることができます。

QCalendarWidget のプロパティは、以下の表にまとめられています。

プロパティ説明
selectedDate現在選択されている日付。
minimumDate選択可能な最も古い日付。
maximumDate選択可能な最新の日付。
firstDayOfWeek週の最初の日として表示される日(通常は日曜日または月曜日)。
gridVisibleグリッドを表示するかどうか。
selectionModeユーザーが日付を選択できるかどうか。
horizontalHeaderFormat水平ヘッダーの曜日名のフォーマット(例:"M"、"Mon"、"Monday")。
verticalHeaderFormat垂直ヘッダーのフォーマット。
navigationBarVisibleカレンダー・ウィジェットの上部にナビゲーション・バーを表示するかどうか。

この例は、QCalendarWidget を作成しレイアウトするクラスWindow と、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"));
}

まず、4 つのQGroupBoxes とその子ウィジェット(QCalendarWidget を含む)を、後述する 4 つの privatecreate...GroupBox() 関数を使用して作成します。次に、グループ・ボックスを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 というウィジェットが1つだけ含まれています。このウィジェットをセットアップし、その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 。このデータは後でQComboBoxitemData() 関数で取り出すことができます。QVariantQt::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 DateMaximum DateCurrent 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);
    ...
}

currentDateEditdateChanged() シグナルを、カレンダーのsetSelectedDate() スロットに直接接続する。カレンダーの選択された日付が変更されると、ユーザーのアクションまたはプログラムによって、selectedDateChanged() スロットがCurrent Date エディタを更新します。また、ユーザーがMinimum DateMaximum 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 ColorWeekend 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 blueMay 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 blueMay 1 in red のチェックボックスはどちらもreformatCalendarPage() に接続されており、カレンダーが月を切り替えるときにも呼び出されます。

    ...
    reformatHeaders();
    reformatCalendarPage();
}

createTextFormatsGroupBox() の最後では、QCalendarWidget を他のウィジェットと同期させるためにプライベートスロットを呼び出します。

これで、4つの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 ()の第2引数は、ユーザー・データ(この場合はQColor オブジェクト)を格納するQVariant

この関数は、Weekday ColorWeekend 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() では、 その月の第 1 金曜日と、 その年の 5 月 1 日をテ キ ス ト 形式 と し て設定 し てい ます。実際に使用されるテキスト・フォーマットは、どのチェック・ボックスにチェックが入っているか、曜日/週末のフォーマットが何であるかによって異なります。

QCalendarWidget では、 ()を使って、個々の日付のテキスト書式を設定することができます。ここでは、カレンダーのページが変わったとき、つまり新しい月が表示されたときと、曜日/週末の書式が変更されたときに、日付の書式を設定することにしました。 と のいずれがチェックされているかを調べ、それに応じてテキスト書式を設定します。setDateTextFormat mayFirstCheckBox firstDayCheckBox

プロジェクト例 @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。