• 14195阅读
  • 8回复

如何编写Qt的Plugin [复制链接]

上一主题 下一主题
离线wvins
 
只看楼主 倒序阅读 楼主  发表于: 2008-11-16
— 本帖被 XChinux 执行加亮操作(2008-11-16) —
继续朝QtRSSReader前进,
今天写了个简单的记XML日志的Qt插件。
从9点摸索到11点,花了两个小时,跌跌撞撞总算搞定了,把这其中曲折记下来,希望大家不要重蹈我的覆辙。

首先需要一个简单的exe调用插件,我的代码如下:
#include <QApplication>
#include <QTextCodec>
#include <QLabel>
#include <QPluginLoader>
#include <QObject>
//---------------------
#include <QMessageBox>
#include "LogInterface.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
    QLabel label("Demo LogInterface");
    label.show();
    QPluginLoader pluginLoader(QObject::tr("/media/工作/个人代码库/练习/QT练习/15.QtPlugin/plugin/liblogWriter.so"));
    QObject *plugin = pluginLoader.instance();
    QMessageBox::information(0, "", QObject::tr("装载成功?%1").arg(pluginLoader.isLoaded()?QObject::tr("装载成功"):pluginLoader.errorString()));
    if (plugin)
    {
        QMessageBox::information(0, "", "plugin != NULL");
        LogInterface * logInterface = qobject_cast<LogInterface *>(plugin);
        if (logInterface)
        {
            QMessageBox::information(0, "", "logInterface != NULL");
            logInterface->writeLog("Demo Message", "Demo Date", "Demo Source",
                          "Demo User", LogInterface::llTrace);
        }
    }
    pluginLoader.unload();
    return app.exec();
}
这个比较简单,主要就是QPluginLoader的使用,
比较关键一点,大概就是isLoad需要在pluginLoader.instance()之后进行判断,
构造的虽然传递了插件的路径,但并不进行Load。进行isLoad判断自然无效了。

接下来是共享的接口文件声明,在C++中就是纯虚类的头文件,代码如下:
#ifndef LOGINTERFACE_H
#define LOGINTERFACE_H
#include <QString>

class LogInterface
{
public:
    enum LogLevel{llTrace, llDebug, llInformation, llError};
    virtual void writeLog(const QString aLogMessage, const QString aLogDate,
                  const QString aLogSource, const QString aLogUser,
                  const LogLevel aLogLevel) = 0;
/*作为接口必须是虚函数,否则会报类似
QLibrary::load_sys: Cannot load /home/foo/src/demo/plugins/libmyPlugin.so (/home/foo/src/demo/plugins/libmyPlugin.so: undefined symbol: _ZTI15PluginInterface)
的错误
*/
};

Q_DECLARE_INTERFACE(LogInterface, "se.thelins.pluginDemo.LogInterface/0.1");
#endif
这里的Q_DECLARE_INTERFACE需要特别留意,开始我不明白什么意思,随编拷贝了一份,把LogInterface替换了。后面才知道这段字符串分成四部分
    公司名或者其他什么东西  ---应该是个描述,不确定
    pluginDemo                    创建该接口的应用,这个名字应该是应用程序的taget名字
    LogInterface                  接口名称
    0.1                          版本号


插件的实现部分着实郁闷了我一把。代码如下:
头文件依葫芦画瓢,比较简单。
#ifndef LOGWRITER_H
#define LOGWRITER_H
#include <QObject>
#include <QString>
#include <QDomDocument>
#include "LogInterface.h"

class LogWriter: public QObject, public LogInterface
{
    Q_OBJECT
    Q_INTERFACES(LogInterface)
public:
    void writeLog(const QString aLogMessage, const QString aLogDate,
              const QString aLogSource, const QString aLogUser,
              const LogLevel aLogLevel);
};

#endif
在这里,我想Q_OBJECT没什么用处吧,主要是为了moc带signal/slot的头文件

实现部分我落下了一个东西,导致后面测试过程中,报无效的插件
#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QtPlugin>
//-------------------------
#include <QMessageBox>
#include "logWriter.h"

void LogWriter::writeLog(const QString aLogMessage, const QString aLogDate,
                const QString aLogSource, const QString aLogUser,
                const LogLevel aLogLevel)
{
    QDomDocument doc;
    QDomElement root, log;
    QFile file("../log.xml");
    if (!file.open(QIODevice::ReadOnly))
    {
        root = doc.createElement("Logs");
        doc.appendChild(root);
    }
    else
    {
        doc.setContent(&file);
        root = doc.documentElement();
    }
    //file.close();
    log = doc.createElement("Log");
    log.setAttribute("Message", aLogMessage);
    log.setAttribute("Date", aLogDate);
    log.setAttribute("Source", aLogSource);
    log.setAttribute("User", aLogUser);
    log.setAttribute("Level", int(aLogLevel));
    root.appendChild(log);
   
    file.seek(0);
    file.write(doc.toByteArray());
    file.close();
}
Q_EXPORT_PLUGIN2(logwriter, LogWriter)//包含在<QtPlugin>中
只能怪我看代码不细致,没有看到代码最后一行有Q_EXPORT_PLUGIN2
--------------------------
代码就是这些,插件的pro文件也需要照样子修改。我的pro文件如下
TEMPLATE = lib
CONFIG += plugin debug#这里我不是很明白CONFIG还能有些什么参数
VERSION = 1.0.0      #照抄,不知道会不会和上面的0.1关联

TARGET = logWriter   #目标
DEPENDPATH += .
INCLUDEPATH += ..    #声明文件在上级目录

# Input
HEADERS += logWriter.h
SOURCES += logWriter.cpp
 
target.path += .     
INSTALLS += target   #不了解,不确定不添加这一段能不能用
主程序源代码 application.tar.gz (3 K) 下载次数:126
插件源代码 plugin.tar.gz (3 K) 下载次数:116
[ 此贴被wvins在2008-11-27 09:56重新编辑 ]
离线wvins
只看该作者 1楼 发表于: 2008-11-16
经过这次练习,我多少加深了点对Qt *.pro文件的了解,好事  

不过可以看得出,我的代码大部分使用的是QMessageBox来调试的,很不方便。
我知道Qt能够通过QtDebug来打开另外一个Console窗口显示调试信息,
还请高手指点一二
离线nmiirq

只看该作者 2楼 发表于: 2008-11-16
pro文件里把console加上,就可以用qDebug来将一些需要调试的信息输出到终端里显示了!
离线wvins
只看该作者 3楼 发表于: 2008-11-16

谢谢!
------
补充一点,在Ubuntu环境下,通过双击程序图标是看不到Console输出的,
需要从Console启动程序才能够看到Console消息。

我看到的Windows环境下是
双击图标同时打开另一个Console窗口。
[ 此贴被wvins在2008-11-16 21:18重新编辑 ]
离线feizhang1987
只看该作者 4楼 发表于: 2011-03-17
同在学习,加油~~!
离线morriszy

只看该作者 5楼 发表于: 2011-03-17
楼主你太好了!学习一下
VS2010+Qt4.7.0
离线justinlue

只看该作者 6楼 发表于: 2011-06-01
学习学习
离线兔子狐狸

只看该作者 7楼 发表于: 2013-01-16
给力  学习了  谢谢分享
离线liulihuogyh

只看该作者 8楼 发表于: 2013-01-20
  一直也不会写插件
快速回复
限100 字节
 
上一个 下一个