本文討論Qt4.8的插件機制數據庫
插件是一種遵循必定規範的應用程序接口編寫出來的程序,定位於開發實現應用軟件平臺不具有的功能的程序。app
Qt提供了兩種API用於建立插件:一種是高階API,用於擴展Qt自己的功能,如自定義數據庫驅動,圖像格式,文本編碼,自定義樣式等;一種是低階API,用於擴展Qt應用程序。ide
A、定義一個接口集(只有純虛函數的類),用來與插件交流。
B、用宏Q_DECLARE_INTERFACE()將該接口告訴Qt元對象系統。
C、應用程序中用QPluginLoader來加載插件。
D、用宏qobject_cast()來判斷一個插件是否實現了接口。函數
建立一個插件的步驟以下:
A、聲明插件類,插件類繼承自QObject和插件實現的接口。
B、用宏Q_INTERFACES()將插件接口告訴Qt元對象系統。
C、用宏Q_EXPORT_PLUGIN2()導出插件類。
D、用適當的.pro文件構建插件。
在加載插件前, QCoreApplication對象必須被初始化。ui
建立工程,選擇「Other Project」->「Subdirs Project」,填寫工程名稱爲PluginApp,選擇保存目錄。
this
在PluginApp工程上右鍵選擇「New Subproject」菜單項,選擇建立一個GUI應用,工程名稱爲MainWindow。
填寫工程應用名稱
填寫主界面類的名稱:
在MainWindow應用增長一個接口Echonterface.h。編碼
#ifndef ECHOINTERFACE_H #define ECHOINTERFACE_H #include <QString> //定義接口 class EchoInterface { public: virtual ~EchoInterface() {} virtual QString echo(const QString &message) = 0; }; #define EchoInterface_iid "Examples.Plugin.EchoInterface" QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid) QT_END_NAMESPACE #endif // ECHOINTERFACE_H
在PluginApp工程上右鍵選擇「New Subproject」菜單項,選擇建立一個空的Qt工程,名稱爲EchoPlugin。
EchoPlugin.pro工程文件內容以下:插件
TEMPLATE = lib CONFIG += plugin QT += widgets INCLUDEPATH += ../MainWindow TARGET = $$qtLibraryTarget(echoplugin) DESTDIR = ../plugins
在插件子工程中添加一個插件類EchoPlugin,實現以下:
EchoPlugin.h文件:debug
#ifndef ECHOPLUGIN_H #define ECHOPLUGIN_H #include <QObject> #include <QtPlugin> #include "EchoInterface.h" class EchoPlugin : public QObject, public EchoInterface { Q_OBJECT Q_INTERFACES(EchoInterface) public: explicit EchoPlugin(QObject *parent = 0); QString echo(const QString &message); }; #endif // ECHOPLUGIN_H
EchoPlugin.cpp文件:3d
#include "EchoPlugin.h" EchoPlugin::EchoPlugin(QObject *parent) : QObject(parent) { } QString EchoPlugin::echo(const QString &message) { return message; } Q_EXPORT_PLUGIN2(EchoPlugin, EchoPlugin);
實現MainWindow主界面
Widget.h文件:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include "EchoInterface.h" QT_BEGIN_NAMESPACE class QString; class QLineEdit; class QLabel; class QPushButton; class QGridLayout; QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private slots: void sendEcho(); private: void createGUI(); //加載插件 bool loadPlugin(); EchoInterface *echoInterface; QLineEdit *lineEdit; QLabel *label; QPushButton *button; QGridLayout *layout; }; #endif // WIDGET_H
Widget.cpp文件:
#include "Widget.h" #include <QtGui> Widget::Widget(QWidget *parent) : QWidget(parent) { createGUI(); setLayout(layout); setWindowTitle("Echo Plugin Example"); if (!loadPlugin()) { QMessageBox::information(this, "Error", "Could not load the plugin"); lineEdit->setEnabled(false); button->setEnabled(false); } } void Widget::sendEcho() { QString text = echoInterface->echo(lineEdit->text()); label->setText(text); } void Widget::createGUI() { lineEdit = new QLineEdit; label = new QLabel; label->setFrameStyle(QFrame::Box | QFrame::Plain); button = new QPushButton(tr("Send Message")); connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(sendEcho())); connect(button, SIGNAL(clicked()), this, SLOT(sendEcho())); layout = new QGridLayout; layout->addWidget(new QLabel(tr("Message:")), 0, 0); layout->addWidget(lineEdit, 0, 1); layout->addWidget(new QLabel(tr("Answer:")), 1, 0); layout->addWidget(label, 1, 1); layout->addWidget(button, 2, 1, Qt::AlignRight); layout->setSizeConstraint(QLayout::SetFixedSize); } bool Widget::loadPlugin() { bool ret = true; //獲取當前應用程序所在路徑 QDir pluginsDir(qApp->applicationDirPath()); #if defined(Q_OS_WIN) if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") pluginsDir.cdUp(); #elif defined(Q_OS_MAC) if (pluginsDir.dirName() == "MacOS") { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); } #elif defined(Q_OS_LINUX) pluginsDir.cdUp(); #endif //切換到插件目錄 pluginsDir.cd("plugins"); //遍歷plugins目錄下全部文件 foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); QObject *plugin = pluginLoader.instance(); if (plugin) { //插件名稱 QString pluginName = plugin->metaObject()->className(); //對插件初始化 if(pluginName == "EchoPlugin") { echoInterface = qobject_cast<EchoInterface *>(plugin); if (echoInterface) ret = true; break; } else { ret = false; } } } return ret; } Widget::~Widget() { }
Main.cpp文件:
#include "Widget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }
程序運行結果以下:
查看構建目錄,生成的插件文件存放在plugins目錄下:
Qt應用程序將會自動感知可用的插件,由於插件都被存儲在標準的子目錄當中。所以應用程序不須要任何查找或者加載插件的代碼。
在開發過程當中,插件的目錄是QTDIR/plugins(QTDIR是Qt的安裝目錄),每一個類型的插件放在相應類型的目錄下面。若是想要應用程序使用插件,但不想用標準的插件存放路徑,能夠在應用程序的安裝過程當中指定要使用的插件的路徑,可使用QSettings,保存插件路徑,在應用程序運行時讀取配置文件。應用程序能夠經過QCoreApplication::addLibraryPath()函數將指定的插件路徑加載到應用程序中。
使插件可加載的一種方法是在應用程序所在目錄建立一個子目錄,用於存放插件。若是要發佈和Qt一塊兒發佈的插件(存放在plugins目錄)中的任何插件,必須拷貝plugins目錄下的插件子目錄到應用程序的根目錄下。
將一個插件與一個應用程序一塊兒使用的一般和最靈活的方法是將插件編譯成一個動態庫,動態庫能夠獨立轉移,並在運行時被檢測和加載。
插件能夠靜態連接到應用程序。構建Qt的靜態版本是包含Qt的預約義插件的惟一選項。使用靜態插件使部署不易出錯,但缺點是插件中沒有的功能不能在應用程序的徹底重編譯和重發布的狀況下添加。
Qt提供了以下靜態插件:
要靜態連接靜態插件,必須在應用程序中使用Q_IMPORT_PLUGIN宏,同時須要使用QTPLUGIN增長相應的插件到工程中。如:
#include <QtPlugin> Q_IMPORT_PLUGIN(qjpeg) Q_IMPORT_PLUGIN(qgif)
在.pro工程文件中,
QTPLUGIN += qjpeg \ qgif
使用以下步驟能夠建立一個靜態插件:A、在.pro工程文件中增長CONFIG += static B、在應用程序中使用 Q_IMPORT_PLUGIN()宏導入靜態插件C、在應用程序.pro工程文件中使用 LIBS連接應用程序和靜態插件。