在C++、Qt软件开发过程中,常常遇到一些编译错误或警告;本文将VS2019、Qt5.12.10和QGis3.16.10的二次开发过程常见的问题做了整理,供大家参考,也便于日后查阅。该内容分为四部分:VS+Qt环境搭配、VS编译常见问题、Qt常见问题、QGis二次开发常见问题。(转载请注明出处:https://www.cnblogs.com/1024bytes/p/15464404.html)
一、VS创建Qt项目
1、安装VS2019或VS2017,编译器原则上是越新越好(此贴发布时VS2022测试版已经发布,没试过有啥新功能),VS选择需要的组件和编译器,不用全选。
2、Qt用5.12版中最新的,我用的是Qt5.12.10(Qt5.15是向Qt6过渡的产品),组件也是根据需求选择。
3、VS中要安装Qt Visual Studio Tools,菜单栏依次选择“扩展”-“管理扩展”-“联机”-“Visual Studio Marketplace”,在右侧搜索“Qt”,选择“Qt Visual Studio Tools”,点击下载,安装界面弹出后,关闭VS,安装完成。重启VS,可在该菜单栏看到菜单“Qt VS Tools”。在该菜单下选择“Qt Versions”配置Qt路径;创建界面可由此菜单选择“Luanch Qt Designer”。
4、新建项目->Visual C++/跨平台->Qt,选择Qt Widgets Application。
二、VS编译常见问题
Q: “M_PI”: 未声明的标识符
A: 程序中头文件的选择,要选择math.h头文件,在cmath文件中是没有对M_PI 的定义的(现在的cmath中对M_PI好像已有定义)。选择“项目”——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,将“_USE_MATH_DEFINES”添加进去。
Q: 源代码字符编码问题: Warning C4819 该文件包含不能在当前代码页(936)中表示的字符,请将该文件保存为 Unicode 格式以防止数据丢失
A: 光标定位到该文件,选择菜单里的高级保存选项utf-8 with signature,带签名的65001,高级保存选项添加方式见https://www.cnblogs.com/daxueba-ITdaren/p/7272210.html
Q: Warning MSB8004: Output Directory does not end with a trailing slash
A: 设置的目录参数不是以反斜杠结束的目录名称,属性→General→Output Direction里加上反斜杠。
Q: 不允许指针指向不完整的类类型
A: 添加该类类型的头文件,如QStatusBar
Q: LNK1169 找到一个或多个多重定义的符号
A: 多次引用头文件,比如所处位置为.h里,改到.cpp里。哪个文件需要,放在哪个文件里,不需要的不要放在.h里
Q: 引用头文件后遇到无法解析的外部符号
A: link里加入lib,目录要添加lib所在目录
Q: Error LNK2019: unresolved external symbol WinMain referenced in function "int _cdecl invoke_main(void)"
A: 右键点击项目,属性→linker→System,修改subsystem。函数入口为main的,选择console;另一种解决办法见QGis二次开发常见问题。
三、Qt常见问题
Q:无法打开包括文件: “QDomDocument”、“QSvgRenderer”之类的问题: No such file or directory
A:C++的包含目录要包含问题模块所处的路径,如QtXml、QtSvg,打印支持还要添加QtPrintSupport、网络QtNetwork
Q: svg的图片为什么在工具栏不显示、编译成功运行不起来?
A: 复制Qt的plugins目录下的imageformats文件夹到程序运行目录;依赖的Qt的dll也要拷到运行目录里(windeployqt了解一下)。
Q:This applicationfailed to start because no Qt platform plugin could be initialized
A: 复制Qt的plugins目录下的platform文件夹到程序运行目录;依赖的Qt的dll也要拷到运行目录里(windeployqt了解一下)。
Q: 信号SIGNAL和槽SLOT如何连接?
A: 如果一个信号不多个槽相联系的话,那么,当这个信号被发射时,与之相关的槽被激活的顺序将是随机的。当点击菜单时,动作触发了多次,这跟connect有关,手动和内部自动重复。connect的几种写法如下:
QMenu* fileMenu = new QMenu;
fileMenu = this->menuBar()->addMenu("File");
actionOpenFile = new QAction("Open", this);
// connect 方式一:SLOT参数传actionOpenFile的默认触发槽函数on_openFileAction_triggered()
connect(actionOpenFile, SIGNAL(triggered(bool)), this, SLOT(on_openFileAction_triggered()));
/**
* connect 方式二:手动设置object name, 然后再显式调用connectSlotsByName()。
* 若由designer自动创建的连接,代码加了connectSlotsByName则会触发两次,因此不要在代码里再连接信号槽。
* 代码创建的部件,可以自定义槽函数void on_<窗口部件名称>_<信号名称>_(<信号参数>),用下面方法连接
* actionOpenFile->setObjectName("actionOpenFile");
* QMetaObject::connectSlotsByName(this);
**/
QgsLayerTreeViewDefaultActions* actions = QgisDemo::instance()->layerTreeView()->defaultActions();
connect(actions->actionRemoveGroupOrLayer(), SIGNAL(triggered(bool)), QgisDemo::instance(), SLOT(slot_updateAppTile()));//connect用法1
//connect(actions->actionRemoveGroupOrLayer(), &QAction::triggered, QgisDemo::instance(), &QgisDemo::slot_updateAppTile);//connect用法2
Q: 一个信号和两个或多个槽函数连接时,由于先后顺序随机性,如何作调整?
A: 将其中一个槽函数里写入非阻塞的延迟处理,调用processEvents会让Qt继续处理线程所在的消息队列中未处理的消息,直到消息队列中没有消息可以处理。当进行长时间的操作的时候可以调用此函数(比方说拷贝文件)。这个函数可能和我们要使用msleep的本意有差别,但是使用它可以在svalue时间内处理events,从而达到类似sleep的目的。可参考https://www.cnblogs.com/bokeyuan-dlam/articles/6794911.html,代码如下:
Time dieTime = QTime::currentTime().addMSecs(svalue);
while( QTime::currentTime() < dieTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
Q: QwtPlot是qwt6的绘图类,第三方库,如何用?
A: linker里添加qwt.lib,C++包含目录里加入对应的include文件qwt6。
Q: Uic'ing xxx.ui系统找不到指定的路径
A: 问题原因是工程引用了环境变量”QTDIR”,但是用户安装QT后没有配置该环境变量。方法一:在系统环境变量里添加QTDIR,重新启动VS。方法二:右键点击xxx.ui文件,选择属性,将命令行和附加依赖项的uic.exe的路径改成qt目录的绝对路径。注意生成的ui_xxx.h路径,可在C/C++附加目录中添加进去,否则头文件找不到。
四、QGis二次开发常见问题
注意:QGis3.16是基于Qt5.11.2编译的,如果开发用Qt5.12版本,库也需要同样版本重新编译一下。
Q: 若用Qt Creator作二次开发构建,如何配置库引用?
A: 在pro文件中加入以下代码:
INCLUDEPATH += "C:/OSGeo4W64/apps/qgis/include"
INCLUDEPATH += "C:/OSGeo4W64/include"
LIBS += -L"C:/OSGeo4W64/apps/qgis/lib" -lqgis_core -lqgis_gui
LIBS += -L"C:/OSGeo4W64/lib" -lgdal_i
DEFINES += CORE_EXPORT=__declspec(dllimport)
DEFINES += GUI_EXPORT=__declspec(dllimport)
Q: VS提示LNK1104: 无法打开文件“qgis_core.lib”
A: 将依赖库拷到运行目录
Q:VS构建需要的依赖库
A:(1)C/C++常规->附加包含目录:
D:\QGIS\OSGeo4W64\include
D:\QGIS\OSGeo4W64\apps\qgis-ltr\include
D:\Qt5\5.12.10\msvc2017_64\include\QtXml
(2)链接器->常规—>附加库目录:
D:\QGIS\OSGeo4W64\apps\qgis-ltr\lib
D:\Qt5\5.12.10\msvc2017_64\lib
(3)链接器->输入->附加依赖项:qgis_core.lib qgis_gui.lib qgis_analysis.lib(根据需求添加,如果app里需要的部分头文件和源文件拷贝过来的话,qgis_app.lib不需要;否则也要加上)
Q: 2.x版本的一些示例中的类在3.x中找不到头文件
A: 参考官网资料,3.x版本作了一些删改与合并,比如QgsProject替代qgsmaplayerregistry,3.x前后API变动记录详见https://qgis.org/api/api_break.html
Q: 如何取消地图工具状态?
A: 切换回到原来的无地图工具状态,解决思路就有了。先获取到当前的地图工具,然后 unset 掉它,并不设置新的工具,就可以了。代码如下:
QgsMapTool *lastMapTool = m_mapCanvas->mapTool();
m_mapCanvas->unsetMapTool( lastMapTool );
Q: qgis_gui项目中出现未定义的标识符 "QWebElement"
A: 附件目录添加D:\QGIS\OSGeo4W64\apps\Qt5\include\QtWebKit;但是由于Qt5.6起弃用这个类,变换了新类,因此要么从源码解决这个问题,要么cmake在build源码时取消WITH_QTWEBKIT;要么注释掉报错函数关于QWebElemen类的内容,并在qgismaptip.cpp文件开头注释掉WITH_QTWEBKIT:
//#if WITH_QTWEBKIT
//#endif
Q: error LNK2019: 无法解析的外部符号 WinMain,MSVCRT.lib
A: 方法一:见上方的“VS常见问题”;方法二:由于QGis新建项目是控制台应用程序,而程序通过的是WinMian(及windows入口函数),因此需要作下处理:在“qgis_core项目”->“属性”->“连接器”->“输入”附加依赖项中,debug版本添加D:\Qt\Qt5.12.10\msvc2017_64\qtmaind.lib,release版本则添加qtmain.lib。