Qt 插件路径

来源:互联网 发布:张学友 知乎 编辑:程序博客网 时间:2024/05/22 06:55

Qt Manual 已经专门介绍了Deploying Plugins 的问题。半年前Qt 插件学习(一) 也简单整理了一点路径相关的问题。

可是,一直以来没理清:图片插件、编解码插件、数据库插件... 到底是如何被加载的?

走马观花

如果我们需要打开或保存一个jpg格式的图片,那么需要加载jpg的插件。程序去何处找插件:

表面的答案:

$QTDIR/plugins/imageformats/you_app_path/imageformats/

注:如果你只是想让插件能工作,对其他不感兴趣,直接在你的应用程序所在目录下创建一个 imageformats 目录,把插件放进去就行了。其他插件类推,分别创建相应的子目录即可。

真实答案

 

QStringList QCoreApplication::libraryPaths()

对于图片插件,程序遍历这个字符串列表,将每一个字符串分别和/imageformats 连接后构成新的路径,然后依次尝试加载这些路径下的动态库。

看点代码(有删节)

void QFactoryLoader::update(){    QStringList paths = QCoreApplication::libraryPaths();    for (int i = 0; i < paths.count(); ++i) {        const QString &pluginDir = paths.at(i);        QString path = pluginDir + d->suffix;        QStringList plugins = QDir(path).entryList(QDir::Files);        QLibraryPrivate *library = 0;        for (int j = 0; j < plugins.count(); ++j) {            QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j));             library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());        }     }...

对于图片插件来说,d->suffix 就是/imageformats ,这是通过一个返回值为QFactoryLoader* 的静态 loader() 函数来实现的:

Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,                          (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))

感觉不对?

前面似乎不对啊?不光和我们的表面答案对不上,而且Manual中或其他人的文章都提到还有很多东西,比如:

  • QLibraryInfo::location(QLibraryInfo::PluginsPath )

  • :/qt/etc/qt.conf
  • QT_PLUGIN_PATH
  • ...
  • QCoreApplication::addLibraryPath()
  • QCoreApplication::setLibraryPaths()

除了后面两个直接相关外,其他合libraryPaths似乎没什么联系。你怎么能说 和 QCoreApplication::libraryPaths() 有关呢??!!

尝试用事实说话...

QCoreApplication::libraryPaths()

  • 创建一个空的QStringList
  • 将  QLibraryInfo::location(QLibraryInfo::PluginsPath)   路径加入

  • 确保将应用程序本身所在路径QCoreApplication::applicationFilePath() 加入

  • 将  QT_PLUGIN_PATH 环境变量指定的路径依次加入

源码:

QStringList QCoreApplication::libraryPaths(){    if (!coreappdata()->app_libpaths) {        QStringList *app_libpaths = coreappdata()->app_libpaths = new QStringList;        QString installPathPlugins =  QLibraryInfo::location(QLibraryInfo::PluginsPath);        if (QFile::exists(installPathPlugins)) {            // Make sure we convert from backslashes to slashes.            installPathPlugins = QDir(installPathPlugins).canonicalPath();            if (!app_libpaths->contains(installPathPlugins))                app_libpaths->append(installPathPlugins);        }        // If QCoreApplication is not yet instantiated,        // make sure we add the application path when we construct the QCoreApplication        if (self) self->d_func()->appendApplicationPathToLibraryPaths();        const QByteArray libPathEnv = qgetenv("QT_PLUGIN_PATH");        if (!libPathEnv.isEmpty()) {#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)            QLatin1Char pathSep(';');#else            QLatin1Char pathSep(':');#endif            QStringList paths = QString::fromLatin1(libPathEnv).split(pathSep, QString::SkipEmptyParts);            for (QStringList::const_iterator it = paths.constBegin(); it != paths.constEnd(); ++it) {                QString canonicalPath = QDir(*it).canonicalPath();                if (!canonicalPath.isEmpty()                    && !app_libpaths->contains(canonicalPath)) {                    app_libpaths->append(canonicalPath);                }            }        }...

现在似乎舒服多了,可是QLibraryInfo::location(QLibraryInfo::PluginsPath) 又是怎么来的呢?

QLibraryInfo::location(QLibraryInfo::PluginsPath)

它受两方面影响:

  • 如果有符合条件的 qt.conf 文件,将使用该文件的内容
  • 如果没有这样的 qt.conf 文件,将使用编译Qt时写死的内容

先看后者:

configure

编译Qt的第一步是configure,它将在 src/corelib/global目录下生成 qconfig.h 和 qconfig.cpp 两个文件。

打开 qconfig.cpp 文件(可以看到类似下面的内容):

static const char qt_configure_installation          [11  + 12] = "qt_instdate=2011-06-08";static const char qt_configure_prefix_path_str       [512 + 12] = "qt_prfxpath=E://Qt5//qtbase-build";static const char qt_configure_binaries_path_str     [512 + 12] = "qt_binspath=E://Qt5//qtbase-build//bin";static const char qt_configure_plugins_path_str      [512 + 12] = "qt_plugpath=E://Qt5//qtbase-build//plugins";#define QT_CONFIGURE_BINARIES_PATH qt_configure_binaries_path_str + 12;#define QT_CONFIGURE_PLUGINS_PATH qt_configure_plugins_path_str + 12;

注意一点:宏 QT_CONFIGURE_PLUGINS_PATH 指向   "qt_plugpath=E://Qt5//qtbase-build//plugins";

qt.conf

  • 先看资源文件中有无  :/qt/etc/qt.conf

  • 不存在则检查应用程序所在路径下有无 qt.conf 文件
  • 找到则返回相应的QSettings,否则返回0

 

QSettings *QLibraryInfoPrivate::findConfiguration(){    QString qtconfig = QLatin1String(":/qt/etc/qt.conf");    if (!QFile::exists(qtconfig) && QCoreApplication::instance()) {            {                QDir pwd(QCoreApplication::applicationDirPath());                qtconfig = pwd.filePath(QLatin1String("qt.conf"));            }    }    if (QFile::exists(qtconfig))        return new QSettings(qtconfig, QSettings::IniFormat);    return 0; }

合二为一

直接贴点代码片段,不解释

QStringQLibraryInfo::location(LibraryLocation loc){    QString ret;    if(!QLibraryInfoPrivate::configuration()) {        const char *path = 0;        switch (loc) {...        case PluginsPath:            path = QT_CONFIGURE_PLUGINS_PATH;            break;...        default:            break;        }        if (path)            ret = QString::fromLocal8Bit(path);    } else {        QString key;        QString defaultValue;        switch(loc) {        case PluginsPath:            key = QLatin1String("Plugins");            defaultValue = QLatin1String("plugins");            break;...

cache

为了加快插件的加载和校验,会用QSettings保存一些插件的信息。

看个代码片段:

QFactoryLoader::update(){    QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));    QString regkey = QString::fromLatin1("Qt Factory Cache %1.%2/%3:/%4")                             .arg((QT_VERSION & 0xff0000) >> 16)                             .arg((QT_VERSION & 0xff00) >> 8)                             .arg(QLatin1String(d->iid))                             .arg(fileName);    QStringList reg;    reg << library->lastModified;    reg += keys;    settings.setValue(regkey, reg);

以及

bool QLibraryPrivate::isPlugin(QSettings *settings){...    QString regkey = QString::fromLatin1("Qt Plugin Cache %1.%2.%3/%4")                     .arg((QT_VERSION & 0xff0000) >> 16)                     .arg((QT_VERSION & 0xff00) >> 8)                     .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false"))                     .arg(fileName);...

看看Windows下的注册表内容:

HKCU/Software/Trolltech/OrganizationDefaults/Qt Factory Cache 4.7/com.trolltech.Qt.QImageIOHandlerFactoryInterface:/F:/Qt4/plugins/imageformats/qtjpeg4.dll = 2010-09-29T14:40:30 jpeg jpg

linux下 ~/.config/Trolltech.conf

[Qt Factory Cache 4.7]com.trolltech.Qt.QImageIOHandlerFactoryInterface:/home/Qt4/plugins/imageformats/qtjpeg4.dll = 2011-04-22T17:21:23 jpeg jpg

参考

  • http://doc.qt.nokia.com/latest/deployment-plugins.html

  • http://doc.qt.nokia.com/latest/qt-conf.html

  • http://hi.baidu.com/cyclone/blog/item/0fc7462376f208429822ed1d.html

原创粉丝点击