gpt4 book ai didi

c++ - 如何降低(非传统的)基于Qt5/QML的软件的内存使用率?

转载 作者:行者123 更新时间:2023-12-01 14:57:15 24 4
gpt4 key购买 nike

内容:
我正在开发一种新颖的交互概念和计算桌面环境的研究原型(prototype),目前称为“可草图交互(SI)”。
当前,SI仅在基于Debian的Linux上工作。
简而言之,SI允许用户在桌面上绘制带有效果的交互式区域。
一旦两个或多个区域重叠,效果相互兼容的区域也会将其效果彼此应用。
这样,可以设置,修改或删除文件的图形表示和数据等。
以下是一些屏幕截图,提供了直观的示例:
显示桌面环境:

用于打开文件夹/文件的绘图区域(蓝色):

蓝色区域的成品图:

通过将其与蓝色区域重叠来打开桌面文件夹,并绘制一个预览文件区域:

将带有猫的图片文件(png)从文件夹中移出:

猫与绿色区域重叠的图像文件,以显示图像预览:

SI的技术现状
SI用C++编写,带有当前的Qt5和QML版本。
代表您在屏幕快照中看到的效果的SI-Plugins是使用python3.7 +编写的,并使用Boost.Python,而不使用PyQt5。
SI将打开一个MainWindow,每个区域绘图(在屏幕快照中看到的所有区域都是一个区域,包括鼠标光标)是QWidget,它是该MainWindow的无边界子级。
为了进行任何造型,例如为了显示纹理(如文件夹图标),SI使用QML文件,表示为QQuickWidgets,它是该QWidget的无边界子级(我知道堆叠顺序问题,但是对于这个问题我们可以忽略它!)
我可以在运行时从SI-Python-Plugins中更改QML样式。
这在内部使用QMetaObject将QMap 传递给容器组件中的函数。

QMetaObject::invokeMethod(reinterpret_cast<QObject *>(d_view->rootObject()), "updateData", QGenericReturnArgument(), Q_ARG(QVariant, region->data()));
我也使用信号/插槽对此进行了测试,但无法使其按预期工作,上述方法确实按预期工作。
显然,这是由于仅初始化一个QQmlEngine,而不是每个QQuickWidget初始化一个。
单个QQmlEngine具有CppOwnership。
engine = new QQmlEngine(this);
engine->setObjectOwnership(engine, QQmlEngine::CppOwnership);
问题
为了测试和性能基准测试,我打算产生数千个区域:
以下屏幕截图显示了1009个区域(中心为1000个)。
这与所有与QML相关的代码均已停用

根据工具htop,这大约产生200 MB的内存消耗。
这是与所有与QML相关的代码均已激活的

这将产生大约4900 MB的内存消耗。
在QML示例中,黄色区域中使用的纹理是64x64 px 32位RGBA图像。
这种记忆上的差异确实让我感到奇怪。
所有图像所需的内存等于1000(区域数)* 64 * 64(像素数)* 4(如果4个通 Prop 有8位,则为字节数)= 16,384,000字节,约为16.5 MB。当然,每个镜像应该有一些额外的开销,但没有4.8 GB的开销。
通过这里或其他来源的其他问题,我发现QML显然需要大量内存(有人称其为内存消耗)。
例如。:
QML memory usage on big grid
但是,这种高差异可能源于我对Qt5和QML的非常规用法。
问题/ S
给定SI软件的当前状态,是否可以降低内存消耗?
是我没有想到的替代方法吗?
是我错过的Qt5 / QML文档中的标志,它使问题变得简单了吗?
抱歉,冗长的帖子,在此先感谢您的帮助。
编辑:错别字,根据要求添加潜在的关键或可疑代码。
可疑代码:
这是QWidget的构造函数,其中包含QQmlQuickWidget并表示一个区域
RegionRepresentation::RegionRepresentation(QWidget *parent, QQmlEngine* engine, const std::shared_ptr<Region>& region):
d_color(QColor(region->color().r, region->color().g, region->color().b, region->color().a)),
d_qml_path(region->qml_path()),
d_view(new QQuickWidget(engine, this)),
d_type(region->type()),
d_uuid(region->uuid()),
d_name(region->name())
{
if(!d_qml_path.empty())
d_view->setSource(QUrl::fromLocalFile(QString(d_qml_path.c_str())));

d_view->setGeometry(0, 0, region->aabb()[3].x - region->aabb()[0].x, region->aabb()[1].y - region->aabb()[0].y);
d_view->setParent(this);
d_view->setAttribute(Qt::WA_AlwaysStackOnTop);
d_view->setAttribute(Qt::WA_NoSystemBackground);
d_view->setClearColor(Qt::transparent);

setParent(parent);
setGeometry(region->aabb()[0].x, region->aabb()[0].y, region->aabb()[3].x - region->aabb()[0].x, region->aabb()[1].y - region->aabb()[0].y);

if(region->effect()->has_data_changed())
QMetaObject::invokeMethod(reinterpret_cast<QObject *>(d_view->rootObject()), "updateData", QGenericReturnArgument(), Q_ARG(QVariant, region->data()));

d_fill.moveTo(region->contour()[0].x - region->aabb()[0].x, region->contour()[0].y - region->aabb()[0].y);

std::for_each(region->contour().begin() + 1, region->contour().end(), [&](auto& point)
{
d_fill.lineTo(point.x - region->aabb()[0].x, point.y - region->aabb()[0].y);
});

show();
}
我可以通过以下方式从插件(python)访问并在QQmlQuickWidget中设置数据:
self.set_QML_data(<key for QMap as str>, <value for key as QVariant>, <datatype constant>)
每个区域都有一个这样的QMap,以任何方式更新时,RegionRepresentation都会调用它:
if(region->effect()->has_data_changed())
QMetaObject::invokeMethod(reinterpret_cast<QObject *>(d_view->rootObject()), "updateData", QGenericReturnArgument(), Q_ARG(QVariant, region->data()));
通过以下方式填充QMap:
  QVariant qv;

switch (type)
{
case SI_DATA_TYPE_INT:
d_data[QString(key.c_str())] = QVariant( bp::extract<int>(value))
d_data_changed = true;
break;

case SI_DATA_TYPE_FLOAT:
d_data[QString(key.c_str())] = QVariant(bp::extract<float>(value));
d_data_changed = true;
break;

case SI_DATA_TYPE_STRING:
d_data[QString(key.c_str())] = QVariant(QString(bp::extract<char*>(value)));
d_data_changed = true;
break;

case SI_DATA_TYPE_BOOL:
d_data[QString(key.c_str())] = QVariant(bp::extract<bool>(value));
d_data_changed = true;
break;

case SI_DATA_TYPE_IMAGE_AS_BYTES:
int img_width = bp::extract<int>(kwargs["width"]);
int img_height = bp::extract<int>(kwargs["height"]);

QImage img(img_width, img_height, QImage::Format::Format_RGBA8888);

if(!value.is_none())
{
const bp::list& bytes = bp::list(value);

int len = bp::len(bytes);
uint8_t buf[len];

for(int i = 0; i < len; ++i)
buf[i] = (uint8_t) bp::extract<int>(value[i]);

img.fromData(buf, len, "PNG");

d_data[QString(key.c_str())] = QVariant(img);
}
else
{
d_data[QString(key.c_str())] = QVariant(QImage());
}
d_data_changed = true;
break;
}
在QML中,该QMap的使用方式如下:
// data is QMap<QString, QVariant>
function updateData(data)
{
// assume that data has key "width" assigned from python as shown in above code snippet
qmlcomponent.width = data.width;
}
这是QML文件的典型布局,用于设置区域/效果的样式:
Item
{
function updateData(data)
{
texture.width = data.icon_width;
texture.height = data.icon_height;
texture.source = data.img_path;
}

id: container
visible: true

Item {
id: iconcontainer
visible: true

Image {
id: texture
anchors.left: parent.left
anchors.top: parent.top

visible: true
}
}
}
中心思想之一是,系统用户可以为区域创建自定义样式,并在运行时通过关联的插件动态处理和解决该样式问题。

最佳答案

虽然这不能解决您的问题,但我认为这可能对您来说是很有值(value)的信息,并且由于我没有足够的声誉点来发表评论,因此我将其发布为答案。
您看到的内存问题似乎是一个错误,与Qt / QML没有关系。下面是一个简单的示例,说明如何在QML中显示一堆图像,以及有关内存消耗的预期。
下面的代码显示1040张具有QML的图像,占用的内存不足30 MB(使用64x64 px 32位RGBA源图像,但是使用较大的图像时,它的变化不大)。显示的图像将缩小到20x20 px,但是即使您有足够的屏幕空间将其显示为64x64 px,在最坏的情况下,如果内存消耗呈线性增加,也应该增加10倍左右,而到4.8 GB 。我希望这会有所帮助,这是我使用的代码:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.3


Window {
visible: true
width: 1200
height: 1000
color: "#00000000"

ColumnLayout {
anchors.fill: parent
anchors.margins: 20
Repeater {
model: 26
RowLayout {
Repeater {
model: 40
Image {
Layout.preferredWidth: 20
Layout.preferredHeight: 20
source: "qrc:/tile.png"
}
}
}
}
}
}
和内存消耗:
enter image description here

关于c++ - 如何降低(非传统的)基于Qt5/QML的软件的内存使用率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63073524/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com