VS2019、Qt5.12及QGis3.16项目开发常见问题
在C++、Qt软件软件过程中,常常遇到一些编译错误和警告;博主通过VS2017+Qt5.12.10+QGis3.16二次开发项目,将这些常见问题做个整理,以便日后查阅。该内容分为四部分:VS+Qt环境搭配、VS编译常见问题、Qt常见问题、QGis二次开发常见问题。
一、VS创建Qt项目
1、安装VS2017或VS2019,编译器原则上是越新越好(此贴发布时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