Exemple de plugin de gestion de calendrier
QCalendar Exemple illustrant l'utilisation de calendriers personnalisés fournis par l'utilisateur.

Introduction
Il existe de nombreux systèmes de calendrier différents utilisés à travers le monde. Qt prend en charge certains d'entre eux de manière native (voir System), mais ne peut pas offrir une prise en charge générale en raison de leur nombre élevé. Il est possible d'ajouter d'autres systèmes de calendrier 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 à des 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é en un plugin et chargé à l’exécution par l’application principale. La date exacte de transition, qui varie selon les régions, est fournie sous forme de chaîne de caractères au plugin et peut être déterminée par l’utilisateur.
Backend de calendrier
La classe du backend de calendrier doit hériter de ` QCalendarBackend ` et implémenter ses fonctions virtuelles pures de manière « thread-safe ». Elle peut également redéfinir 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 elle-même de ` QCalendarBackend ` et implémente certaines de ses fonctions virtuelles. Cette approche est judicieuse car 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;
};L’ QDate transmis au constructeur — endJulian — correspond à 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, tandis qu’en 1700, 12 jours ont dû l’être. Le backend du calendrier est enregistré sous le nom name et une instance de calendrier peut être créée à l'aide de ce nom. La classe ne redéfinit que les fonctions pour lesquelles les deux calendriers qu'elle combine diffèrent du calendrier romain de base. Elle dispose d'instances des calendriers julien et grégorien auxquelles ces fonctions peuvent déléguer.
Conversions de jours juliens
dateToJulianDay(int year, int month, int day, qint64 *jd) calcule le numéro de 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é se situe en dehors de la période couverte par ce calendrier, la valeur renvoyée pour isValid() est false. Dans cet exemple, si la date donnée se situe dans l’intervalle sauté lors du passage du calendrier julien au calendrier grégorien, elle est hors de la période couverte.
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 paramètres régionaux
Un calendrier peut, en général, avoir sa propre nomenclature pour les mois de l’année et les jours de la semaine. Celle-ci doit être correctement localisée pour être compréhensible par tous les utilisateurs. Par défaut, la classe de base du backend gère pour nous les noms des jours de la semaine, ce qui est tout à fait suffisant pour ces calendriers de transition entre le calendrier julien et le calendrier grégorien.
Bien qu’un backend puisse directement redéfinir les méthodes de dénomination des mois, la version fournie par la classe de base peut être personnalisée en implémentant les méthodes ` localeMonthData() ` et ` localeMonthIndexData() ` afin de fournir des tables de noms de mois localisés. Comme les calendriers julien et grégorien utilisent la même nomenclature des mois, ils héritent de cette personnalisation d’une base commune, QRomanCalendar. Cela signifie également que le calendrier personnalisé peut utiliser les mêmes noms, là encore en héritant de cette base. La localisation est ainsi prise en charge.
Plugin
Les applications Qt peuvent être étendues à l’aide de 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 de l'enregistrer dans le système de méta-objets Qt :
#define RequestedCalendarInterface_iid \
"org.qt-project.Qt.Examples.CalendarBackend.RequestedCalendarInterface/1.0"
Q_DECLARE_INTERFACE(RequestedCalendarInterface, RequestedCalendarInterface_iid)Q_DECLARE_INTERFACELa macro () sert à associer l’ ClassName (ici : RequestedCalendarInterface) à l’ Identifier défini (ici : RequestedCalendarInterface_iid). L'Identifier doit être unique. Cette interface peut être implémentée par des plugins qui chargent d'autres calendriers, en interprétant de différentes manières le paramètre de chaîne de caractères de loadCalendar(). Elle n'est pas limitée au plugin particulier qui sera implémenté à l'aide de celle-ci ; elle porte donc 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_METADATALes méthodes () et Q_INTERFACES() sont utilisées pour déclarer les métadonnées qui ont également été déclarées dans la classe d’interface et pour indiquer à Qt quelle interface la classe implémente.
Ce plugin instancie et enregistre un backend de calendrier personnalisé qui peut à son tour être utilisé par l’application pour instancier ` QCalendar ` à tout moment.
Les plugins Qt sont stockés dans une seule bibliothèque partagée (une DLL) et QPluginLoader est utilisé pour détecter et charger dynamiquement le fichier du plugin (pour en savoir plus, voir Comment créer des plugins Qt).
Chargement du plugin
QPluginLoader vérifie si la version de Qt du plugin correspond à celle de l'application et permet d'accéder directement à un plugin Qt.
Voici comment utiliser ` 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, il faut initialiser une instance d’un objet ` QPluginLoader `. Ensuite, il faut spécifier le plugin à charger en passant le nom d’un fichier DLL à la fonction ` setFileName()`. Puis, à l’aide de la fonction ` load()`, le fichier du plugin est chargé dynamiquement. Enfin, un appel à ` qobject_cast()` permet de vérifier si un plugin implémente une interface donnée. La méthode ` 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, le plugin ne contient qu’une seule fonction. ` loadCalendar() ` est chargée d’enregistrer le backend de calendrier personnalisé dans ` QCalendarRegistry ` avec la date de transition et les noms indiqué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 type chaîne de caractères pour ` loadCalendar() ` est fourni par l'utilisateur via des arguments de ligne de commande. Ensuite, la date de transition du calendrier julien au calendrier grégorien est extraite en fractionnant la chaîne fournie. 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 le backend enregistré, un ` QCalendar ` peut être instancié avec les méthodes correspondantes ` SystemId ` ou ` name`.
Voici comment utiliser ` loadCalendar ` dans le fichier ` 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 ce backend à ` QCalendarWidget ` et de l’afficher.
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 également 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.