Better introspection via the dev console

This commit is contained in:
Robert Griebl
2022-10-05 23:20:53 +02:00
parent e09b0d79b1
commit 32fe569bb7

View File

@@ -43,13 +43,160 @@ public:
QmlException(const QList<QQmlError> &errors, const char *msg)
: Exception(msg)
{
for (auto &error : errors) {
m_message.append(u"\n"_qs);
m_message.append(error.toString());
}
for (auto &error : errors)
m_message = m_message % u'\n' % error.toString();
}
};
static QString stringifyType(QMetaType mt)
{
if (mt.metaObject()) {
int qei = mt.metaObject()->indexOfClassInfo("QML.Element");
if (qei >= 0) {
const auto name = QString::fromLatin1(mt.metaObject()->classInfo(qei).value());
if (name == u"auto")
return QString::fromLatin1(mt.metaObject()->className()).section(u"::"_qs, -1);
else
return name;
}
}
switch (mt.id()) {
case QMetaType::QString : return u"string"_qs;
case QMetaType::QStringList : return u"list<string>"_qs;
case QMetaType::QVariant : return u"var"_qs;
case QMetaType::QVariantList: return u"list"_qs;
case QMetaType::QVariantMap : return u"object"_qs;
case QMetaType::QSize : return u"size"_qs;
case QMetaType::QColor : return u"color"_qs;
case QMetaType::Float :
case QMetaType::Double : return u"real"_qs;
default : return QLatin1String(mt.name());
}
}
static QString stringify(const QVariant &value, int level = 0, bool indentFirstLine = false);
static QString stringifyQObject(const QObject *o, const QMetaObject *mo, int level, bool indentFirstLine)
{
QString str;
QString indent = QString(level * 2, QLatin1Char(' '));
QString nextIndent = QString((level + 1) * 2, QLatin1Char(' '));
bool isGadget = mo->metaType().flags().testFlag(QMetaType::IsGadget);
if (indentFirstLine)
str.append(indent);
str = str % QLatin1String(mo->className()) % u" {\n";
if (!isGadget && mo->superClass()) {
str = str % nextIndent % u"superclass: "
% stringifyQObject(o, mo->superClass(), level + 1, false) % u'\n';
}
for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) {
QMetaProperty p = mo->property(i);
QVariant value = isGadget ? p.readOnGadget(o) : p.read(o);
str = str % nextIndent % stringifyType(p.metaType()) % u' '
% QLatin1String(p.name()) % u": " % stringify(value, level + 1, false) % u'\n';
}
for (int i = mo->methodOffset(); i < mo->methodCount(); ++i) {
QMetaMethod m = mo->method(i);
switch (m.methodType()) {
case QMetaMethod::Slot: break;
case QMetaMethod::Method: break;
default: continue;
}
const auto pnames = m.parameterNames();
QStringList params;
for (int pi = 0; pi < m.parameterCount(); ++pi)
params.append(stringifyType(m.parameterMetaType(pi)) % u' ' % QLatin1String(pnames.at(pi)));
str = str % nextIndent % stringifyType(m.returnMetaType()) % u' ' % QLatin1String(m.name())
% u'(' % params.join(u", ") % u")\n";
}
str = str % indent % u'}';
return str;
}
static QString stringifyQGadget(const void *g, const QMetaObject *mo, int level, bool indentFirstLine)
{
Q_ASSERT(g && mo && mo->metaType().flags().testFlag(QMetaType::IsGadget));
return stringifyQObject(reinterpret_cast<const QObject *>(g), mo, level, indentFirstLine);
}
static QString stringify(const QVariant &value, int level, bool indentFirstLine)
{
QString str;
QString indent = QString(level * 2, QLatin1Char(' '));
QString nextIndent = QString((level + 1) * 2, QLatin1Char(' '));
if (indentFirstLine)
str.append(indent);
switch (int(value.typeId())) {
case QMetaType::QVariantList: {
QListIterator<QVariant> it(value.toList());
if (!it.hasNext()) {
str.append(u"[]");
} else {
str = str.append(u"[\n");
while (it.hasNext()) {
str = str % stringify(it.next(), level + 1, true);
if (it.hasNext())
str.append(u',');
str.append(u'\n');
}
str = str % indent % u']';
}
break;
}
case QMetaType::QVariantMap: {
QMapIterator<QString, QVariant> it(value.toMap());
if (!it.hasNext()) {
str.append(u"{}");
} else {
str.append(u"{\n");
while (it.hasNext()) {
it.next();
str = str % nextIndent % it.key() % u": " % stringify(it.value(), level + 1, false);
if (it.hasNext())
str.append(u',');
str.append(u'\n');
}
str = str % indent % u'}';
}
break;
}
case QMetaType::QString: {
str = str % u'"' % value.toString().remove(u"\0"_qs) % u'"';
break;
}
case QMetaType::QObjectStar: {
QObject *o = qvariant_cast<QObject *>(value);
if (!o) {
str.append(u"<invalid QObject>");
break;
}
str.append(stringifyQObject(o, o->metaObject(), level, false));
break;
}
default: {
QMetaType meta(value.typeId());
if (meta.flags().testFlag(QMetaType::IsGadget))
str.append(stringifyQGadget(value.data(), meta.metaObject(), level, false));
else
str.append(value.toString());
break;
}
}
return str;
}
ScriptManager::ScriptManager(QQmlEngine *engine)
: m_engine(engine)
@@ -146,8 +293,9 @@ bool ScriptManager::executeString(const QString &s)
Q_ASSERT(m_engine);
const char script[] =
"import BrickStore 1.0\n"
"import QtQml 2.12\n"
"import BrickStore\n"
"import BrickLink\n"
"import QtQml\n"
"QtObject {\n"
" property var bl: BrickLink\n"
" property var bs: BrickStore\n"
@@ -161,15 +309,17 @@ bool ScriptManager::executeString(const QString &s)
m_rootObject = component.create();
QQmlExpression e(m_engine->rootContext(), m_rootObject, s);
qCDebug(LogScript).noquote() << "#" << s;
auto result = e.evaluate();
qCInfo(LogScript).noquote() << "#" << s;
bool isUndefined = false;
auto result = e.evaluate(&isUndefined);
if (e.hasError()) {
qCWarning(LogScript).noquote() << "> ERROR:" << e.error().toString();
qCInfo(LogScript).noquote() << "> <error>:" << e.error().toString();
return false;
} else if (isUndefined) {
qCInfo(LogScript, "> <undefined>");
return true;
} else {
auto rs = result.toString();
if (!rs.isEmpty())
qCDebug(LogScript).noquote() << ">" << rs;
qCInfo(LogScript).noquote() << ">" << stringify(result, 1, false);
return true;
}
}