一、Qt Widgets 问题交流
二、Qt Quick 问题交流
1.Qt5 的 QML Settings 没有设置编码的接口
Qt6 虽然移除了 QSettings 的 setIniCodec 接口,默认为 utf8,但是 Qt5 这个接口还能用,且没有默认 utf8。这就导致用 Qt5 QML 的 Settings 没法像 C++ 的 QSettings 一样设置成 utf8 编码。
import QtQuick 2.15
import QtQuick.Window 2.15
import Qt.labs.settings 1.0
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello Settings")
Settings {
category: "Group"
fileName: "config.ini"
property string mykey: "中文值"
Component.onCompleted: {
setValue("MyKey2", "第二个")
}
}
}
而且这个接口设计还有个问题,属性名只能小写开头,没法绑定大写的 key 名,只能用 set 接口来设置。
2.Qt5.15.2 安卓 QML 组件的 plugins.qmltypes 文件接口描述不完整
Qt Bug:https://bugreports.qt.io/browse/QTBUG-85888
以 Window 类型为例,安卓目录下(\5.15.2\android\qml\QtQuick\Window.2)的 plugins.qmltypes 文件才 2KB,而 MSVC 目录下的有 15KB,打开对比文本发现接口少了很多。这就导致写安卓项目时,Qt Creator 没法正常的提示接口,还会报红色警告:
3.ListView 和 Repeater 组件在增删 model 元素时的一点区别
如果在一个函数中 remove model 的一个元素,Repeater 会立即删除 Item,ListView 会在函数执行完删除 Item。使用 Repeater 的时候,如果在 delegate Item 内部执行带 remove 操作的函数,那函数内接下来就没法访问其他的对象了。
测试环境 Qt5.15.2,测试代码:
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello Qml")
Column {
anchors.centerIn: parent
spacing: 12
ListView {
id: a_listview
width: 400
height: 24
model: a_model
orientation: ListView.Horizontal
spacing: 1
delegate: Button {
text: model.title
width: 100
height: 24
onClicked: {
var i = model.index;
var data = a_model.get(i);
a_model.remove(i);
a_model.counter++;
a_model.insert(i, {"title" : a_model.counter});
console.log('a clicked finished')
}
Component.onDestruction: {
console.log('a free')
}
}
}
Row {
spacing: 1
height: 24
Repeater {
id: b_repeater
model: b_model
delegate: Button {
text: model.title
width: 100
height: 24
onClicked: {
var i = model.index;
var data = b_model.get(i);
b_model.remove(i);
//b_model.counter++;
//b_model.insert(i, {"title" : b_model.counter});
console.log('b clicked finished')
}
Component.onDestruction: {
console.log('b free')
}
}
}
}
}
ListModel {
id: a_model
property int counter: 0
Component.onCompleted: {
for (var i = 0; i < 3; i++)
{
a_model.counter++;
a_model.append({"title" : a_model.counter});
}
}
}
ListModel {
id: b_model
property int counter: 0
Component.onCompleted: {
for (var i = 0; i < 3; i++)
{
b_model.counter++;
b_model.append({"title" : b_model.counter});
}
}
}
}
三、其他
1.信号槽跨线程传递数据,接收线程不能及时处理的问题
假设:A 线程持续从 IO 接收数据,然后传递给 B 线程进行处理,比较完善的写法是会用一个线程安全的队列。但有时为了省事选择直接用信号槽来传递数据,这可能在开发机上没什么问题,正常运行,但到了配置比较低的机器,可能会内存溢出崩掉,或者运行一段时间后崩掉。因为配置低的机器可能处理速度比我们接收的速度慢,这就导致信号槽的异步调用事件一直堆积,如果信号传的数据比较大就会很快内存溢出,如果数据很小也可能会超过事件队列的上限值。
这时候还是得用上跨线程的队列来传递,对来不及处理的进行丢弃或者警告;也可以用自定义事件,然后对来不及处理的事件进行合并。
2.Asio 在安卓上用 socket.native_handle() 设置 UDP 阻塞超时没效果
asio::ip::udp::socket 可以用 native_handle 拿到原始套接字,在 windows 上可以用来设置 UDP 的读写超时时间,因为 Asio 是异步操作的库,所以本身没有提供阻塞超时相关的接口,用 native_handle 来实现的话比用定时器方便很多。但是,当我把代码放到安卓环境里跑的时候,这样设置超时不生效了,但是 C 语言 API 创建的 socket 设置超时时间是有用的。
// 设置超时时间
#if defined(WIN32)
int time_out = 2000;
int ret = setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&time_out, sizeof(time_out));
#else
timeval timeout = { 2, 0 };
int ret = setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const void*)&time_out, sizeof(time_out));
#endif
if (ret != 0) {
return false;
}