Exemple de plugin backend pour le calendrier
QCalendar exemple illustrant les calendriers personnalisés fournis par l'utilisateur.

Introduction
Il existe de nombreux systèmes de calendrier différents utilisés dans le monde entier. Qt a un support intégré pour certains d'entre eux (voir System), mais ne peut pas fournir un support général en raison de leur nombre élevé. Des systèmes de calendrier supplémentaires peuvent être fournis en implémentant un QCalendarBackend personnalisé, qui est une API privée.
Cet exemple montre comment écrire un backend de calendrier personnalisé et comment utiliser l'API de plugin de bas niveau pour étendre une application aux calendriers sélectionnables par l'utilisateur. De nombreux pays sont passés du calendrier julien au calendrier grégorien à un moment donné de leur histoire, et ce backend de calendrier personnalisé implémentera le calendrier correspondant à titre d'exemple. Le backend personnalisé est compilé dans un plugin et chargé au moment de l'exécution par l'application principale. La date exacte de transition, différente selon les régions, est fournie sous forme de chaîne au plugin et peut être déterminée par l'utilisateur.
Calendrier
La classe du backend du calendrier doit hériter de QCalendarBackend et implémenter ses fonctions virtuelles pures de la même manière que thread-safe. Elle peut également remplacer d'autres fonctions virtuelles si nécessaire.
Exemple d'implémentation
Cet exemple hérite de la classe déjà existante QRomanCalendar, qui hérite à son tour de la classe QCalendarBackend et implémente certaines de ses fonctions virtuelles. Il est constructif de le faire parce que le calendrier de transition partage, avec les calendriers julien et grégorien, des éléments fournis par le calendrier romain.
Voici la déclaration de classe de 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; };
Le QDate passé au constructeur - endJulian - est la date du dernier jour du calendrier julien. Le calendrier calculera automatiquement le décalage pour une année donnée, par exemple en 1582, 10 jours ont été omis, mais en 1700, 12 jours ont dû être omis. Le backend du calendrier est enregistré sous name et une instance de calendrier peut être créée en utilisant ce nom. La classe ne remplace les fonctions que lorsque les deux calendriers qu'elle combine diffèrent de la base romaine. Elle possède des instances des calendriers julien et grégorien auxquelles ces fonctions peuvent être déléguées.
Conversion du jour julien
dateToJulianDay(int year, int month, int day, qint64 *jd) calcule le numéro du jour julien correspondant aux dates spécifiées year, month et day. Renvoie true et définit jd s'il existe une telle date dans ce calendrier ; sinon, renvoie 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) calcule l'année, le mois et le jour dans ce calendrier pour le numéro de jour julien donné, jd. Si le jour donné sort du cadre de ce calendrier, la valeur de retour de isValid() est false. Dans cet exemple, si la date donnée se situe dans la période de transition entre le calendrier julien et le calendrier grégorien, elle sort du cadre.
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); }
Prise en charge des langues
Un calendrier peut, en général, avoir sa propre dénomination des mois de l'année et des jours de la semaine. Ceux-ci doivent être localisés de manière appropriée afin d'être compréhensibles pour tous les utilisateurs. Par défaut, la classe de base du backend s'occupe des noms des jours de la semaine pour nous, ce qui est tout à fait suffisant pour ces calendriers de transition julien/grégorien.
Bien qu'un backend puisse directement surcharger les méthodes de dénomination des mois, la version de la classe de base peut être personnalisée en implémentant localeMonthData() et localeMonthIndexData() pour fournir des tables de noms de mois localisés. Comme les calendriers julien et grégorien utilisent la même dénomination des mois, ils héritent de cette personnalisation à partir d'une base commune, QRomanCalendar. Cela signifie également que le calendrier personnalisé peut utiliser les mêmes noms, toujours en héritant de cette base. Cela permet d'éviter les problèmes de localisation.
Plugin
Les applications Qt peuvent être étendues grâce à des plugins. Pour cela, l'application doit détecter et charger les plugins à l'aide de QPluginLoader.
Écrire un plugin
Pour écrire un plugin, la première chose à faire est de créer une classe virtuelle pure qui définit l'interface entre le plugin et l'application.
Dans cet exemple, l'interface suivante a été utilisée :
class RequestedCalendarInterface { public: RequestedCalendarInterface() = default; virtual QCalendar::SystemId loadCalendar(QAnyStringView requested) = 0; virtual ~RequestedCalendarInterface() = default; };
et l'enregistrer dans le système de méta-objets de Qt :
#define RequestedCalendarInterface_iid \ "org.qt-project.Qt.Examples.CalendarBackend.RequestedCalendarInterface/1.0" Q_DECLARE_INTERFACE(RequestedCalendarInterface, RequestedCalendarInterface_iid)
Q_DECLARE_INTERFACELa macro () est utilisée pour associer le ClassName (ici : RequestedCalendarInterface) au Identifier (ici : RequestedCalendarInterface_iid) défini. Le Identifier doit être unique. Cette interface peut être mise en œuvre par des plugins qui chargent d'autres calendriers, en interprétant le paramètre chaîne de loadCalendar() de différentes manières. Elle n'est pas limitée à ce plugin particulier qui sera implémenté en l'utilisant, c'est pourquoi elle a un nom générique, et non un nom spécifique à ce backend particulier.
Ensuite, une classe de plugin héritant de QObject et de l'interface est créée.
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() et Q_INTERFACES() sont utilisés pour déclarer des métadonnées qui ont également été déclarées dans la classe d'interface et pour indiquer à Qt XML quelle interface la classe implémente.
Ce plugin instancie et enregistre un backend de calendrier personnalisé qui peut à son tour être utilisé pour instancier QCalendar par l'application à n'importe quel moment.
Les plugins Qt sont stockés dans une bibliothèque partagée unique (une DLL) et QPluginLoader est utilisé pour détecter et charger dynamiquement le fichier du plugin (pour plus d'informations, voir Comment créer des plugins Qt).
Chargement du plugin
QPluginLoader vérifie si la version de Qt du plugin est la même que celle de l'application et fournit un accès direct à un plugin Qt.
Voici l'utilisation de QPluginLoader dans l'exemple :
QPluginLoader loader; loader.setFileName("../plugin/calendarPlugin"); loader.load(); if (!loader.isLoaded()) return 1; auto *myplugin = qobject_cast<RequestedCalendarInterface*>(loader.instance());
Tout d'abord, une instance de l'objet QPluginLoader doit être initialisée. Ensuite, il faut spécifier le plugin à charger en passant un nom de fichier DLL à setFileName(). Ensuite, en utilisant load(), le fichier du plugin est chargé dynamiquement. À la fin, un appel à qobject_cast() teste si un plugin implémente une interface donnée. qobject_cast() utilise instance() pour accéder au composant racine du plugin. Si le plugin a été chargé correctement, ses fonctions devraient être disponibles.
Instanciation du backend
Dans cet exemple, il n'y a qu'une seule fonction dans le plugin. loadCalendar() est responsable de l'enregistrement du backend du calendrier personnalisé dans QCalendarRegistry avec une date de transition et des noms donnés.
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() { }
L'argument de chaîne pour loadCalendar() est fourni par l'utilisateur via les arguments de la ligne de commande. Ensuite, la date de transition du calendrier julien au calendrier grégorien est extraite en divisant la chaîne donnée. Après validation, un objet backend personnalisé est créé. Le backend doit être enregistré avant de pouvoir être utilisé dans QCalendar, à l'aide de la méthode registerCustomBackend(). Une fois qu'un backend est enregistré, un QCalendar peut être instancié avec le SystemId ou le name correspondant.
Voici l'utilisation de loadCalendar dans main:
const auto cid = myplugin->loadCalendar(args.at(0)) ; if (!cid.isValid()) { qWarning() << "Invalid ID"; parser.showHelp(1) ; } const QCalendar calendar(cid) ;
Extension de QCalendarWidget
En créant une instance de QCalendar avec un calendrier spécifique comme backend, il est possible de fournir à QCalendarWidget ce backend et de le visualiser.
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);
Voir aussi QCalendarWidget, QCalendar, QDate, QLocale, QtPlugin, et 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.