Qt 플러그인 생성 방법
Qt는 플러그인을 생성하기 위한 두 가지 API를 제공합니다:
- 사용자 정의 데이터베이스 드라이버, 이미지 형식, 텍스트 코덱, 스타일 등 Qt 자체에 대한 확장 기능을 작성하기 위한 고수준 API.
- Qt 애플리케이션을 확장하기 위한 저수준 API.
예를 들어, 사용자 정의 ` QStyle ` 서브클래스를 작성하고 Qt 애플리케이션이 이를 동적으로 로드하도록 하려면 상위 수준 API를 사용해야 합니다.
고수준 API는 저수준 API를 기반으로 구축되었기 때문에, 두 API 모두에서 공통적으로 발생하는 문제들이 있습니다.
Qt Widgets Designer 와 함께 사용할 플러그인을 제공하려면 “사용자 정의 위젯 플러그인 만들기”를 참조하십시오.
고수준 API: Qt 확장 기능 작성
Qt 자체를 확장하는 플러그인을 작성하려면, 적절한 플러그인 기본 클래스를 상속하고, 몇 가지 함수를 구현하며, 매크로를 추가하면 됩니다.
여러 가지 플러그인 기본 클래스가 있습니다. 파생된 플러그인은 기본적으로 표준 플러그인 디렉터리의 하위 디렉터리에 저장됩니다. 플러그인이 적절한 디렉터리에 저장되어 있지 않으면 Qt는 해당 플러그인을 찾지 못합니다.
다음 표는 플러그인 기본 클래스를 요약한 것입니다. 일부 클래스는 비공개이므로 문서에 기재되어 있지 않습니다. 이러한 클래스를 사용할 수는 있지만, 향후 Qt 버전과의 호환성은 보장되지 않습니다.
| 기본 클래스 | 디렉터리 이름 | Qt 모듈 | 키 대소문자 구분 |
|---|---|---|---|
| QAccessibleBridgePlugin | accessiblebridge | Qt GUI | 대소문자 구분 |
| QImageIOPlugin | imageformats | Qt GUI | 대소문자 구분 |
| QPictureFormatPlugin (사용 중단됨) | pictureformats | Qt GUI | 대소문자 구분 |
| QBearerEnginePlugin | bearer | Qt Network | 대소문자 구분 |
| QPlatformInputContextPlugin | platforminputcontexts | Qt 플랫폼 추상화 | 대소문자 구분 없음 |
| QPlatformIntegrationPlugin | platforms | Qt 플랫폼 추상화 | 대소문자 구분 없음 |
| QPlatformThemePlugin | platformthemes | Qt 플랫폼 추상화 | 대소문자 구분 없음 |
| QPlatformPrinterSupportPlugin | printsupport | Qt Print Support | 대소문자 구분 없음 |
| QSGContextPlugin | scenegraph | Qt Quick | 대소문자 구분 |
| QSqlDriverPlugin | sqldrivers | Qt SQL | 대소문자 구분 |
| QIconEnginePlugin | iconengines | Qt SVG | 대소문자 구분 없음 |
| QAccessiblePlugin | accessible | Qt Widgets | 대소문자 구분 |
| QStylePlugin | styles | Qt Widgets | 대소문자 구분 없음 |
JsonViewer 라는 새로운 문서 뷰어 클래스를 플러그인으로 제공하려는 경우, 해당 클래스는 다음과 같이 정의되어야 합니다(jsonviewer.h):
class JsonViewer : public ViewerInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" FILE "jsonviewer.json")
Q_INTERFACES(ViewerInterface)
public:
JsonViewer();
~JsonViewer() override;
private:
void retranslate() override;
bool openJsonFile();
QTreeView *m_tree = nullptr;
QListWidget *m_toplevel = nullptr;
QJsonDocument m_root;
QAction *m_expandAllAction = nullptr;
QAction *m_collapseAllAction = nullptr;
};클래스 구현이 .cpp 파일에 위치해 있는지 확인하십시오:
JsonViewer::JsonViewer()
: m_expandAllAction(new QAction(this)),
m_collapseAllAction(new QAction(this))
{
connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi);
m_expandAllAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomIn));
m_collapseAllAction->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomOut));
}
void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
{
AbstractViewer::init(file, new QTreeView(parent), mainWindow);
setTranslationBaseName("jsonviewer"_L1);
m_tree = qobject_cast<QTreeView *>(widget());
}또한, 대부분의 플러그인의 경우 플러그인을 설명하는 메타데이터가 포함된 json 파일(jsonviewer.json)이 필요합니다. 문서 뷰어 플러그인의 경우, 이 파일에는 단순히 뷰어 플러그인의 이름만 포함됩니다.
{ "Keys": [ "jsonviewer" ] }JSON 파일에 제공해야 하는 정보의 유형은 플러그인마다 다릅니다. 파일에 포함되어야 하는 정보에 대한 자세한 내용은 클래스 문서를 참조하십시오.
데이터베이스 드라이버, 이미지 형식, 텍스트 코덱 및 대부분의 다른 플러그인 유형의 경우, 명시적인 객체 생성이 필요하지 않습니다. Qt가 필요에 따라 이를 찾아 생성합니다.
플러그인 클래스의 경우 추가 함수를 구현해야 할 수도 있습니다. 각 플러그인 유형별로 재구현해야 하는 가상 함수에 대한 자세한 내용은 클래스 문서를 참조하십시오.
‘문서 뷰어 데모’는 파일의 구조화된 내용을 표시하는 플러그인을 구현하는 방법을 보여줍니다. 따라서 각 플러그인은 가상 함수를 재구현하며, 이는
- 플러그인이
- 지원하는 MIME 유형을 반환하며
- 표시할 콘텐츠가 있는지 여부를 알리고
- 콘텐츠가 어떻게 표시되는지
QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
QStringList supportedMimeTypes() const override;
bool hasContent() const override;
bool supportsOverview() const override { return true; }저수준 API: Qt 애플리케이션 확장하기
Qt 자체 외에도, Qt 애플리케이션은 플러그인을 통해 확장될 수 있습니다. 이를 위해서는 애플리케이션이 ` QPluginLoader`를 사용하여 플러그인을 감지하고 로드해야 합니다. 이러한 맥락에서 플러그인은 임의의 기능을 제공할 수 있으며, 데이터베이스 드라이버, 이미지 형식, 텍스트 코덱, 스타일 및 Qt의 기능을 확장하는 기타 유형의 플러그인에 국한되지 않습니다.
플러그인을 통해 애플리케이션을 확장 가능하게 만드는 과정은 다음 단계를 포함합니다:
- 플러그인과 통신하는 데 사용되는 일련의 인터페이스(순수 가상 함수만 포함된 클래스)를 정의합니다.
- Q_DECLARE_INTERFACE() 매크로를 사용하여 Qt의 메타 객체 시스템에 해당 인터페이스를 알립니다.
- 애플리케이션 내에서 ` QPluginLoader `를 사용하여 플러그인을 로드합니다.
- qobject_cast()을 사용하여 플러그인이 주어진 인터페이스를 구현하는지 테스트합니다.
플러그인을 작성하는 과정은 다음과 같은 단계로 이루어집니다:
- QObject 를 상속하고, 플러그인이 제공하고자 하는 인터페이스들을 상속하는 플러그인 클래스를 선언합니다.
- Q_INTERFACES() 매크로를 사용하여 Qt의 메타 객체 시스템에 해당 인터페이스들을 알립니다.
- Q_PLUGIN_METADATA() 매크로를 사용하여 플러그인을 내보냅니다.
예를 들어, 다음은 인터페이스 클래스의 정의입니다:
class ViewerInterface : public AbstractViewer
{
public:
virtual ~ViewerInterface() = default;
};다음은 인터페이스 선언입니다:
#define ViewerInterface_iid "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0"
Q_DECLARE_INTERFACE(ViewerInterface, ViewerInterface_iid)Qt Widgets Designer 에 특화된 문제에 대한 정보는 ‘ Qt Widgets Designer 용 사용자 정의 위젯 만들기’ 문서를 참조하십시오. 플러그인을 사용하여 사용자 정의 캘린더 시스템을 지원하는 예제는 ‘캘린더 백엔드 플러그인 예제’를 참조하십시오.
플러그인 찾기
플러그인은 표준 플러그인 하위 디렉터리에 저장되므로, Qt 애플리케이션은 사용 가능한 플러그인을 자동으로 인식합니다. 이로 인해 Qt가 플러그인을 자동으로 처리하므로, 애플리케이션에서 플러그인을 찾고 로드하기 위한 별도의 코드가 필요하지 않습니다.
개발 중에는 플러그인 디렉터리가 ` QTDIR/plugins `(여기서 ` QTDIR `는 Qt가 설치된 디렉터리)이며, 각 플러그인 유형은 해당 유형의 하위 디렉터리에 위치합니다. 예를 들어, ` styles`와 같습니다. 애플리케이션에서 플러그인을 사용하되 표준 플러그인 경로를 사용하지 않으려면, 설치 과정에서 플러그인에 사용할 경로를 지정하고, 예를 들어 QSettings 를 사용하여 해당 경로를 저장해 두면 애플리케이션이 실행될 때 이 경로를 읽을 수 있습니다. 그러면 애플리케이션에서 이 경로를 사용하여 ` QCoreApplication::addLibraryPath()`를 호출하면, 해당 플러그인을 애플리케이션에서 사용할 수 있게 됩니다. 단, 경로의 마지막 부분(예: styles)은 변경할 수 없다는 점에 유의하십시오.
플러그인을 로드 가능하게 하려면, 애플리케이션 아래에 하위 디렉터리를 생성하고 해당 디렉터리에 플러그인을 배치하는 방법이 있습니다. Qt와 함께 제공되는 플러그인( plugins 디렉터리에 있는 플러그인)을 배포하는 경우, plugins 아래에 있는 플러그인이 위치한 하위 디렉터리를 애플리케이션의 루트 폴더로 복사해야 합니다(즉, plugins 디렉터리는 포함하지 마십시오).
배포에 대한 자세한 내용은 ‘Qt 애플리케이션 배포’ 및 ‘플러그인 배포’ 문서를 참조하십시오.
정적 플러그인
애플리케이션에 플러그인을 포함시키는 일반적이고 가장 유연한 방법은, 플러그인을 별도로 배포되는 동적 라이브러리로 컴파일한 다음, 실행 시에 이를 감지하여 로드하는 것입니다.
플러그인을 애플리케이션에 정적으로 링크할 수도 있습니다. Qt의 정적 버전을 빌드하는 경우, Qt의 사전 정의된 플러그인을 포함하는 유일한 방법은 바로 이 방법입니다. 정적 플러그인을 사용하면 배포 시 오류가 발생할 가능성이 줄어들지만, 애플리케이션을 완전히 재빌드하고 재배포하지 않고서는 플러그인의 기능을 추가할 수 없다는 단점이 있습니다.
CMake와 qmake는 사용되는 Qt 모듈에 일반적으로 필요한 플러그인을 자동으로 추가하지만, 보다 특수한 플러그인은 수동으로 추가해야 합니다. 자동으로 추가되는 플러그인의 기본 목록은 유형별로 재정의할 수 있습니다.
기본 설정은 바로 사용할 수 있는 최적의 환경을 위해 조정되어 있지만, 애플리케이션의 크기를 불필요하게 부풀릴 수 있습니다. 링커 명령줄을 검토하여 불필요한 플러그인을 제거하는 것이 좋습니다.
정적 플러그인이 실제로 링크되고 인스턴스화되도록 하려면 애플리케이션 코드에 ` Q_IMPORT_PLUGIN()` 매크로도 필요하지만, 이러한 매크로는 빌드 시스템에 의해 자동으로 생성되어 애플리케이션 프로젝트에 추가됩니다.
CMake에서 정적 플러그인 가져오기
CMake 프로젝트에서 플러그인을 정적으로 링크하려면 qt_import_plugins() 명령을 호출해야 합니다.
예를 들어, Linux용 libinput 플러그인은 기본적으로 임포트되지 않습니다. 다음 명령을 실행하면 해당 플러그인을 임포트할 수 있습니다:
qt_import_plugins(myapp INCLUDE Qt::QLibInputPlugin)기본 Qt Platform Adaptation 플러그인 대신 최소 플랫폼 통합 플러그인을 링크하려면 다음을 사용하십시오:
qt_import_plugins(myapp
INCLUDE_BY_TYPE platforms Qt::MinimalIntegrationPlugin
)또 다른 일반적인 사용 사례는 특정 imageformats 플러그인 세트만 링크하는 것입니다:
qt_import_plugins(myapp
INCLUDE_BY_TYPE imageformats Qt::QJpegPlugin Qt::QGifPlugin
)imageformats 플러그인의 연결을 모두 차단하려면 다음을 사용하십시오:
qt_import_plugins(myapp
EXCLUDE_BY_TYPE imageformats
)기본 플러그인의 추가를 비활성화하려면 qt_import_plugins()의 NO_DEFAULT 옵션을 사용하십시오.
qmake에서 정적 플러그인 가져오기
qmake 프로젝트에서는 QTPLUGIN 를 사용하여 빌드에 필요한 플러그인을 추가해야 합니다:
QTPLUGIN += qlibinputplugin예를 들어, 기본 Qt Platform Adaptation 플러그인 대신 minimal 플러그인을 링크하려면 다음을 사용합니다:
QTPLUGIN.platforms = qminimal기본 QPA 플러그인이나 최소 QPA 플러그인 모두 자동으로 링크되지 않게 하려면 다음을 사용하십시오:
QTPLUGIN.platforms = -QTPLUGIN에 추가된 모든 플러그인이 자동으로 연결되는 것을 원하지 않는다면, CONFIG 변수에서 ` import_plugins `를 제거하십시오:
CONFIG -= import_plugins정적 플러그인 생성
다음 단계를 따라 사용자 정의 정적 플러그인을 생성할 수도 있습니다:
CMakeLists.txt파일에서 qt_add_plugin() 명령에STATIC옵션을 전달하십시오. qmake 프로젝트의 경우, 플러그인의.pro파일에CONFIG += static를 추가하십시오.- 애플리케이션에서 Q_IMPORT_PLUGIN() 매크로를 사용하십시오.
- 플러그인이 qrc 파일을 포함하는 경우, 애플리케이션에서 Q_INIT_RESOURCE() 매크로를 사용하십시오.
CMakeLists.txt파일의 target_link_libraries 또는.pro파일의LIBS을 사용하여 애플리케이션을 플러그인 라이브러리와 링크하십시오.
구체적인 방법에 대한 자세한 내용은 Plug & Paint 예제와 관련 Basic Tools 플러그인을 참조하십시오.
참고: CMake나 qmake를 사용하지 않고 플러그인을 빌드하는경우 , QT_STATICPLUGIN 전처리기 매크로가 정의되어 있는지 확인해야 합니다.
플러그인 불러오기
플러그인 유형(정적 또는 공유)과 운영 체제에 따라 플러그인을 찾아서 로드하는 데 필요한 접근 방식이 다릅니다. 플러그인 로드를 위한 추상화 계층을 구현하는 것이 유용합니다.
void ViewerFactory::loadViewerPlugins()
{
if (!m_viewers.isEmpty())
return;QPluginLoader::staticInstances()는 각 정적 링크된 플러그인에 대한 포인터를 포함하는 ` QObjectList `를 반환합니다.
// Load static plugins
const QObjectList &staticPlugins = QPluginLoader::staticInstances();
for (auto *plugin : staticPlugins)
addViewer(plugin);공유 플러그인은 배포 디렉터리에 위치하며, 이 경우 OS별 처리가 필요할 수 있습니다.
// 공유 플러그인 불러오기
QDir pluginsDir = QDir(QApplication::applicationDirPath());
#if defined(Q_OS_DARWIN)
if (pluginsDir.exists("../PlugIns"_L1)) { // 설치된 빌드
pluginsDir.cd("../PlugIns"_L1);
} else {
pluginsDir.cd("../../../../plugins"_L1); // 설치되지 않은 빌드
}
#elif defined(Q_OS_WIN)
if (pluginsDir.exists("plugins"_L1)) { // 설치되지 않은 빌드
pluginsDir.cd("plugins"_L1);
} else {
pluginsDir.cd("../plugins"_L1); // 설치된 빌드
}
#else
pluginsDir.cd("../plugins"_L1); // 설치된 빌드 및 설치되지 않은 빌드
#endif
// qDebug("%s에서 플러그인 불러오기 중...", qUtf8Printable(pluginsDir.path()));
const auto entryList = pluginsDir.entryList(QDir::Files);
for (const QString&fileName: entryList) {
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
QObject*plugin = loader.instance();
if (plugin)
addViewer(plugin);
#if 0
else
qDebug() << loader.errorString();
#endif
}
}플러그인 배포 및 디버깅
'플러그인 배포' 문서는 애플리케이션과 함께 플러그인을 배포하는 과정과 문제가 발생했을 때 이를 디버깅하는 방법을 다룹니다.
QPluginLoader 및 QLibrary도 참조하십시오 .
© 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.