QtCreator源码分析(三)——QtCreator源码结构分析
一、QtCreator源码目录简介QtCreator-2.8.1包含5000多个文件,代码行数超过了110万行。源码目录如下:bin: 生成Linux平台shell脚本。dist: 安装文件配置信息和版本更新记录。doc: 生成doxygen文档的配置文件。lib:Qt组件相关的QML文件qbs:QBS 配置文件。QBS,即Qt Build Suite,是一种跨平台的编译工具,目的是将高层的项目描
一、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
更多推荐
所有评论(0)