前言
一般情况下,在Qt或者是VS创建一个项目后,只会简单的生成三个Filter过滤器,头文件+源文件+UI设计师文件;
实际项目中的代码是这样子的:
小项目或者是Demo,这样组建项目是没有问题的。但是当项目代码有一定规模,或者一个项目从小项目不断追加形成一个大项目时,就会发现这种构建项目的缺点:无组织无纪律;头文件和源文件以及UI文件都掺杂在一块;毫无层次,毫无模块的划分;这时是不是就需要对这些文件进行分门别类整理,该使用MVC的使用MVC,该对公有工具类或者是算法类独立为静态库或者动态库单独模块就赶紧肝起来;
项目的管理和电脑上文件的管理的思路是一样的,好的文件管理,就是能够清晰文件之间类别,让使用者方便查找和快速查找;有经验的程序员都知道,写代码的时间很短,查找代码和看代码的时间是占了绝大部分时间;代码功能内聚,模块之间低耦合,就是给自己和别人看代码提供了最大的方便。
比如:
在文件的构建和编译过程中,需要对pro和pri文件有所一定的了解;
对项目的分解、整理的目的,除了控制子项目的类型等基本信息,剩下的就是约束子项目的输出文件夹位置、输入库和引用头文件位置、引入第三方库、设置宏控制进行条件编译;
具体操作
创建一个子目录项目,在“其他项目”分类下选择“子目录项目”;
在子目录项目下,添加“新子项目”,只有在子目录项目中,右击才会出现这个选择,其他情况此选项是置灰不可用的;
新增三个子项目,一个是主窗口界面APP(MainWindow),一个是基础类库(静态库Utils),另外一个是ImageReconstructionLib;
总目录下的pro文件内容如下:
TEMPLATE = subdirs
# 不再推荐使用ordered配置;
CONFIG+=ordered
# 我先创建的app,然后创建的Utils和ImageReconstructionLib;
SUBDIRS += \
ImageReconstructionLib \
Utils \
app
TEMPLATE = subdirs
subdirs表示本项目将扫描指定的子目录集合并执行相关操作,常配合SUBDIRS使用。
CONFIG+=ordered
之前项目中多使用ordered,后来不再推荐使用;这里是有问题的,在之后的实践过程中,发现如果不设定ordered,会出现编译报错,库可能在app之后编译;
结构如下:
在子项目的根目录下有pro文件,每个独立的子文件夹内有各自的pri文件;
在项目配置中设置环境变量TRAINDEVHOME指定项目的所在文件夹位置,供pro文件和pri文件使用;
基础类库Utils
pro文件内容:
QT -= gui
include(./Math/Math.pri)
# TARGET变量指定生成的目标库文件的名字,生成应用程序时即指定生成应用程序名
# 可以确保windows下debug模式生成的动态库可以自动加个d
TARGET = $$qtLibraryTarget(Utils)
TEMPLATE = lib
#动态库,如果是静态库CONFIG += staticlib。
CONFIG += staticlib
CONFIG += c++11
SOURCES +=
HEADERS += \
utils_include.h
#配置:项目使用debug和release两种模式构建;
CONFIG += debug_and_release
CONFIG(debug,debug|release) {
message("Utils debug")
# DESTDIR指定目标文件的输出文件路径
DESTDIR = $$PWD/../thirdParty/lib/debug/
} else {
message("Utils release")
DESTDIR = $$PWD/../thirdParty/lib/release/
}
# Default rules for deployment.
unix {
target.path = $$[QT_INSTALL_PLUGINS]/generic
}
!isEmpty(target.path): INSTALLS += target
# 在install拷贝头文件时,需要将每个文件夹下的头文件拷贝到目标文件对应文件夹下;
# 在不同pri文件内,设置files和path的对象不能重名;
Utils_File_Path.files = $$PWD/*.h
Utils_File_Path.path = $$(TRAINDEVHOME)/thirdParty/include/Utils
INSTALLS += Utils_File_Path
DISTFILES += \
include_utils.pri
INSTALLS可以将输出内容拷贝到指定目录,但需要从项目属性设置“Build的步骤”后,才能生效;
编译输出:
使用INSTALL把库内的头文件拷贝到指定文件夹下;
注意:编写路径时,需要使用“/”而不是“\”。有跨行内容时在行末使用“\”进行换行,并在“\”前加一个空格。
库内增加文件夹Math,内部放置常用的算法和函数;
HEADERS += \
$$PWD/base_function.h \
$$PWD/data_define.h \
$$PWD/line2d.h \
$$PWD/point2d.h
SOURCES += \
$$PWD/base_funtion.cpp
message("Math")
message($$PWD)
message($$(TRAINDEVHOME))
#在install拷贝头文件时,需要将每个文件夹下的头文件拷贝到目标文件对应文件夹下;
Math_File_Path.files = $$PWD/*.h
Math_File_Path.path = $$(TRAINDEVHOME)/thirdParty/include/Utils/Math/
INSTALLS += Math_File_Path
在qmake后:
Utils库对外提供一个include_utils.pri文件和一个utils_include .h文件,方便使用Utils库;
include_utils.pri文件:
#加载外部头文件
INCLUDEPATH += $$(TRAINDEVHOME)/thirdParty/include/Utils
#配置加载不同的库文件
CONFIG+=debug_and_release
CONFIG(debug,debug|release) {
message("debug")
LIBS += $$(TRAINDEVHOME)/thirdParty/lib/debug/Utilsd.lib
} else {
message("release")
LIBS += $$(TRAINDEVHOME)/thirdParty/lib/release/Utils.lib
}
utils_include .h文件:
#ifndef UTILS_INCLUDE_H
#define UTILS_INCLUDE_H
// 将对外提供的所有.h头文件都放在这个h文件内;
#include "Math/data_define.h"
#include "Math/base_function.h"
#include "Math/point2d.h"
#include "Math/line2d.h"
#endif // UTILS_INCLUDE_H
ImageReconstructionLib库
同Utils库基本相同,这里把pro文件内容贴一下:
QT -= gui
TARGET = $$qtLibraryTarget(ImageReconstructionLib)
TEMPLATE = lib
# 配置:静态库
CONFIG += staticlib
CONFIG += c++11
SOURCES +=
HEADERS += \
image_reconstruction_include.h
#配置:项目使用debug和release两种模式构建;
CONFIG+=debug_and_release
CONFIG(debug,debug|release) {
message("ImageReconstructionLib debug")
DESTDIR = $$PWD/../thirdParty/lib/debug/
} else {
message("ImageReconstructionLib release")
DESTDIR = $$PWD/../thirdParty/lib/release/
}
# Default rules for deployment.
unix {
target.path = $$[QT_INSTALL_PLUGINS]/generic
}
!isEmpty(target.path): INSTALLS += target
ImageReconstructionLib_Root.files = $$PWD/*.h
ImageReconstructionLib_Root.path = $$PWD/../thirdParty/include/ImageReconstructionLib/
message($$PWD)
INSTALLS += ImageReconstructionLib_Root
message($$INSTALLS)
DISTFILES += \
include_image_reconstruction.pri
app工程
app是对外显示的界面app,用于界面交互和数据显示,调用工具库Utils和图像处理库
app的pro文件内容,默认生成如下内容:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# 引入Utils工具库
include(../Utils/include_utils.pri)
# 引入ImageReconstructionLib图像处理库
include(../ImageReconstructionLib/include_image_reconstruction.pri)
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
注意:在复制操作时,除了使用INSTALL外,还可以使用QMAKE_POST_LINK或者QMAKE_PRE_LINK;在pro配置文件中添加各种编译前后的操作及配置,就是通过 QMAKE_POST_LINK和QMAKE_PRE_LINK;
QMAKE_POST_LINK表示编译后执行内容
QMAKE_PRE_LINK表示编译前执行内容
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS += target
#拷贝dll和lib文件
CONFIG(debug, debug|release) {
compiled = debug
libName = scatterplugind.lib
dllName = scatterplugind.dll
} else {
compiled = release
libName = scatterplugin.lib
dllName = scatterplugin.dll
}
#设置目标输出路径
DESTDIR = $$PWD/bin/$$compiled
# lib的路径
libSourePath = $$DESTDIR/$$libName
libDesPath = $$PWD/../../app/thirdParty/libs/$$libName
message($$libDesPath)
#QT默认安装路径
libQTDesingerPath = $$[QT_INSTALL_PLUGINS]/designer/$$dllName
#dll的路径
dllSourePath = $$DESTDIR/$$dllName
dllDesPath = $$PWD/../../app/$$compiled/$$dllName
#复制操作,复制到指定目录上面,包括指定的QT默认路径下面
QMAKE_POST_LINK += copy $${replace(libSourePath, /, \\)} $${replace(libDesPath, /, \\)}
QMAKE_POST_LINK += && copy $${replace(dllSourePath, /, \\)} $${replace(dllDesPath, /, \\)}
QMAKE_POST_LINK += && copy $${replace(dllSourePath, /, \\)} $${replace(libQTDesingerPath, /, \\)}
至此,算是建立了一个较为清晰的目录结构。我们忽略了指定中间文件等等的各种文件路径的设置,在之后使用的过程中,在逐步增加进来。
遇到的问题
静态库中使用第三方库
当新增静态库DecodeDICOM子项目,使用DCMTK库解析DICOM文件;
编译app,app使用DecodeDICOM库;
提示链接库没有找到:
在app使用的include_decode_dicom.pri文件中增加:
include(./import_dicom.pri)
则可以编译通过;
之前在VS中也遇到过类似情况,使用静态库时,就需要导入静态库使用的第三方库的lib;