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; 
} 
合二为一
直接贴点代码片段,不解释
QString
QLibraryInfo::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
                  
                  
                  
                  
                            
                            
      
          
                
                
                
                
              
                
                
                
                
                
              
                
                
              
            
                  
					2395
					
被折叠的  条评论
		 为什么被折叠?
		 
		 
		
    
  
    
  
            


            