カレンダー・バックエンド・プラグインの例
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); }
ロケールのサポート
暦は一般に、その年独自の月名や曜日名を持つことができる。これらは、すべてのユーザが理解できるように適切にローカライズされなければなりません。デフォルトでは、バックエンドの baseclass が曜日の名前を管理してくれます。
バックエンドは月の命名メソッドを直接オーバーライドすることができますが、localeMonthData()
やlocaleMonthIndexData()
を実装することで、 baseclass をカスタマイズすることができます。ユリウス暦とグレゴリオ暦は同じ月命名法を使用しているので、 カスタマイズも共通のベースクラス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() を使用して、プラグインのルート・コンポーネントにアクセスします。プラグインが正しくロードされていれば、その関数を使用できるはずです。
バックエンドのインスタンス化
この例では、プラグインの関数はひとつだけです。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()
メソッドで登録する必要があります。バックエンドが登録されると、SystemId あるいはname
でQCalendar をインスタンス化できるようになります。
以下は、main
におけるloadCalendar
の使用例です:
const autocid=myplugin->loadCalendar(args.at(0));if(!cid.isValid()) { { { { { qWarning() << "Invalid ID"; parser.showHelp(1); }constQCalendarcalendar(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も参照してください 。
© 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.