カレンダーバックエンドプラグインの例
QCalendar ユーザーが指定したカスタムカレンダーを示す例。

はじめに
世界中で数多くの異なるカレンダーシステムが使用されています。Qt にはその一部に対する組み込みサポートがあります(System を参照)が、その数が多すぎるため、一般的なサポートを提供することはできません。追加のカレンダーシステムは、非公開 API であるカスタム QCalendarBackend を実装することで提供できます。
この例では、カスタムカレンダーバックエンドの作成方法と、低レベルのプラグインAPIを使用してアプリケーションを拡張し、ユーザーがカレンダーを選択できるようにする方法について説明します。多くの国では、歴史上のある時点でユリウス暦からグレゴリオ暦へと移行しており、このカスタムカレンダーバックエンドでは、その例としてそれぞれの暦を実装します。 カスタムバックエンドはプラグインとしてコンパイルされ、実行時にメインアプリケーションによって読み込まれます。地域ごとに異なる正確な移行日は、文字列としてプラグインに提供され、ユーザーが指定することができます。
カレンダーバックエンド
カレンダー・バックエンドクラスは、QCalendarBackend を継承し、その純粋仮想関数をthread-safe な方法で実装する必要があります。また、必要に応じて他の仮想関数をオーバーライドすることも可能です。
実装例
この例では、既存のQRomanCalendar を継承しています。 はQCalendarBackend を継承しており、その仮想関数のいくつかを実装しています。移行暦は、ユリウス暦およびグレゴリオ暦の両方と、ローマ暦によって提供される部分を共有しているため、このように実装することは理にかなっています。
以下に、JulianGregorianCalendar のクラス宣言を示します:
class JulianGregorianCalendar : public QRomanCalendar
{
public:
JulianGregorianCalendar(QDate endJulian, QAnyStringView name);
QString name() const override;
int daysInMonth(int month, int year = QCalendar::Unspecified) const override;
bool isLeapYear(int year) const override;
bool dateToJulianDay(int year, int month, int day, qint64 *jd) const override;
QCalendar::YearMonthDay julianDayToDate(qint64 jd) const override;
private:
static inline const QCalendar julian = QCalendar(QCalendar::System::Julian);
static inline const QCalendar gregorian = QCalendar(QCalendar::System::Gregorian);
QCalendar::YearMonthDay m_julianUntil;
QCalendar::YearMonthDay m_gregorianSince;
QString m_name;
};コンストラクタに渡されるQDate (endJulian )は、ユリウス暦の最終日の日付です。このカレンダーは、指定された年におけるずれを自動的に計算します。例えば、1582年には10日が省略されましたが、1700年には12日を省略する必要がありました。 カレンダーのバックエンドはname で登録されており、その名前を使用してカレンダーのインスタンスを作成できます。このクラスは、統合する2つのカレンダーがローマ暦の基準から異なる部分についてのみ、関数をオーバーライドしています。また、これらの関数が処理を委譲できるユリウス暦およびグレゴリオ暦のインスタンスも備えています。
ユリウス日数の変換
dateToJulianDay(int year, int month, int day, qint64 *jd) 指定されたyear 、month 、およびday に対応するユリウス日数を計算します。このカレンダーにそのような日付が存在する場合、true を返し、jd を設定します。そうでない場合は、false を返します。
bool JulianGregorianCalendar::dateToJulianDay(int year, int month, int day, qint64 *jd) const
{
if (year == m_julianUntil.year && month == m_julianUntil.month) {
if (m_julianUntil.day < day && day < m_gregorianSince.day) {
// Requested date is in the gap skipped over by the transition.
*jd = 0;
return false;
}
}
QDate givenDate = gregorian.dateFromParts(year, month, day);
QDate julianUntil = julian.dateFromParts(m_julianUntil);
if (givenDate > julianUntil) {
*jd = givenDate.toJulianDay();
return true;
}
*jd = julian.dateFromParts(year, month, day).toJulianDay();
return true;
}julianDayToDate(qint64 jd) 指定されたユリウス日数(jd )に対して、このカレンダーにおける年、月、日を計算します。指定された日がこのカレンダーの範囲外にある場合、isValid() の戻り値はfalse となります。この例において、指定された日付がユリウス暦からグレゴリオ暦への移行によってスキップされた期間に含まれる場合、その日付は範囲外となります。
QCalendar::YearMonthDay JulianGregorianCalendar::julianDayToDate(qint64 jd) const
{
const qint64 jdForChange = julian.dateFromParts(m_julianUntil).toJulianDay();
if (jdForChange < jd) {
QCalendar gregorian(QCalendar::System::Gregorian);
QDate date = QDate::fromJulianDay(jd);
return gregorian.partsFromDate(date);
} else if (jd <= jdForChange) {
QCalendar julian(QCalendar::System::Julian);
QDate date = QDate::fromJulianDay(jd);
return julian.partsFromDate(date);
}
return QCalendar::YearMonthDay(QCalendar::Unspecified, QCalendar::Unspecified,
QCalendar::Unspecified);
}ロケールのサポート
カレンダーには、一般的に、独自の月の名称や曜日の名称が設定されている場合があります。これらは、すべてのユーザーが理解できるよう、適切にローカライズされる必要があります。デフォルトでは、バックエンドの基底クラスが曜日の名称を自動的に処理してくれますが、これはユリウス暦とグレゴリオ暦の移行期にあるカレンダーにとっては、十分に機能します。
バックエンドでは月の名称設定メソッドを直接上書きすることも可能ですが、localeMonthData() およびlocaleMonthIndexData() を実装してローカライズされた月名のテーブルを提供することで、基底クラスのバージョンもカスタマイズできます。 ユリウス暦とグレゴリオ暦は同じ月の名称を使用しているため、共通の基底クラスである `QRomanCalendar` からそのカスタマイズを継承します。これはまた、カスタムカレンダーも同様にその基底クラスを継承することで、同じ名称を使用できることを意味します。これにより、ローカライズが適切に処理されます。
プラグイン
Qtアプリケーションは、プラグインを通じて機能を拡張できます。これには、アプリケーションがQPluginLoader を使用してプラグインを検出し、読み込む必要があります。
プラグインの作成
プラグインを作成するには、まず、プラグインとアプリケーション間のインターフェースを定義する純粋仮想クラスを作成する必要があります。
この例では、以下のインターフェースが使用されました:
class RequestedCalendarInterface
{
public:
RequestedCalendarInterface() = default;
virtual QCalendar::SystemId loadCalendar(QAnyStringView requested) = 0;
virtual ~RequestedCalendarInterface() = default;
};そして、それをQtのメタオブジェクトシステムに登録します:
#define RequestedCalendarInterface_iid \
"org.qt-project.Qt.Examples.CalendarBackend.RequestedCalendarInterface/1.0"
Q_DECLARE_INTERFACE(RequestedCalendarInterface, RequestedCalendarInterface_iid)Q_DECLARE_INTERFACE() マクロは、ClassName (ここでは:RequestedCalendarInterface )を、定義されたIdentifier (ここでは:RequestedCalendarInterface_iid )に関連付けるために使用されます。Identifier は一意でなければなりません。このインターフェースは、他のカレンダーを読み込むプラグインによって実装され、loadCalendar() の文字列パラメータを様々な方法で解釈することができます。これを使用する特定の実装プラグインに限定されないため、このインターフェースには特定のバックエンドに固有のものではなく、汎用的な名前が付けられています。
次に、QObject およびこのインターフェースを継承するプラグインクラスが作成されます。
class JulianGregorianPlugin : public QObject, public RequestedCalendarInterface
{
Q_OBJECT
Q_INTERFACES(RequestedCalendarInterface)
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples."
"CalendarBackend."
"RequestedCalendarInterface/1.0")
public:
JulianGregorianPlugin();
QCalendar::SystemId loadCalendar(QAnyStringView request) override;
~JulianGregorianPlugin();
};Q_PLUGIN_METADATA() およびQ_INTERFACES() は、インターフェースクラスでも宣言されていたメタデータを宣言し、そのクラスがどのインターフェースを実装しているかを Qt に伝えるために使用されています。
このプラグインは、カスタムカレンダーバックエンドをインスタンス化して登録します。このバックエンドは、アプリケーションがいつでもQCalendar をインスタンス化する際に使用できます。
Qt プラグインは単一の共有ライブラリ(DLL)に格納されており、QPluginLoader はプラグインファイルの検出および動的ロードに使用されます(詳細については、「Qt プラグインの作成方法」を参照してください)。
プラグインの読み込み
QPluginLoader プラグインのQtバージョンがアプリケーションのバージョンと一致するかどうかを確認し、Qtプラグインへの直接アクセスを提供します。
以下は、この例におけるQPluginLoader の使用例です:
QPluginLoader loader;
loader.setFileName("../plugin/calendarPlugin");
loader.load();
if (!loader.isLoaded())
return 1;
auto *myplugin = qobject_cast<RequestedCalendarInterface*>(loader.instance());まず、QPluginLoader オブジェクトのインスタンスを初期化する必要があります。次に、setFileName()にDLLファイル名を渡して、読み込むプラグインを指定します。その後、load()を使用して、プラグインファイルを動的に読み込みます。 最後に、qobject_cast()を呼び出すことで、プラグインが指定されたインターフェースを実装しているかどうかをテストします。qobject_cast()は、instance()を使用して、プラグイン内のルートコンポーネントにアクセスします。プラグインが正しく読み込まれていれば、その関数を利用できるようになります。
バックエンドのインスタンス化
この例では、プラグインには1つの関数しか含まれていません。loadCalendar() は、指定された遷移日時と名前を用いて、QCalendarRegistry にカスタムカレンダーバックエンドを登録する役割を担っています。
QCalendar::SystemId JulianGregorianPlugin::loadCalendar(QAnyStringView request)
{
Q_ASSERT(!request.isEmpty());
QStringList names = request.toString().split(u';');
if (names.size() < 1)
return {};
QString dateString = names.takeFirst();
auto date = QDate::fromString(dateString, u"yyyy-MM-dd",
QCalendar(QCalendar::System::Julian));
if (!date.isValid())
return {};
QString primary = names.isEmpty() ?
QString::fromStdU16String(u"Julian until ") + dateString : names[0];
auto backend = new JulianGregorianCalendar(date, primary);
names.emplaceFront(backend->name());
auto cid = backend->registerCustomBackend(names);
return cid;
}
JulianGregorianPlugin::~JulianGregorianPlugin()
{
}loadCalendar() への文字列引数は、ユーザーがコマンドライン引数を通じて指定します。その後、指定された文字列を分割して、ユリウス暦からグレゴリオ暦への移行日を抽出します。検証後、カスタムバックエンドオブジェクトが作成されます。 バックエンドをQCalendar で使用する前に、registerCustomBackend() メソッドを使用して登録する必要があります。バックエンドが登録されると、QCalendar を、対応するSystemId またはname を使用してインスタンス化できます。
以下は、main におけるloadCalendar の使用例です:
const autocid= myplugin->loadCalendar(args.at(0));
if(!cid.isValid()) {
qWarning() << "Invalid ID";
parser.showHelp(1);
}
constQCalendar calendar(cid);QCalendarWidgetの拡張
特定のカレンダーをバックエンドとしてQCalendar インスタンスを作成することで、そのバックエンドをQCalendarWidget に提供し、表示させることができます。
QCalendarWidget widget;
widget.setCalendar(calendar);
widget.show();
QCalendar::YearMonthDay when = { 1582, 10, 4 };
QCalendar julian = QCalendar(QCalendar::System::Julian);
auto got = QDate::fromString(args.at(0).left(10), u"yyyy-MM-dd", julian);
if (got.isValid())
when = julian.partsFromDate(got);
widget.setCurrentPage(when.year, when.month);関連項目: QCalendarWidget 、QCalendar 、QDate 、QLocale 、QtPlugin、およびQPluginLoader 。
© 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.