《QDebug 2025年8月》
一、Qt Widgets 问题交流
1.
二、Qt Quick 问题交流
1.Qt5结构体在QML中使用,成员是另一结构体或容器类型时的赋值问题
QML中虽然不能直接创建结构体对象,但是可以通过Cpp传递到QML,且能读写访问。在Qt5的QML中修改结构体对象属性值时,如果属性类型又是一个结构体,那不会修改到原来的结构体对象中,不过可以将属性结构体整体赋值给原来的结构体对象,对于QStringList等容器类型属性也有类似问题。实测Qt6已经优化,嵌套结构体可以正常对成员的成员赋值了。
测试代码:
#pragma once
#include <QObject>
#include <QVariant>
#include <QDebug>struct MySub {Q_GADGETQ_PROPERTY(QString str MEMBER str)
public:// Qt5作为属性需要重载!=bool operator!=(const MySub &other) const {return !(*this == other);}// Qt6作为属性需要重载==bool operator==(const MySub &other) const {return str == other.str;}QString str;
};struct MyData {Q_GADGETQ_PROPERTY(int id MEMBER id)Q_PROPERTY(QString str MEMBER str)Q_PROPERTY(MySub sub MEMBER sub)Q_PROPERTY(QStringList slist MEMBER slist)Q_PROPERTY(QVariantList vlist MEMBER vlist)
public:int id;QString str;MySub sub;QStringList slist;QVariantList vlist;
};class MyCreator : public QObject
{Q_OBJECT
public:using QObject::QObject;static void registerMeta() {qRegisterMetaType<MyData>("MyData");qRegisterMetaType<MySub>("MySub");}Q_INVOKABLE QVariantMap createVariantMap() const {return QVariantMap{};}Q_INVOKABLE MyData createMyData() const {return MyData{};}Q_INVOKABLE MySub createMySub() const {return MySub{};}Q_INVOKABLE void logMyData(const MyData &data) {qDebug() << "MyData" << data.id << data.str<< data.slist << data.vlist<< "sub:" << data.sub.str;}
};
MyCreator {id: creator}Component.onCompleted: {testJs()testQVariantMap()testStruct()}function testJs() {console.log("test js")// 测试js map和listlet my_map = { a: 10, b: 20, c: {c1: 10, c2: 20} }console.log(my_map.a, my_map.c.c2)my_map.c.c2 = 30console.log(my_map.a, my_map.c.c2)let my_list = [10, 20, [10, 20]]console.log(my_list[0], my_list[2][1])my_list[2][1] = 30console.log(my_list[0], my_list[2][1])}function testQVariantMap() {console.log("test QVariantMap")// 测试QVariantMaplet my_map = creator.createVariantMap()my_map.a = "a"my_map.b = 100let my_sub = creator.createVariantMap()my_sub.d = "d"my_sub.e = 200my_map.c = my_sub // {d: "d", e: 200}console.log(my_map.a, my_map.b, my_map.c.d, my_map.c.e)my_map.a = "aa"my_map.c.e = 2000console.log(my_map.a, my_map.b, my_map.c.d, my_map.c.e)}function testStruct() {console.log("test struct")// 测试结构体let my_data = creator.createMyData()my_data.id = 10my_data.str = "hello"my_data.slist = ["a", "b"]my_data.slist[0] = "aa"my_data.vlist = ["a", "b"]my_data.vlist[0] = "aa"my_data.sub.str = "world"// Qt5对二级属性直接赋值是没有效果的console.log(my_data.id, my_data.str, my_data.slist, my_data.vlist, my_data.sub.str)creator.logMyData(my_data)let my_slist = my_data.slistmy_slist[0] = "aa"my_data.slist = my_slist// let sub = my_data.sub// sub.str = "world"// my_data.sub = sublet my_sub = creator.createMySub()my_sub.str = "sub"my_data.sub = my_sub// 对成员结构体整体赋值有效果console.log(my_data.id, my_data.str, my_data.slist, my_data.sub.str)creator.logMyData(my_data)}
三、其他
1.qmake在构建前执行cmd脚本生成版本信息的头文件
有时候需要在每次构建时更新编译时间和版本号等信息,可以通过执行脚本来生成版本信息的头文件,再include到项目中。如果只是构建前执行命令,可用QMAKE_PRE_LINK,如果要声明依赖关系可以用QMAKE_EXTRA_TARGETS+PRE_TARGETDEPS。
先准备一个生成版本信息的脚本(因为构建执行时当前目录不是pro路径,所以添加一个入参为输出头文件的路径):
@echo off
chcp 65001if "%~1"=="" (set OUTPATH=version.h
) else (set OUTPATH=%~1
)echo #pragma once > "%OUTPATH%"
for /f %%i in ('git rev-parse HEAD') do (set commitid=%%i)
echo const char *commit_id=^"%commitid%^"; >> "%OUTPATH%"
for /f %%i in ('git rev-parse --short HEAD') do (set commitshort=%%i)
echo const char *commit_short=^"%commitshort%^"; >> "%OUTPATH%"
for /f %%i in ('echo %date%) do (set date=%%i)
echo const char *build_date=^"%date%^"; >> "%OUTPATH%"
for /f %%i in ('echo %time%) do (set time=%%i)
echo const char *build_time=^"%time%^"; >> "%OUTPATH%"
echo const char *version=^"1.00.00^"; >> "%OUTPATH%"
pause
在pro文件中使用:
pre_version.target = $$PWD/version.h
# 加FORCE每次构建都执行
pre_version.depends = FORCE
# 要加echo、call、cmd /C等命令才能正常执行bat
pre_version.commands = call $$PWD/version.bat $$pre_version.target# 构建前依赖
PRE_TARGETDEPS += $$pre_version.target
QMAKE_EXTRA_TARGETS += pre_version
INCLUDEPATH += $$PWD
这里要注意的就是commands直接写脚本路径不会执行,GPT说make工具不会自动用cmd.exe解释命令,需要显式调用cmd或call