캘린더 백엔드 플러그인 예제
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 에 등록되어 있으며 이 이름을 사용하여 캘린더 인스턴스를 만들 수 있습니다. 이 클래스는 결합하는 두 달력이 로마 기준과 다른 경우에만 함수를 재정의합니다. 이 클래스에는 이러한 함수가 위임할 수 있는 율리우스력 및 그레고리력의 인스턴스가 있습니다.
율리우스 력 변환
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()를 사용하여 플러그인의 루트 컴포넌트에 액세스합니다. 플러그인이 올바르게 로드되었다면 해당 기능을 사용할 수 있어야 합니다.
백엔드 인스턴스화하기
이 예에서는 플러그인에 함수가 하나만 있습니다. 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()
에 대한 문자열 인수는 사용자가 명령줄 인수를 통해 제공합니다. 그런 다음 주어진 문자열을 분할하여 율리우스력에서 그레고리력으로 전환되는 날짜를 추출합니다. 유효성 검사 후 사용자 정의 백엔드 객체가 생성됩니다. 백엔드는 registerCustomBackend()
메서드를 사용하여 QCalendar 에서 사용하려면 먼저 등록해야 합니다. 백엔드가 등록되면 해당 SystemId 또는 name
을 사용하여 QCalendar 을 인스턴스화할 수 있습니다.
다음은 main
에서 loadCalendar
을 사용하는 예입니다:
const auto cid = myplugin->loadCalendar(args.at(0)); if (!cid.isValid()) {} qWarning() << "Invalid ID"; parser.showHelp(1); } const QCalendar 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도 참조하십시오 .
© 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.