QtCreator源码分析(三)——QtCreator源码结构分析

一、QtCreator源码目录简介

QtCreator-2.8.1包含5000多个文件,代码行数超过了110万行。

源码目录如下:

 bin: 生成Linux平台shell脚本。

dist: 安装文件配置信息和版本更新记录。

doc: 生成doxygen文档的配置文件。

lib:Qt组件相关的QML文件

qbs:QBS 配置文件。QBS,即 Qt Build Suite,是一种跨平台的编译工具,目的是将高层的项目描述(使用类似 QML 的语言)转换成底层的编译描述(供 make 等工具使用的信息)。它可以简化多平台的编译过程。QBS 与 qmake 类似,区别在于前者适用于任意项目,而后者一般仅供 Qt 项目使用。我们在阅读代码时将关注 qmake,不会深入研究 QBS 的使用。

scripts: QtCreator使用的perl以及python等脚本。

share: 源代码中所需要的一些非代码共享文件,例如代码模板等。

src: QtCreator源代码文件。

tests: QtCreator测试代码。

HACKING: QtCreator编码规范。

LICENSE.LGPL: LGPL协议。

qtcreator.pri: QtCreator项目需要使用的通用配置,一般会被include到大部分 pro文件。

qtcreator.pro: QtCreator的qmake项目文件。

qtcreator.qbs:  Qt Creator 的 QBS 项目文件。

README: 有关如何编译QtCreator等相关事宜的一些说明。

.gitignore: git忽略文件配置。

.gitmodules:git 子模块配置

二、QtCreator源码目录结构分析

Qt工程源码通常从qtcreator.pro文件开始阅读。

 1、qtcreator.pro 

include(qtcreator.pri)
#qtcreator.pri 中定义了很多函数和适用于各个模块的通用操作
#使用include操作符将其引入一个pro文件,qmake 会自动处理引用操作

#version check qt
!minQtVersion(4, 8, 0) {
    message("Cannot build Qt Creator with Qt version $${QT_VERSION}.")
    error("Use at least Qt 4.8.0.")
}

#判断Qt的版本。minQtVersion()是在qtcreator.pri中定义的函数,
#Qt版本低于4.8.0时执行块中语句
#message()是qmake预定义的函数,可以在控制台输出一段文本
#$${QT_VERSION}是占位符,会使用QT_VERSION变量的内容进行替换。
include(doc/doc.pri)

TEMPLATE  = subdirs
#TEMPLATE即代码模板,将告诉qmake要怎么生成最后的文件,可选值如下:
#app:创建用于构建可执行文件的Makefile。
#lib:创建用于构建库的 Makefile。
#subdirs:创建依次构建子目录中文件的Makefile。子目录使用SUBDIRS变量指定。
#aux:创建不构建任何东西的Makefile。如果构建目标不需要编译器,就可以使用
#例如,项目使用的是解释型语言。此时生成的Makefile仅适用于基Makefile的生成器
#不一定能供vcxproj或Xcode使用。
#vcapp:仅适用于Windows平台,用于生成VS应用程序项目。
#vclib:仅适用于Windows平台,用于生成VS库项目。
CONFIG   += ordered

#按照SUBDIRS书写顺序来编译
#虽然将源代码分为不同目录,但是目录之间是存在依赖关系的。
#比如,一个基础类库要被其它所有模块使用,在编译时,基础类库应该首先被编译。

SUBDIRS = src share lib/qtcreator/qtcomponents

#按照顺序,应该是先编译src,然后编译share,最后lib/qtcreator/qtcomponents
unix:!macx:!isEmpty(copydata):SUBDIRS += bin
#对于Unix平台(unix),如果不是Mac OS(!macx),
#并且copydata不为空(!isEmpty(copydata)),则需要再增加一个bin目录
!isEmpty(BUILD_TESTS):SUBDIRS += tests
#如果BUILD_TESTS不为空(!isEmpty(BUILD_TESTS)),则增加一个tests目录
OTHER_FILES += dist/copyright_template.txt \
    $$files(dist/changes-*) \
    qtcreator.qbs \
    qbs/pluginspec/pluginspec.qbs \
    $$files(dist/installer/ifw/config/config-*) \
    dist/installer/ifw/packages/org.qtproject.qtcreator/meta/package.xml.in \
    dist/installer/ifw/packages/org.qtproject.qtcreator.application/meta/installscript.qs \
    dist/installer/ifw/packages/org.qtproject.qtcreator.application/meta/package.xml.in \
    dist/installer/ifw/packages/org.qtproject.qtcreator.application/meta/license.txt \
    $$files(scripts/*.py) \
    $$files(scripts/*.sh) \
    $$files(scripts/*.pl)
#需要在最终的目标包括的文件

qmake_cache = $$targetPath($$IDE_BUILD_TREE/.qmake.cache)
!equals(QMAKE_HOST.os, Windows) {
    maybe_quote = "\""
    maybe_backslash = "\\"
}

#如果操作系统时Windows,双引号为",反斜线为\

system("echo $${maybe_quote}$${LITERAL_HASH} config for qmake$${maybe_quote} > $$qmake_cache")
# Make sure the qbs dll ends up alongside the Creator executable.
exists(src/shared/qbs/qbs.pro) {
    system("echo QBS_DLLDESTDIR = $${IDE_BUILD_TREE}/bin >> $$qmake_cache")
    system("echo QBS_DESTDIR = $${maybe_backslash}\"$${IDE_LIBRARY_PATH}$${maybe_backslash}\" >> $$qmake_cache")
    system("echo QBSLIBDIR = $${maybe_backslash}\"$${IDE_LIBRARY_PATH}$${maybe_backslash}\" >> $$qmake_cache")
    system("echo QBS_INSTALL_PREFIX = $${QTC_PREFIX} >> $$qmake_cache")
    system("echo QBS_LIB_INSTALL_DIR = $${QTC_PREFIX}/$${IDE_LIBRARY_BASENAME}/qtcreator >> $$qmake_cache")
    system("echo QBS_RESOURCES_BUILD_DIR = $${maybe_backslash}\"$${IDE_DATA_PATH}/qbs$${maybe_backslash}\" >> $$qmake_cache")
    system("echo QBS_RESOURCES_INSTALL_DIR = $${QTC_PREFIX}/share/qtcreator/qbs >> $$qmake_cache")
    system("echo CONFIG += qbs_no_dev_install >> $$qmake_cache")
}
_QMAKE_CACHE_ = $$qmake_cache # Qt 4 support prevents us from using cache(), so tell Qt 5 about the cache

contains(QT_ARCH, i386): ARCHITECTURE = x86
else: ARCHITECTURE = $$QT_ARCH
#如果QT_ARCH中有i386,则将ARCHITECTURE赋值为x86,否则就是$$QT_ARCH
macx: PLATFORM = "mac"
else:win32: PLATFORM = "windows"
else:linux-*: PLATFORM = "linux-$${ARCHITECTURE}"
else: PLATFORM = "unknown"
#根据操作系统定义了一个新的宏PLATFORM

PATTERN = $${PLATFORM}$(INSTALL_EDITION)-$${QTCREATOR_VERSION}$(INSTALL_POSTFIX)

macx:INSTALLER_NAME = "qt-creator-$${QTCREATOR_VERSION}"
else:INSTALLER_NAME = "qt-creator-$${PATTERN}"
#根据操作系统平台,定义新的宏INSTALLER_NAME
macx {
    APPBUNDLE = "$$OUT_PWD/bin/Qt Creator.app"
    BINDIST_SOURCE = "$$OUT_PWD/bin/Qt Creator.app"
    BINDIST_INSTALLER_SOURCE = $$BINDIST_SOURCE
    deployqt.commands = $$PWD/scripts/deployqtHelper_mac.sh \"$${APPBUNDLE}\" \"$$[QT_INSTALL_TRANSLATIONS]\" \"$$[QT_INSTALL_PLUGINS]\" \"$$[QT_INSTALL_IMPORTS]\" \"$$[QT_INSTALL_QML]\"

    codesign.commands = codesign -s \"$(SIGNING_IDENTITY)\" $(SIGNING_FLAGS) \"$${APPBUNDLE}\"
    dmg.commands = $$PWD/scripts/makedmg.sh $$OUT_PWD/bin qt-creator-$${PATTERN}.dmg
    dmg.depends = deployqt
    QMAKE_EXTRA_TARGETS += codesign dmg
#$$OUT_PWD是qmake生成的Makefile所在的文件夹
} else {
    BINDIST_SOURCE = "$(INSTALL_ROOT)$$QTC_PREFIX"
    BINDIST_INSTALLER_SOURCE = "$$BINDIST_SOURCE/*"
    deployqt.commands = $$PWD/scripts/deployqt.py -i \"$(INSTALL_ROOT)$$QTC_PREFIX\"
    deployqt.depends = install
    win32 {
        deployartifacts.depends = install
        deployartifacts.commands = git clone "git://gitorious.org/qt-creator/binary-artifacts.git" -b $$BINARY_ARTIFACTS_BRANCH&& xcopy /s /q /y /i "binary-artifacts\\win32" \"$(INSTALL_ROOT)$$QTC_PREFIX\"&& rmdir /s /q binary-artifacts

        QMAKE_EXTRA_TARGETS += deployartifacts
    }
}

INSTALLER_ARCHIVE_FROM_ENV = $$(INSTALLER_ARCHIVE)
isEmpty(INSTALLER_ARCHIVE_FROM_ENV) {
    INSTALLER_ARCHIVE = $$OUT_PWD/qt-creator-$${PATTERN}-installer-archive.7z
} else {
    INSTALLER_ARCHIVE = $$OUT_PWD/$$(INSTALLER_ARCHIVE)
}


bindist.depends = deployqt
bindist.commands = 7z a -mx9 $$OUT_PWD/qt-creator-$${PATTERN}.7z \"$$BINDIST_SOURCE\"
bindist_installer.depends = deployqt
bindist_installer.commands = 7z a -mx9 $${INSTALLER_ARCHIVE} \"$$BINDIST_INSTALLER_SOURCE\"
installer.depends = bindist_installer
installer.commands = $$PWD/scripts/packageIfw.py -i \"$(IFW_PATH)\" -v $${QTCREATOR_VERSION} -a \"$${INSTALLER_ARCHIVE}\" "$$INSTALLER_NAME"

macx {
    # this should be very temporary:
    MENU_NIB = $$(MENU_NIB_FILE)
    isEmpty(MENU_NIB): MENU_NIB = "FATAT_SET_MENU_NIB_FILE_ENV"
    copy_menu_nib_installer.commands = cp -R \"$$MENU_NIB\" \"$${INSTALLER_NAME}.app/Contents/Resources\"

    codesign_installer.commands = codesign -s \"$(SIGNING_IDENTITY)\" $(SIGNING_FLAGS) \"$${INSTALLER_NAME}.app\"
    dmg_installer.commands = hdiutil create -srcfolder "$${INSTALLER_NAME}.app" -volname \"Qt Creator\" -format UDBZ "qt-creator-$${PATTERN}-installer.dmg" -ov -scrub -stretch 2g
    QMAKE_EXTRA_TARGETS += codesign_installer dmg_installer copy_menu_nib_installer
}


win32 {
    deployqt.commands ~= s,/,\\\\,g
    bindist.commands ~= s,/,\\\\,g
    bindist_installer.commands ~= s,/,\\\\,g
    installer.commands ~= s,/,\\\\,g
#~=运算符将符合正则表达式的内容替换为后面的部分

#s,/,\\\\,g表示将输入字符串中的/全部替换为\

}


QMAKE_EXTRA_TARGETS += deployqt bindist bindist_installer installer
#将目标都添加到QMAKE_EXTRA_TARGETS

2、qtcreator.pri 

!isEmpty(QTCREATOR_PRI_INCLUDED):error("qtcreator.pri already included")
QTCREATOR_PRI_INCLUDED = 1
#如果存在QTCREATOR_PRI_INCLUDED,抛出错误,防止QTCREATOR_PRI_INCLUDED被多次引入
QTCREATOR_VERSION = 2.8.1
# Qt Creator的版本
BINARY_ARTIFACTS_BRANCH = 2.8
#指定git的分支
isEqual(QT_MAJOR_VERSION, 5) {
#Qt版本高于5时,定义替换函数cleanPath
defineReplace(cleanPath) {
    return($$clean_path($$1))
}

#定义替换函数使用的语句是defineReplace,其参数是函数名字
defineReplace(targetPath) {
    return($$shell_path($$1))
}

} else { # qt5
#Qt版本低于5时,定义替换函数cleanPath
defineReplace(cleanPath) {
    win32:1 ~= s|\\\\|/|g
    contains(1, ^/.*):pfx = /
    else:pfx =
    segs = $$split(1, /)
    out =
    for(seg, segs) {
        equals(seg, ..):out = $$member(out, 0, -2)
        else:!equals(seg, .):out += $$seg
    }
    return($$join(out, /, $$pfx))
}

defineReplace(targetPath) {
    return($$replace(1, /, $$QMAKE_DIR_SEP))
}

} # qt5

defineReplace(qtLibraryName) {
   unset(LIBRARY_NAME)
#取消LIBRARY_NAME定义
   LIBRARY_NAME = $$1
#定义LIBRARY_NAME并使用qtLibraryName函数第一个参数赋值
   CONFIG(debug, debug|release) {
      !debug_and_release|build_pass {
          mac:RET = $$member(LIBRARY_NAME, 0)_debug
              else:win32:RET = $$member(LIBRARY_NAME, 0)d
      }
   }
   isEmpty(RET):RET = $$LIBRARY_NAME
   return($$RET)
}

defineTest(minQtVersion) {
#定义测试函数使用的语句是defineTest,其参数是函数名字minQtVersion
    maj = $$1
    min = $$2
    patch = $$3
    isEqual(QT_MAJOR_VERSION, $$maj) {
        isEqual(QT_MINOR_VERSION, $$min) {
            isEqual(QT_PATCH_VERSION, $$patch) {
                return(true)
            }
            greaterThan(QT_PATCH_VERSION, $$patch) {
                return(true)
            }
        }
        greaterThan(QT_MINOR_VERSION, $$min) {
            return(true)
        }
    }
    greaterThan(QT_MAJOR_VERSION, $$maj) {
        return(true)
    }

    return(false)
}

isEqual(QT_MAJOR_VERSION, 5) {

# For use in custom compilers which just copy files
defineReplace(stripSrcDir) {
    return($$relative_path($$absolute_path($$1, $$OUT_PWD), $$_PRO_FILE_PWD_)
}

} else { # qt5

# For use in custom compilers which just copy files
win32:i_flag = i
defineReplace(stripSrcDir) {
    win32 {
        !contains(1, ^.:.*):1 = $$OUT_PWD/$$1
    } else {
        !contains(1, ^/.*):1 = $$OUT_PWD/$$1
    }
    out = $$cleanPath($$1)
    out ~= s|^$$re_escape($$_PRO_FILE_PWD_/)||$$i_flag
    return($$out)
}

#OUT_PWD表示生成的makefile所在目录
#_PRO_FILE_PWD_表示.pro文件所在目录
} # qt5

!isEmpty(BUILD_TESTS):TEST = 1

isEmpty(TEST):CONFIG(debug, debug|release) {
    !debug_and_release|build_pass {
        TEST = 1
    }
}

isEmpty(IDE_LIBRARY_BASENAME) {
    IDE_LIBRARY_BASENAME = lib
}

equals(TEST, 1) {
    QT +=testlib
    DEFINES += WITH_TESTS
}

IDE_SOURCE_TREE = $$PWD
#源码代码所在目录,根据.pro文件或.pri文件的不同位置而有所不同
isEmpty(IDE_BUILD_TREE) {
    sub_dir = $$_PRO_FILE_PWD_
    sub_dir ~= s,^$$re_escape($$PWD),,
#参数string中出现的所有正则表达式中的保留字进行转义
    IDE_BUILD_TREE = $$cleanPath($$OUT_PWD)
    IDE_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,
#IDE_BUILD_TREE输出根目录

}

#构建路径
IDE_APP_PATH = $$IDE_BUILD_TREE/bin
#最终输出的二进制文件的位置
macx {
    IDE_APP_TARGET   = "Qt Creator"
    IDE_LIBRARY_PATH = $$IDE_APP_PATH/$${IDE_APP_TARGET}.app/Contents/PlugIns
    IDE_PLUGIN_PATH  = $$IDE_LIBRARY_PATH
    IDE_LIBEXEC_PATH = $$IDE_APP_PATH/$${IDE_APP_TARGET}.app/Contents/Resources
    IDE_DATA_PATH    = $$IDE_APP_PATH/$${IDE_APP_TARGET}.app/Contents/Resources
    IDE_DOC_PATH     = $$IDE_DATA_PATH/doc
    IDE_BIN_PATH     = $$IDE_APP_PATH/$${IDE_APP_TARGET}.app/Contents/MacOS
    copydata = 1
    isEmpty(TIGER_COMPAT_MODE):TIGER_COMPAT_MODE=$$(QTC_TIGER_COMPAT)
    !isEqual(QT_MAJOR_VERSION, 5) {
        # Qt5 doesn't support 10.5, and will set the minimum version correctly to 10.6 or 10.7.
        isEmpty(TIGER_COMPAT_MODE) {
            QMAKE_CXXFLAGS *= -mmacosx-version-min=10.5
            QMAKE_LFLAGS *= -mmacosx-version-min=10.5
        }
    }
} else {
    contains(TEMPLATE, vc.*):vcproj = 1
    IDE_APP_TARGET   = qtcreator
    IDE_LIBRARY_PATH = $$IDE_BUILD_TREE/$$IDE_LIBRARY_BASENAME/qtcreator
    IDE_PLUGIN_PATH  = $$IDE_LIBRARY_PATH/plugins
    IDE_LIBEXEC_PATH = $$IDE_APP_PATH # FIXME
    IDE_DATA_PATH    = $$IDE_BUILD_TREE/share/qtcreator
    IDE_DOC_PATH     = $$IDE_BUILD_TREE/share/doc/qtcreator
    IDE_BIN_PATH     = $$IDE_APP_PATH
    !isEqual(IDE_SOURCE_TREE, $$IDE_BUILD_TREE):copydata = 1
}

INCLUDEPATH += \
    $$IDE_BUILD_TREE/src \ # for <app/app_version.h>
    $$IDE_SOURCE_TREE/src/libs \
    $$IDE_SOURCE_TREE/tools \
    $$IDE_SOURCE_TREE/src/plugins
#INCLUDEPATH给出了头文件检索目录,在头文件包含中不必写全路径
CONFIG += depend_includepath

LIBS += -L$$IDE_LIBRARY_PATH
#LIBS是qmake连接第三方库的配置。-L指定了第三方库所在的目录;
#-l指定了第三方库的名字。如果没有-l,则会连接-L指定的目录中所有的库。
!isEmpty(vcproj) {
    DEFINES += IDE_LIBRARY_BASENAME=\"$$IDE_LIBRARY_BASENAME\"
} else {
    DEFINES += IDE_LIBRARY_BASENAME=\\\"$$IDE_LIBRARY_BASENAME\\\"
}

DEFINES += QT_CREATOR QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
!macx:DEFINES += QT_USE_FAST_OPERATOR_PLUS QT_USE_FAST_CONCATENATION

unix {
    CONFIG(debug, debug|release):OBJECTS_DIR = $${OUT_PWD}/.obj/debug-shared
    CONFIG(release, debug|release):OBJECTS_DIR = $${OUT_PWD}/.obj/release-shared

    CONFIG(debug, debug|release):MOC_DIR = $${OUT_PWD}/.moc/debug-shared
    CONFIG(release, debug|release):MOC_DIR = $${OUT_PWD}/.moc/release-shared

    RCC_DIR = $${OUT_PWD}/.rcc
    UI_DIR = $${OUT_PWD}/.uic
}

win32-msvc* { 
    #Don't warn about sprintf, fopen etc being 'unsafe'
    DEFINES += _CRT_SECURE_NO_WARNINGS
}

qt:greaterThan(QT_MAJOR_VERSION, 4) {
    contains(QT, core): QT += concurrent
    contains(QT, gui): QT += widgets
    DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x040900
}

QBSFILE = $$replace(_PRO_FILE_, \\.pro$, .qbs)
exists($$QBSFILE):OTHER_FILES += $$QBSFILE

# recursively resolve plugin deps
done_plugins =
for(ever) {
    isEmpty(QTC_PLUGIN_DEPENDS): \
        break()
#默认QTC_PLUGIN_DEPENDS没有设置,跳出循环
    done_plugins += $$QTC_PLUGIN_DEPENDS
    for(dep, QTC_PLUGIN_DEPENDS) {
        include($$PWD/src/plugins/$$dep/$${dep}_dependencies.pri)
        LIBS += -l$$qtLibraryName($$QTC_PLUGIN_NAME)
    }

    QTC_PLUGIN_DEPENDS = $$unique(QTC_PLUGIN_DEPENDS)
    QTC_PLUGIN_DEPENDS -= $$unique(done_plugins)
}

#遍历处理插件依赖,允许用户在编译时直接通过QTC_PLUGIN_DEPENDS指定插件依赖。
#如果没有,则根据每个插件自己的依赖处理。

# recursively resolve library deps
done_libs =
for(ever) {
    isEmpty(QTC_LIB_DEPENDS): \
        break()
    done_libs += $$QTC_LIB_DEPENDS
    for(dep, QTC_LIB_DEPENDS) {
        include($$PWD/src/libs/$$dep/$${dep}_dependencies.pri)
        LIBS += -l$$qtLibraryName($$QTC_LIB_NAME)
    }

    QTC_LIB_DEPENDS = $$unique(QTC_LIB_DEPENDS)
    QTC_LIB_DEPENDS -= $$unique(done_libs)
}
#遍历处理库依赖

3、src.pro

Qtcreator.pro文件中SUBDIRS定义了编译的顺序,因此先看src目录。

SUBDIRS = src share lib/qtcreator/qtcomponents

   src.pro内容如下:

TEMPLATE  = subdirs
CONFIG   += ordered

SUBDIRS = \
    libs \
    app \
    plugins \
    tools

4、libs.pro

libs.pro内容如下:

TEMPLATE  = subdirs

SUBDIRS   = \
    aggregation \
    extensionsystem \
    utils \
    languageutils \
    cplusplus \
    qmljs \
    qmldebug \
    qmleditorwidgets \
    glsl \
    ssh \
    zeroconf

for(l, SUBDIRS) {
    QTC_LIB_DEPENDS =
    include($$l/$${l}_dependencies.pri)
    lv = $${l}.depends
    $$lv = $$QTC_LIB_DEPENDS
}

#遍历SUBDIRS中的目录项,QTC_LIB_DEPENDS赋值为$$l/$${l}_dependencies.pri输出值
#l为aggregation,QTC_LIB_DEPENDS是include(aggregation/aggregation_dependencies.pri)输出值
SUBDIRS += \
    utils/process_stub.pro \
    qtcomponents/styleitem

exists(../shared/qbs/qbs.pro):SUBDIRS += \
    ../shared/qbs/src/lib \
    ../shared/qbs/src/plugins \
    ../shared/qbs/static.pro

win32:SUBDIRS += utils/process_ctrlc_stub.pro

# Windows: Compile Qt Creator CDB extension if Debugging tools can be detected.    
win32 {
    include(qtcreatorcdbext/cdb_detect.pri)
    exists($$CDB_PATH):SUBDIRS += qtcreatorcdbext
}

5、aggregation.pro

aggregation.pro内容如下:

include(../../qtcreatorlibrary.pri)


DEFINES += AGGREGATION_LIBRARY

HEADERS = aggregate.h \
    aggregation_global.h

SOURCES = aggregate.cpp

qtcreatorlibrary.pri内容如下:

include($$replace(_PRO_FILE_PWD_, ([^/]+$), \\1/\\1_dependencies.pri))
#qt-creator/src/libs/aggregation/aggregation_dependencies.pri
TARGET = $$QTC_LIB_NAME
#QTC_LIB_NAME在aggregation_dependencies.pri中定义,值为Aggregation
include(../qtcreator.pri)
#qtcreator.pri
# use precompiled header for libraries by default
isEmpty(PRECOMPILED_HEADER):PRECOMPILED_HEADER = $$PWD/shared/qtcreator_pch.h
win32 {
    DLLDESTDIR = $$IDE_APP_PATH
}

DESTDIR = $$IDE_LIBRARY_PATH
include(rpath.pri)
TARGET = $$qtLibraryName($$TARGET)
TEMPLATE = lib
CONFIG += shared dll
contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols
!macx {
    win32 {
        dlltarget.path = $$QTC_PREFIX/bin
        INSTALLS += dlltarget
    } else {
        target.path = $$QTC_PREFIX/$$IDE_LIBRARY_BASENAME/qtcreator
        INSTALLS += target
    }
}

Aggregation类分析:

Aggregation::Aggregate将多个相关组件定义为一个聚合体,从而使外界可以将多个组件视为一个整体。类似于一个类通过C++多继承实现多个接口。

class Aggregation : public Interface1, public Interface2, public Interface3
{

}

虽然C++支持多继承,但多继承通常会带来一些问题,所以一般会避免使用。Aggregate仅限于接口,从而规避了多继承的问题。Aggregate内部的组件可以是任意QObject的子类。Aggregate可以实现:

Aggregate内部的每个组件都可以相互“转换”

Aggregate内部的每个组件的生命周期都被绑定在一起,例如,其中任意一个被析构,那么,剩余的所有组件也就会被析构

Aggregate的使用示例如下:

class Interface1 : public QObject
{

};

class Interface2 : public QObject
{  

};

Interface1* objetct1 = new Interface1;
Interface2* objetct2 = new Interface2;
Aggregate* aggregate = new Aggregate;
//Aggregate将objetct1、objetct2捆绑为一个聚合体
aggregate->add(objetct1);
aggregate->add(objetct2);
//删除任意一个都会销毁聚合体
delete objetct1;//同时析构objetct2

   聚合实例aggregate现在有两个接口的实现。如果需要转换成相应接口

Interface1* iface1Ptr = Aggregation::query<Interface1>(&aggregate);
Interface2* iface2Ptr = Aggregation::query<Interface2>(&aggregate);

Aggregate实现如下:

Aggregation.h文件:

#ifndef AGGREGATE_H
#define AGGREGATE_H

#include "aggregation_global.h"

#include <QObject>
#include <QList>
#include <QHash>
#include <QReadWriteLock>
#include <QReadLocker>

namespace Aggregation {

class AGGREGATION_EXPORT Aggregate : public QObject
{
    Q_OBJECT
public:
    Aggregate(QObject *parent = 0);
    virtual ~Aggregate();
    void add(QObject *component);
    void remove(QObject *component);
    //将组件转化为一个聚合体对象
    template <typename T> T *component() {
        QReadLocker(&lock());
        //遍历属性中的组件连表
        foreach (QObject *component, m_components) {
            //如果属性连表中存在可以转换为本聚合体的组件
            if (T *result = qobject_cast<T *>(component))
                return result;
        }

        return (T *)0;
    }

    //将多个组件转换为聚合体连表
    template <typename T> QList<T *> components() {
        QReadLocker(&lock());
        QList<T *> results;
        foreach (QObject *component, m_components) {
            if (T *result = qobject_cast<T *>(component)) {
                results << result;
            }
        }
        return results;
    }

    static Aggregate *parentAggregate(QObject *obj);
    static QReadWriteLock &lock();
signals:
    void changed();
private slots:
    void deleteSelf(QObject *obj);
private:
    //聚合体注册管理器函数
    static QHash<QObject *, Aggregate *> &aggregateMap();
    //聚合体属性连表,存储聚合体包含的组件
    QList<QObject *> m_components;
};

// get a component via global template function
template <typename T> T *query(Aggregate *obj)
{
    if (!obj)
        return (T *)0;
    return obj->template component<T>();
}

template <typename T> T *query(QObject *obj)
{
    if (!obj)
        return (T *)0;
    T *result = qobject_cast<T *>(obj);
    if (!result) {
        QReadLocker(&lock());
        Aggregate *parentAggregation = Aggregate::parentAggregate(obj);
        result = (parentAggregation ? query<T>(parentAggregation) : 0);
    }

    return result;
}

// get all components of a specific type via template function
template <typename T> QList<T *> query_all(Aggregate *obj)
{
    if (!obj)
        return QList<T *>();
    return obj->template components<T>();
}

template <typename T> QList<T *> query_all(QObject *obj)
{
    if (!obj)
        return QList<T *>();
    QReadLocker(&lock());
    Aggregate *parentAggregation = Aggregate::parentAggregate(obj);
    QList<T *> results;
    if (parentAggregation)
        results = query_all<T>(parentAggregation);
    else if (T *result = qobject_cast<T *>(obj))
        results.append(result);
    return results;
}

} // namespace Aggregation

#endif // AGGREGATE_H

Aggregation.cpp文件:

#include "aggregate.h"


#include <QWriteLocker>
#include <QDebug>
using namespace Aggregation;

Aggregate *Aggregate::parentAggregate(QObject *obj)
{
    QReadLocker locker(&lock());
    return aggregateMap().value(obj);
}

QHash<QObject *, Aggregate *> &Aggregate::aggregateMap()
{
    static QHash<QObject *, Aggregate *> map;
    return map;
}

QReadWriteLock &Aggregate::lock()
{
    static QReadWriteLock lock;
    return lock;
}

Aggregate::Aggregate(QObject *parent)
    : QObject(parent)
{
    QWriteLocker locker(&lock());
    //Aggregate本身向Aggregate聚合体中注册
    aggregateMap().insert(this, this);
}

Aggregate::~Aggregate()
{
    QList<QObject *> components;
    {
        QWriteLocker locker(&lock());
        //遍历m_components包含的组件
        foreach (QObject *component, m_components) {
            //断开destroyed与槽函数deleteSelf的关联
            disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*)));
            //将组件注销
            aggregateMap().remove(component);
        }
        components = m_components;
        //删除属性中所以组件对象
        m_components.clear();
        //将聚合体自身注销
        aggregateMap().remove(this);
    }

    //析构所有组件
    qDeleteAll(components);
}

//destroyed信号发出时会被调用
void Aggregate::deleteSelf(QObject *obj)
{
    {
        QWriteLocker locker(&lock());
        //注销obj
        aggregateMap().remove(obj);
        //从属性中删除
        m_components.removeAll(obj);
    }

    //析构聚合体本身
    delete this;
}

//add()函数用于向Aggregate中添加组件
void Aggregate::add(QObject *component)
{
    if (!component)
        return;
    {
        QWriteLocker locker(&lock());
        //查阅component是否已经向Aggregate聚合体注册
        Aggregate *parentAggregation = aggregateMap().value(component);
        if (parentAggregation == this)
            return;//如果component已经注册,并且在Aggregate聚合体自身注册,返回

        //如果component已经在其他Aggregate聚合体注册
        if (parentAggregation) {
            qWarning() << "Cannot add a component that belongs to a different aggregate" << component;
            return;
        }

        //如果component没有被注册添加到任何聚合体,添加到m_components属性中
        m_components.append(component);
        //连接destroyed信号到deleteSelf槽函数
        connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*)));
        //将component注册到本聚合体中
        aggregateMap().insert(component, this);
    }
    emit changed();
}



void Aggregate::remove(QObject *component)
{
    if (!component)
        return;
    {
        QWriteLocker locker(&lock());
        //将组件注销
        aggregateMap().remove(component);
        //从属性中删除组件
        m_components.removeAll(component);
        //断开destroyed信号与槽deleteSelf连接
        disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(deleteSelf(QObject*)));
    }

    emit changed();
}

6、extensionsystem.pro

Qt Creator 的核心是一个插件系统,插件系统由extensionsystem模块实现。

   extensionsystem.pro内容如下:

DEFINES += EXTENSIONSYSTEM_LIBRARY
include(../../qtcreatorlibrary.pri)

unix:!macx:!freebsd*:LIBS += -ldl

!isEmpty(vcproj) {
    DEFINES += IDE_TEST_DIR=\"$$IDE_SOURCE_TREE\"
} else {
    DEFINES += IDE_TEST_DIR=\\\"$$IDE_SOURCE_TREE\\\"
}

HEADERS += pluginerrorview.h \
    plugindetailsview.h \
    invoker.h \
    iplugin.h \
    iplugin_p.h \
    extensionsystem_global.h \
    pluginmanager.h \
    pluginmanager_p.h \
    pluginspec.h \
    pluginspec_p.h \
    pluginview.h \
    optionsparser.h \
    plugincollection.h \
    pluginerroroverview.h

SOURCES += pluginerrorview.cpp \
    plugindetailsview.cpp \
    invoker.cpp \
    iplugin.cpp \
    pluginmanager.cpp \
    pluginspec.cpp \
    pluginview.cpp \
    optionsparser.cpp \
    plugincollection.cpp \
    pluginerroroverview.cpp

FORMS += pluginview.ui \
    pluginerrorview.ui \
    plugindetailsview.ui \
    pluginerroroverview.ui

RESOURCES += pluginview.qrc

extensionsystem模块中有四个带有图形界面的类,分别是PluginView、PluginErrorView、PluginDetailsView和PluginErrorOverview,分贝表示有关插件的信息、错误信息、详细列表以及错误概览。

QtCreator在源代码中使用了大量D-Pointer设计,为了解决解决二进制兼容的问题。

要使一个库能够实现二进制兼容,就要求每一个结构以及每一个对象的数据模型保持不变。所谓“数据模型保持不变”,就是不能在类中增加、删除数据成员。这是C++编译器要求的,其根本是保持数据成员在数据模型中的位移保持不变。

ExtensionSystem::PluginSpec类

PluginSpec类是最基础的类,代表QtCreator的一个插件。

QtCreator中每一个插件都是一个动态链接库,QtCreator可以指定插件是否启用,即是否允许在QtCreator启动时加载插件以及更多的操作。QtCreator将插件动态链接库对应的物理文件抽象为ExtensionSystem::PluginSpec类。PluginSpec类更多的是提供给整个插件系统关于插件的信息,包括插件的名字、当前状态等。其中,名字作为静态数据是嵌入到插件文件中的,作为插件的“元数据”;当前状态则是标记插件当前是出于加载中,还是已经加载完毕,是保存在内存中的动态数据。同时,如果插件出现了任务错误,其详细的错误信息都是在PluginSpec类中。

在QtCreator中,每一个插件会对应着一个XML文件,用于描述插件的元信息、插件的依赖等。

PluginSpec类采用了D-Pointer设计,有三个文件:pluginspec.h、pluginspec_p.h 和 pluginspec.cpp。

pluginspec.h文件:

#ifndef PLUGINSPEC_H
#define PLUGINSPEC_H

#include "extensionsystem_global.h"
//定义向外暴露的宏
#include <QString>
#include <QList>
#include <QHash>

QT_BEGIN_NAMESPACE
class QStringList;
QT_END_NAMESPACE

namespace ExtensionSystem {
//扩展系统的命名空间
namespace Internal {
//扩展系统内部实现的命名空间,不能在自定义插件中使用
    class PluginSpecPrivate;
    class PluginManagerPrivate;
}

class IPlugin;
//可以被外部使用的类
//PluginDependency定义了有关被依赖插件的信息,包括被依赖插件的名字以及版本号等
//开发人员使用PluginDependency定义所需要的依赖,QtCreator则根据定义的依赖,
//利用Qt的反射机制,通过名字和版本号获取到插件对应的状态,
//从而获知被依赖插件是否加载之类的信息。
//QtCreator在匹配版本号时,并不会直接按照给出的version值完全匹配,
//而是按照一定的算法,选择一段区间内兼容的版本。
//这样做的目的是,有些插件升级了版本号之后,另外的插件可以按照版本号兼容,不需要一同升级。
struct EXTENSIONSYSTEM_EXPORT PluginDependency
{
    enum Type { //依赖类型枚举
        Required,   //必须的依赖
        Optional    //可选依赖
    };

    PluginDependency() : type(Required) {}
    QString name;   //被依赖插件名字
    QString version;    //被依赖插件版本号
    Type type;  //依赖类型
    bool operator==(const PluginDependency &other) const;
};

uint qHash(const ExtensionSystem::PluginDependency &value);

//用于描述插件参数。QtCreator的插件可以在启动时提供额外的参数
struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
{
    QString name;
    QString parameter;
    QString description;
};


class EXTENSIONSYSTEM_EXPORT PluginSpec
{
public:
    //指示插件加载时的状态的枚举
    enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
    //Invalid:起始点:任何信息都没有读取,甚至连插件元数据都没有读到
    //Read:成功读取插件元数据,并且元数据是合法的;此时,插件的相关信息已经可用
    //Resolved:插件描述文件中给出的各个依赖已经被成功找到,这些依赖可以通过dependencySpecs()函数获取
    //Loaded:插件的库已经加载,插件实例成功创建;此时插件实例可以通过plugin()函数获取
    //Initialized:调用插件实例的IPlugin::initialize()函数,并且该函数返回成功
    //Running:插件的依赖成功初始化,并且调用了extensionsInitialized()函数;此时,加载过程完毕
    //Stopped:插件已经停止,插件的IPlugin::aboutToShutdown()函数被调用
    //Deleted:插件实例被删除销毁
    ~PluginSpec();

    // information from the xml file, valid after 'Read' state is reached
    QString name() const;//插件名字
    QString version() const;//插件版本
    QString compatVersion() const;//插件兼容版本
    QString vendor() const;//插件提供者
    QString copyright() const;//插件版权
    QString license() const;//插件协议
    QString description() const;//插件描述
    QString url() const;//插件主页

    QString category() const;//插件类别,用于在界面分组显示插件信息
    bool isExperimental() const;//是否实验性质的插件
    bool isDisabledByDefault() const;//是否默认禁用
    bool isEnabledInSettings() const;//因配置信息启动
    bool isEffectivelyEnabled() const;//是否在启动时已经加载
    bool isDisabledIndirectly() const;
    bool isForceEnabled() const;//是否通过命令行参数 -load 加载
    bool isForceDisabled() const;//是否通过命令行参数 -noload 禁用
    QList<PluginDependency> dependencies() const;//插件依赖列表,状态Read可用

    typedef QList<PluginArgumentDescription> PluginArgumentDescriptions;
    PluginArgumentDescriptions argumentDescriptions() const;//插件处理的命令行参数描述符列表

    // other information, valid after 'Read' state is reached
    QString location() const;// PluginSpec实例对应的插件XML描述文件所在目录的绝对位置
    QString filePath() const;//PluginSpec实例对应的插件XML描述文件的绝对路径

    void setEnabled(bool value);
    void setDisabledByDefault(bool value);
    void setDisabledIndirectly(bool value);
    void setForceEnabled(bool value);
    void setForceDisabled(bool value);

    QStringList arguments() const;//插件命令行参数。启动时设置
    //设置插件命令行参数为 arguments
    void setArguments(const QStringList &arguments);
    //将 argument 添加到插件的命令行参数
    void addArgument(const QString &argument);
    //当一个依赖需要插件名为pluginName、版本为version时,返回该插件是否满足
    bool provides(const QString &pluginName, const QString &version) const;

    // dependency specs, valid after 'Resolved' state is reached
    //插件的依赖。当状态达到 PluginSpec::Resolved 时才可用
    QHash<PluginDependency, PluginSpec *> dependencySpecs() const;

    // linked plugin instance, valid after 'Loaded' state is reached
    //PluginSpec实例对应的IPlugin实例。当状态达到PluginSpec::Loaded时才可用
    IPlugin *plugin() const;
    // state
    State state() const;//当前状态
    bool hasError() const;//是否发生错误
    QString errorString() const;//错误信息
private:
    PluginSpec();//构造函数私有,只能在友元类中创建对象
    Internal::PluginSpecPrivate *d;//d指针,指向私类对象
    friend class Internal::PluginManagerPrivate;
    friend class Internal::PluginSpecPrivate;
};

} // namespace ExtensionSystem

#endif // PLUGINSPEC_H

pluginspec_p.h文件:

#ifndef PLUGINSPEC_P_H
#define PLUGINSPEC_P_H

#include "pluginspec.h"
#include "iplugin.h"

#include <QObject>
#include <QStringList>
#include <QXmlStreamReader>

namespace ExtensionSystem {
class IPlugin;
class PluginManager;

namespace Internal {

class EXTENSIONSYSTEM_EXPORT PluginSpecPrivate : public QObject
{
    Q_OBJECT
public:
    PluginSpecPrivate(PluginSpec *spec);
    bool read(const QString &fileName);
    bool provides(const QString &pluginName, const QString &version) const;
    bool resolveDependencies(const QList<PluginSpec *> &specs);
    bool loadLibrary();
    bool initializePlugin();
    bool initializeExtensions();
    bool delayedInitialize();
    IPlugin::ShutdownFlag stop();
    void kill();
    QString name;
    QString version;
    QString compatVersion;
    bool experimental;
    bool disabledByDefault;
    QString vendor;
    QString copyright;
    QString license;
    QString description;
    QString url;
    QString category;
    QList<PluginDependency> dependencies;
    bool enabledInSettings;
    bool disabledIndirectly;
    bool forceEnabled;
    bool forceDisabled;

    QString location;
    QString filePath;
    QStringList arguments;

    QHash<PluginDependency, PluginSpec *> dependencySpecs;
    PluginSpec::PluginArgumentDescriptions argumentDescriptions;
    IPlugin *plugin;
    PluginSpec::State state;
    bool hasError;
    QString errorString;

    static bool isValidVersion(const QString &version);
    static int versionCompare(const QString &version1, const QString &version2);
    void disableIndirectlyIfDependencyDisabled();
private:

    PluginSpec *q;//q指针,指向公类对象
    bool reportError(const QString &err);
    void readPluginSpec(QXmlStreamReader &reader);
    void readDependencies(QXmlStreamReader &reader);
    void readDependencyEntry(QXmlStreamReader &reader);
    void readArgumentDescriptions(QXmlStreamReader &reader);
    void readArgumentDescription(QXmlStreamReader &reader);
    bool readBooleanValue(QXmlStreamReader &reader, const char *key);
    static QRegExp &versionRegExp();
};

} // namespace Internal

} // namespace ExtensionSystem

#endif // PLUGINSPEC_P_H

IPlugin类

IPlugin类是所有插件的基类,每个插件都必须继承IPlugin抽象类,并实现其中的纯虚函数。QtCreator的插件包含两部分:一个描述文件和一个至少包含了IPlugin实现的库文件。在库文件中,插件实现的IPlugin类需要与描述文件中的name属性相匹配。对于IPlugin抽象接口的实现需要使用标准的Qt插件系统暴露给外界,使用Q_PLUGIN_METADATA宏,并且给定 IID 为org.qt-project.Qt.QtCreatorPlugin。

Q_PLUGIN_METADATA宏是 Qt 5.0 新引入的,用于声明插件的元数据。当实例化插件对象时,插件的元数据会作为插件对象的一部分。Q_PLUGIN_METADATA宏需要声明一个 IID 属性,用于标识对象实现的接口;还需要一个文件的引用,文件包含了插件的元数据。任何一个 Qt 插件都需要在源代码中添加这么一个宏,并且只允许添加一个。

class CorePlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Core.json")
};

class GitPlugin : public VcsBase::VcsBasePlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Git.json")
};

使用Q_PLUGIN_METADATA宏的类必须有无参数的构造函数。FILE是可选的,指向一个 JSON 文件,必须能够被构建系统找到。

Qt Creator 规定其插件的 IID 必须是 org.qt-project.Qt.QtCreatorPlugin。而IID 所代表的,就是IPlugin。FILE根据每个插件而定。

IPlugin提供了一系列回调函数,用于插件加载到一定阶段时进行回调。

当插件描述文件读取、并且所有依赖都满足后,插件将开始进行加载。插件加载分为三个阶段:

A、所有插件的库文件按照依赖树从根到叶子的顺序进行加载。

B、按照依赖树从根到叶子的顺序依次调用每个插件的IPlugin::initialize()函数。

virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;

每个插件都必须实现initialize函数。initialize函数会在插件加载完成,并且创建了插件对应的IPlugin对象之后调用。initialize函数返回值是bool类型,当插件初始化成功,返回true;否则,需要在errorString参数中设置人可读的错误信息。initialize函数的调用顺序是依赖树从根到叶子,因此,所有依赖本插件的插件的initialize()函数,都会在本插件自己的initialize()函数之后被调用。如果插件需要共享一些对象,就应该将共享对象放在initialize函数中。

C、按照依赖树从叶子到根的顺序依次调用每个插件的IPlugin::extensionsInitialized()函数

virtual void extensionsInitialized() = 0;

extensionsInitialized函数会在IPlugin::initialize()调用完毕、并且自己所依赖插件的IPlugin::initialize()和IPlugin::extensionsInitialized()调用完毕之后被调用。在extensionsInitialized函数中,插件可以假设自己所依赖的插件已经成功加载并且正常运行。插件所依赖的各个插件提供的可被共享的对象都已经创建完毕(在IPlugin::initialize()函数中完成的),可以正常使用。

如果插件的库文件加载失败,或者插件初始化失败,所有依赖该插件的插件都会失败。

virtual bool delayedInitialize() { return false; }

delayedInitialize函数会在所有插件的IPlugin::extensionsInitialized()函数调用完成、同时所依赖插件的IPlugin::delayedInitialize()函数也调用完成后才被调用,即延迟初始化。插件的IPlugin::delayedInitialize()函数会在程序运行之后才被调用,并且距离程序启动有几个毫秒的间隔。为避免不必要的延迟,插件对delayedInitialize函数的实现应该尽快返回。delayedInitialize函数的意义在于,有些插件可能需要进行一些重要的启动工作,虽然不必在启动时直接完成,但也应该在程序启动后的较短时间内完成。delayedInitialize函数默认返回false,即不需要延迟初始化。如果插件有这类需求,就可以重写delayedInitialize函数。

virtual ShutdownFlag aboutToShutdown() { return SynchronousShutdown; }

aboutToShutdown()函数会以插件初始化的相反顺序调用。aboutToShutdown函数应该用于与其它插件断开连接、隐藏所有 UI、优化关闭操作。如果插件需要延迟真正的关闭,例如,需要等待外部进程执行完毕,以便自己完全关闭,则应该返回IPlugin::AsynchronousShutdown。这么做的话会进入主事件循环,等待所有返回了IPlugin::AsynchronousShutdown的插件都发出了asynchronousShutdownFinished()信号之后,再执行相关操作。aboutToShutdown函数默认实现是不作任何操作,直接返回IPlugin::SynchronousShutdown,即不等待其它插件关闭.

可被共享的对象。插件之间的依赖容易解决。比如,插件 B 依赖于插件 A,那么,如果 B 成功加载,可以肯定,A一定是存在的。那么,就可以在 B中使用 A 提供的各个函数。但是,还有另外一种情况:插件B扩展插件 A 的功能。例如,不同的插件提供了打开不同文件的功能:插件 A 提供打开文本文件的功能;插件 B 提供打开二进制文件的功能。那么,当程序启动时,程序怎么知道自己能打开哪些文件呢?因为在程序启动时,并不知道哪些插件是可用的。QtCreator提供了一个对象池。A 可以打开文本,B 可以打开二进制文件,那么,把能打开文件的类实例放到对象池里面。程序启动之后,自己去对象池找,看有哪些关于打开文件的对象,就知道自己能打开什么文件了。

对象池可以理解为一个全局变量,里面会有很多不同插件提供的对象,其它插件只需要从这里面找到自己需要的对象即可使用。这种设计避免了插件要暴露很多接口的情况。如果没有对象池,那么,插件 A 和 B 就需要主动注册自己能够打开哪些文件,就要求主程序提供类似的注册接口——进一步说,程序需要将所有可能被扩展的功能都提供注册接口,这无疑会引起接口“爆炸”。

IPlugin将对象池操作委托给PluginManager类。

IPlugin.h文件:

#ifndef IPLUGIN_H
#define IPLUGIN_H

#include "extensionsystem_global.h"

#include <QObject>
#if QT_VERSION >= 0x050000
#    include <QtPlugin>
#endif

namespace ExtensionSystem {

namespace Internal {
    class IPluginPrivate;
    class PluginSpecPrivate;
}

class PluginManager;
class PluginSpec;

class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject
{
    Q_OBJECT
public:
    enum ShutdownFlag {
        SynchronousShutdown,//异步
        AsynchronousShutdown//同步
    };

    IPlugin();
    virtual ~IPlugin();
    //每个子插件都必须实现,initialize函数会在插件加载完成,并且创建了插件对应的IPlugin对象之后调用

    virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
    virtual void extensionsInitialized() = 0;
    virtual bool delayedInitialize() { return false; }
    virtual ShutdownFlag aboutToShutdown() { return SynchronousShutdown; }
    virtual QObject *remoteCommand(const QStringList & /* options */, const QStringList & /* arguments */) { return 0; }
    //相关联的PluginSpec实例的访问
    PluginSpec *pluginSpec() const;
    //添加对象到对象池
    void addObject(QObject *obj);
    //自动析构对象池中的对象
    void addAutoReleasedObject(QObject *obj);
    //删除对象池中对象
    void removeObject(QObject *obj);
signals:
    void asynchronousShutdownFinished();
private:
    Internal::IPluginPrivate *d;
    friend class Internal::PluginSpecPrivate;
};

} // namespace ExtensionSystem

// The macros Q_EXPORT_PLUGIN, Q_EXPORT_PLUGIN2 become obsolete in Qt 5.
#if QT_VERSION >= 0x050000
#    if defined(Q_EXPORT_PLUGIN)
#        undef Q_EXPORT_PLUGIN
#        undef Q_EXPORT_PLUGIN2
#    endif
#    define Q_EXPORT_PLUGIN(plugin)
#    define Q_EXPORT_PLUGIN2(function, plugin)
#else
#    define Q_PLUGIN_METADATA(x)
#endif


#endif // IPLUGIN_H

IPlugin.cpp文件:

#include "iplugin.h"
#include "iplugin_p.h"
#include "pluginmanager.h"
#include "pluginspec.h"

using namespace ExtensionSystem;

IPlugin::IPlugin()
    : d(new Internal::IPluginPrivate())
{

}

IPlugin::~IPlugin()
{
    foreach (QObject *obj, d->addedObjectsInReverseOrder)
        PluginManager::removeObject(obj);
    qDeleteAll(d->addedObjectsInReverseOrder);
    d->addedObjectsInReverseOrder.clear();
    delete d;
    d = 0;
}

PluginSpec *IPlugin::pluginSpec() const
{
    return d->pluginSpec;
}

void IPlugin::addObject(QObject *obj)
{
    PluginManager::addObject(obj);
}


void IPlugin::addAutoReleasedObject(QObject *obj)
{
    d->addedObjectsInReverseOrder.prepend(obj);
    PluginManager::addObject(obj);
}

void IPlugin::removeObject(QObject *obj)
{
    PluginManager::removeObject(obj);
}

   iplugin_p.h文件:

#include "iplugin.h"

#include <QString>

namespace ExtensionSystem {

class PluginManager;
class PluginSpec;

namespace Internal {

class IPluginPrivate
{
public:
    PluginSpec *pluginSpec;
    QList<QObject *> addedObjectsInReverseOrder;
};

} // namespace Internal

} // namespace ExtensionSystem

#endif // IPLUGIN_P_H

猜你喜欢

转载自blog.csdn.net/A642960662/article/details/124158961