首页 Qt Qt 4 插件开发(4)

Qt 4 插件开发(4)

6 2K

前面我们已经完成了一个插件 Troll,也已经将文件位置放到了合适的位置(通过 .pro 文件的配置)。现在,我们将来完善下main()函数,让我们的小游戏(姑且这么认为吧)能够加载插件。

下面就是完整的main()函数代码:

#include <QtCore>

#include "monsterinterface.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    QDir pluginsDir(QDir(app.applicationDirPath())
                        .canonicalPath()
                        .append(QDir::separator())
                        .append("plugins"));
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        QFileInfo pluginFileInfo(fileName);
        if (pluginFileInfo.completeSuffix() == "dll"
                        || pluginFileInfo.completeSuffix() == "so") {
            QPluginLoader *pluginLoader
                = new QPluginLoader(pluginsDir.absoluteFilePath(fileName), &app);
            QObject *pluginObject = pluginLoader->instance();
            if (pluginObject) {
                MonsterInterface *monster
                    = qobject_cast<MonsterInterface *>(pluginObject);
                qDebug() << monster->name();
            }
        }
    }

    return app.exec();
}

我们将来着重分析下这段代码。在QCoreApplication实例创建之间,我们需要添加我们自己的代码。首先,我们创建一个QDir对象。这个目录将会是我们的程序扫描的插件目录。为了保持程序的可移植性,我们不能使用硬编码设置插件目录。第一,目录的基础路径是app.applicationDirPath(),该目录即可执行文件所在目录。canonicalPath()函数将applicationDirPath()中包含的 .、.. 或者其它一些相对符号全部过滤掉,得到一个规范的路径。然后在后面追加一个QDir::separator(),即路径分隔符(使用这个函数可以获得平台无关的路径分隔符,而不需要是 / 或者 \ 这种);最后再追加 plugins 目录名。

举个例子,如果app.applicationDirPath()返回的是 C:\Demos\GameSystem\.,那么其canonicalPath()返回的是规范的路径,去除 .、.. 以及连接符号(Unix 平台)等,也就是 C:\Demos\GameSystem。在后面追加QDir::separator(),Windows 平台上将返回 \,那么就变成了 C:\Demos\GameSystem\,再追加 plugins,最终得到插件目录 C:\Demos\GameSystem\plugins。由于 exe 文件就是在 C:\Demos\GameSystem 中,这个插件目录就是在 exe 同目录下的 plugins 文件夹中。(回忆一下我们设置的 .pro,就是得到了这种目录结构。)

现在已经获得插件目录,接下来的 for 循环,遍历目录中每一个文件(注意,由于我们的目录结构是,plugins 目录下直接就是插件文件,因此不需要递归遍历其中所有子目录)。首先判断文件后缀名是不是 dll 或者 so(插件在 Windows 平台上后缀名是 dll;类 Unix 平台上是 so)。如果是的话,进行下面的操作:创建一个QPluginLoader对象,提供两个参数,第一个是插件文件名,以绝对地址给出;第二个是 parent 指针,这里就是 app 的指针。在QPluginLoader对象创建完毕,使用其instance()函数可以获得这个插件对象,该函数返回插件实现的那个对象(我们的插件一定是继承QObject的,还记得吗?),其生命周期与整个 loader 相同,也就是说,当你第二次调用instance()的时候,其实返回的是同一个对象。如果创建成功,则使用qobject_cast将其转换成我们的接口对象。这样,便可以调用接口中定义的各个函数。

接下来,运行一下我们的程序吧!如果顺利的话,应该在控制台看到 Troll 这个名字。

然后,我们可以仿照 Troll 再新建一个项目,比如就叫 Argus(阿耳戈斯,百眼巨人)。代码几乎和 Troll 的一样,只是把name()函数的返回值设置为 Argus。最后,将生成的 dll 放在 plugins 目录下,重新运行程序。如果你看到了两个怪物,那就说明我们的插件已经加载进来了!

虽然例子很简单,我们已经由此看出,如果要增加更多的怪物,只需创建更多的插件即可。这就是插件机制扩展应用程序的一般方法。

6 评论

米米 2012年8月2日 - 13:52

无意搜索发现了您的网站。看了不少。受益匪浅。十分感谢。这个系列我也一直在关注。希望这个系列能讲的更深入些。多写一些。呵呵。比如插件国际化什么的。自己研究了下没搞懂。 😀

回复
DevBean 2012年8月2日 - 17:13

插件的主要问题还是在于与主程序的交互。国际化方面,同一般程序没有什么区别,就是用 tr() 就可以了。

回复
米米 2012年8月2日 - 17:15

呵呵。就在刚刚。解决了问题。谢谢DevBean的回复。期待这您的更新

回复
精英王子 2012年8月5日 - 06:12

很好,基本每篇文章我都会看

回复
DevBean 2012年8月5日 - 15:02

感谢支持!

回复
aben 2013年11月28日 - 20:51

看了你的文章,受益匪浅,比自己琢磨快捷很多。过去,自己琢磨,彻夜难眠,但不得其解。感谢

回复

回复 aben 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2