From 32fe569bb739b6959ee00bcf5a96fa3a3cf6f0fa Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Wed, 5 Oct 2022 23:20:53 +0200 Subject: [PATCH] Better introspection via the dev console --- src/common/scriptmanager.cpp | 174 ++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 12 deletions(-) diff --git a/src/common/scriptmanager.cpp b/src/common/scriptmanager.cpp index 32caaf22..48223efd 100755 --- a/src/common/scriptmanager.cpp +++ b/src/common/scriptmanager.cpp @@ -43,13 +43,160 @@ public: QmlException(const QList &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"_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(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 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 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(value); + if (!o) { + str.append(u""); + 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() << "> :" << e.error().toString(); return false; + } else if (isUndefined) { + qCInfo(LogScript, "> "); + return true; } else { - auto rs = result.toString(); - if (!rs.isEmpty()) - qCDebug(LogScript).noquote() << ">" << rs; + qCInfo(LogScript).noquote() << ">" << stringify(result, 1, false); return true; } }