Files
gammaray-mcp/bridge/src/property_reader.cpp
2026-07-08 13:09:00 +08:00

142 lines
4.6 KiB
C++

/*
property_reader.cpp — shared AggregatedPropertyModel reader.
Extracted from scenegraph_tools.cpp getItemProperties().
SPDX-FileCopyrightText: 2026 The GammaRay MCP Bridge Authors
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "property_reader.h"
#include <QAbstractItemModel>
#include <QCoreApplication>
#include <QEventLoop>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include <QVariant>
// Poll until column-0 data arrives for at least one row (RemoteModel lazy-fetch).
static void waitForPropertyData(QAbstractItemModel *m, int rows, int timeoutMs)
{
QEventLoop loop;
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
bool gotData = false;
while (std::chrono::steady_clock::now() < deadline && !gotData) {
for (int r = 0; r < rows; ++r) {
const auto val = m->data(m->index(r, 0), Qt::DisplayRole).toString();
if (!val.isEmpty() && val != QStringLiteral("Loading...")) {
gotData = true;
break;
}
}
if (!gotData) {
QTimer::singleShot(200, &loop, &QEventLoop::quit);
loop.exec();
}
}
}
QJsonObject readAggregatedPropertyModel(QAbstractItemModel *m)
{
if (!m)
return {};
// Wait for rows to become available (RemoteModel lazy-fetch).
{
QEventLoop loop;
int waited = 0;
const int step = 150;
while (waited < 3000) {
if (m->rowCount() > 0)
break;
QTimer::singleShot(step, &loop, &QEventLoop::quit);
loop.exec();
waited += step;
}
}
const int rows = m->rowCount();
if (rows == 0)
return {};
waitForPropertyData(m, rows, 5000);
const int actualRows = m->rowCount();
const int cols = m->columnCount();
// Pre-fetch children for grouped properties.
for (int r = 0; r < actualRows; ++r)
m->rowCount({ m->index(r, 0) });
if (actualRows > 0) {
QEventLoop l2;
QTimer::singleShot(500, &l2, &QEventLoop::quit);
l2.exec();
}
for (int r = 0; r < actualRows; ++r) {
const QModelIndex idx0 = m->index(r, 0);
if (m->hasChildren(idx0) && m->canFetchMore(idx0))
m->fetchMore(idx0);
}
if (actualRows > 0) {
QEventLoop l2b;
QTimer::singleShot(500, &l2b, &QEventLoop::quit);
l2b.exec();
}
QJsonObject simpleProps;
QJsonObject groups;
for (int r = 0; r < actualRows; ++r) {
const QString name = m->data(m->index(r, 0), Qt::DisplayRole).toString();
const QString val = m->data(m->index(r, 1), Qt::DisplayRole).toString();
const QString ptype = cols > 2 ? m->data(m->index(r, 2), Qt::DisplayRole).toString() : QString();
QJsonObject prop;
prop.insert(QStringLiteral("value"), val);
prop.insert(QStringLiteral("type"), ptype);
const QModelIndex idx0 = m->index(r, 0);
if (m->hasChildren(idx0)) {
const int childRows = m->rowCount(idx0);
if (childRows > 0) {
for (int cr = 0; cr < childRows; ++cr) {
m->data(m->index(cr, 0, idx0), Qt::DisplayRole);
m->data(m->index(cr, 1, idx0), Qt::DisplayRole);
}
{
QEventLoop l3;
QTimer::singleShot(500, &l3, &QEventLoop::quit);
l3.exec();
}
QJsonObject children;
for (int cr = 0; cr < childRows; ++cr) {
const QString cname = m->data(m->index(cr, 0, idx0), Qt::DisplayRole).toString();
const QString cval = m->data(m->index(cr, 1, idx0), Qt::DisplayRole).toString();
const QString ctype = cols > 2 ? m->data(m->index(cr, 2, idx0), Qt::DisplayRole).toString() : QString();
if (cname.isEmpty() || cname == QStringLiteral("Loading..."))
continue;
QJsonObject child;
child.insert(QStringLiteral("value"), cval);
child.insert(QStringLiteral("type"), ctype);
children.insert(cname, child);
}
if (!children.isEmpty())
prop.insert(QStringLiteral("children"), children);
}
groups.insert(name, prop);
} else {
simpleProps.insert(name, prop);
}
}
QJsonObject out;
out.insert(QStringLiteral("properties"), simpleProps);
if (!groups.isEmpty())
out.insert(QStringLiteral("groups"), groups);
return out;
}