集成Qt Quick(QML)地图项目至Qt Widgets C++应用后按钮无响应问题
为啥按钮点了完全没反应
三个问题凑到一块导致的异常:
- 编写的QML加载路径不对,根本没把正确的地图QML文件加载进来
- 嵌到Widgets窗口里的时候,QML的窗口层级不对,鼠标点下去的事件被外层窗口截走了,按钮根本收不到点击信号
- 没有把C++编写的gmap类同步到QML的运行环境里,点按钮的时候调用的gmap对象根本不存在,QML静默报错不执行任何逻辑
分步修复步骤
1. 先把QML的加载路径改对
原来的加载代码是:
view->setSource(QUrl("/maps/main.qml"));
这个路径指向操作系统文件系统的根目录,跟项目里的QML文件完全不匹配,加载出来的就是个空界面,当然点不到按钮。
如果QML文件是打包在Qt资源系统里的,要改成资源路径写法:
view->setSource(QUrl("qrc:/maps/main.qml"));
注意:必须在项目的.pro工程文件里,把main.qml和关联的所有资源文件全部加入RESOURCES块,不然就算路径写对,程序也找不到对应文件。
2. 把QML的根节点从Window换成Item
之前独立运行这个Qt Quick项目的时候,QML根节点写Window是完全正常的,因为整个程序就是个独立的应用窗口。但现在要把它嵌到QWidget里,用的是QWidget::createWindowContainer方法,QQuickView本身已经会生成一个顶层窗口来承载内容了。
如果QML里自己又写了一个Window当根节点,就会形成两层嵌套窗口,鼠标点下去的事件全被外层的QQuickView窗口截走,里面的按钮根本收不到点击通知。
改法很简单:打开main.qml,把最外层的Window {}替换为Item {},删除原来Window上visible、屏幕尺寸适配这些只有顶层窗口才需要的属性,把原来所有放在Window里的地图、按钮、输入框控件全部挪到这个Item下面就行,原有布局代码完全不用改动。
3. 把gmap对象放到QML可访问的位置
朋友之前独立运行这个项目的时候,肯定是在启动代码里把gmap类注册给了QML环境,不然QML里根本不认识gmap是什么。但现在集成的代码里完全没做这步,QML里调用gmap.setData()的时候,根本找不到这个对象,直接静默报错没有任何反应。
只要在创建QQuickView之后、加载QML文件之前,加几行代码把gmap实例塞到QML上下文里就行:
// 直接把gmap实例绑定为QML里的同名对象,原有QML代码不需要任何修改 gmap *mapCore = new gmap(this); view->engine()->rootContext()->setContextProperty("gmap", mapCore);
如果需要多个gmap实例,也可以用类型注册的方式,不过那样还要在QML开头加对应的import语句,不如直接绑定实例适配性好。
4. 给嵌入的容器加上焦点支持
嵌完之后给容器补一行代码,让它能接收鼠标和键盘焦点,避免出现点了输入框没法输入的问题:
container->setFocusPolicy(Qt::StrongFocus);
改完怎么验证
全部修改完成后运行程序,观察Qt Creator下方的应用输出窗口:
- 点击ADD MARKER按钮时,如果能看到之前gmap.cpp里
qDebug打印出的输入经纬度字符串,就说明按钮已经正常响应,地图上的绿色标记会自动跳转到输入的对应位置。 - 如果还是没反应,直接看输出窗口里的QML报错信息,顺着报错定位未定义对象、路径加载错误即可。
内容的提问来源于stack exchange,提问作者Saumay Dudeja

