From 202af3021362d3c8066bc479a95e7aad889bd928 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Mon, 19 Apr 2010 21:42:49 +1000 Subject: Make the dynamic creation functions on the Qt object Also updated examples to still work, and the dynamic example now uses exceptions a little, to demonstrate that it can be done. Exceptions are also now filled out with the QML error data. Task-number: QT-2801 Reviewed-by: Aaron Kennedy --- .../declarative/samegame/SamegameCore/samegame.js | 2 +- demos/declarative/snake/content/snake.js | 4 +- doc/src/declarative/globalobject.qdoc | 9 +++- examples/declarative/dynamic/dynamic.qml | 37 +++++++++++++++- examples/declarative/dynamic/qml/itemCreation.js | 2 +- .../tutorials/samegame/samegame2/samegame.js | 2 +- .../tutorials/samegame/samegame3/samegame.js | 2 +- .../samegame/samegame4/content/samegame.js | 2 +- src/declarative/QmlChanges.txt | 2 + src/declarative/qml/qdeclarativeengine.cpp | 50 ++++++++++++++++------ .../qdeclarativeqt/data/createComponent.qml | 10 ++--- .../qdeclarativeqt/data/createQmlObject.qml | 18 ++++---- .../qdeclarativeqt/tst_qdeclarativeqt.cpp | 6 +-- 13 files changed, 106 insertions(+), 40 deletions(-) diff --git a/demos/declarative/samegame/SamegameCore/samegame.js b/demos/declarative/samegame/SamegameCore/samegame.js index 3888381..aafbfdf 100755 --- a/demos/declarative/samegame/SamegameCore/samegame.js +++ b/demos/declarative/samegame/SamegameCore/samegame.js @@ -8,7 +8,7 @@ var blockSrc = "SamegameCore/BoomBlock.qml"; var scoresURL = "http://qtfx-nokia.trolltech.com.au/samegame/scores.php"; var scoresURL = ""; var gameDuration; -var component = createComponent(blockSrc); +var component = Qt.createComponent(blockSrc); //Index function used instead of a 2D array function index(column,row) { diff --git a/demos/declarative/snake/content/snake.js b/demos/declarative/snake/content/snake.js index 12176c7..2457fbd 100644 --- a/demos/declarative/snake/content/snake.js +++ b/demos/declarative/snake/content/snake.js @@ -5,8 +5,8 @@ var links = new Array; var scheduledDirections = new Array; var numRows = 1; var numColumns = 1; -var linkComponent = createComponent("content/Link.qml"); // XXX should resolve relative to script, not component -var cookieComponent = createComponent("content/Cookie.qml"); +var linkComponent = Qt.createComponent("content/Link.qml"); // XXX should resolve relative to script, not component +var cookieComponent = Qt.createComponent("content/Cookie.qml"); var cookie; var linksToGrow = 0; var linksToDie = 0; diff --git a/doc/src/declarative/globalobject.qdoc b/doc/src/declarative/globalobject.qdoc index 97f5d91..e2152b3 100644 --- a/doc/src/declarative/globalobject.qdoc +++ b/doc/src/declarative/globalobject.qdoc @@ -258,6 +258,11 @@ of their use. } \endcode + The methods and properties of the Component element are defined in its own + page, but when using it dynamically only two methods are usually used. + Component.createObject() returns the created object or null if there is an error. + If there is an error, Component.errorsString() describes what the error was. + If you want to just create an arbitrary string of QML, instead of loading a QML file, consider the createQmlObject() function. @@ -277,7 +282,9 @@ of their use. similarly to eval, but for creating QML elements. Returns the created object, or null if there is an error. In the case of an - error, details of the error are output using qWarning(). + error, a QtScript Error object is thrown. This object has the additional property, + qmlErrors, which is an array of all the errors encountered when trying to execute the + QML. Each object in the array has the members: lineNumber, columnNumber, fileName and message. Note that this function returns immediately, and therefore may not work if the QML loads new components. If you are trying to load a new component, diff --git a/examples/declarative/dynamic/dynamic.qml b/examples/declarative/dynamic/dynamic.qml index eea528f..0e6e197 100644 --- a/examples/declarative/dynamic/dynamic.qml +++ b/examples/declarative/dynamic/dynamic.qml @@ -7,6 +7,34 @@ Item { //This is a desktop-sized example width: 1024; height: 512 property int activeSuns: 0 + + //This is the message that pops up when there's an error + Rectangle{ + id: dialog + opacity: 0 + anchors.centerIn: parent + width: dialogText.width + 6 + height: dialogText.height + 6 + border.color: 'black' + color: 'lightsteelblue' + z: 65535 //Arbitrary number chosen to be above all the items, including the scaled perspective ones. + function show(str){ + dialogText.text = str; + dialogAnim.start(); + } + Text{ + id: dialogText + x:3 + y:3 + font.pixelSize: 14 + } + SequentialAnimation{ + id: dialogAnim + NumberAnimation{target: dialog; property:"opacity"; to: 1; duration: 1000} + PauseAnimation{duration: 5000} + NumberAnimation{target: dialog; property:"opacity"; to: 0; duration: 1000} + } + } // sky Rectangle { id: sky @@ -114,7 +142,14 @@ Item { } Button { text: "Create" - onClicked: createQmlObject(qmlText.text, window, 'CustomObject'); + function makeCustom() { + try{ + Qt.createQmlObject(qmlText.text, window, 'CustomObject'); + }catch(err){ + dialog.show('Error on line ' + err.qmlErrors[0].lineNumber + '\n' + err.qmlErrors[0].message ); + } + } + onClicked: makeCustom(); } } } diff --git a/examples/declarative/dynamic/qml/itemCreation.js b/examples/declarative/dynamic/qml/itemCreation.js index ccc03aa..4fa0d9f 100644 --- a/examples/declarative/dynamic/qml/itemCreation.js +++ b/examples/declarative/dynamic/qml/itemCreation.js @@ -31,7 +31,7 @@ function loadComponent() { if (itemComponent != null) //Already loaded the component createItem(); - itemComponent = createComponent(itemButton.file); + itemComponent = Qt.createComponent(itemButton.file); //print(itemButton.file) if(itemComponent.isLoading){ component.statusChanged.connect(finishCreation); diff --git a/examples/declarative/tutorials/samegame/samegame2/samegame.js b/examples/declarative/tutorials/samegame/samegame2/samegame.js index 9809c1d..81da31b 100644 --- a/examples/declarative/tutorials/samegame/samegame2/samegame.js +++ b/examples/declarative/tutorials/samegame/samegame2/samegame.js @@ -35,7 +35,7 @@ function startNewGame() { function createBlock(column, row) { if (component == null) - component = createComponent("Block.qml"); + component = Qt.createComponent("Block.qml"); // Note that if Block.qml was not a local file, component.isReady would be // false and we should wait for the component's statusChanged() signal to diff --git a/examples/declarative/tutorials/samegame/samegame3/samegame.js b/examples/declarative/tutorials/samegame/samegame3/samegame.js index c12def7..eaf47d9 100644 --- a/examples/declarative/tutorials/samegame/samegame3/samegame.js +++ b/examples/declarative/tutorials/samegame/samegame3/samegame.js @@ -32,7 +32,7 @@ function startNewGame() { function createBlock(column, row) { if (component == null) - component = createComponent("Block.qml"); + component = Qt.createComponent("Block.qml"); // Note that if Block.qml was not a local file, component.isReady would be // false and we should wait for the component's statusChanged() signal to diff --git a/examples/declarative/tutorials/samegame/samegame4/content/samegame.js b/examples/declarative/tutorials/samegame/samegame4/content/samegame.js index 7800b6e..c527f66 100755 --- a/examples/declarative/tutorials/samegame/samegame4/content/samegame.js +++ b/examples/declarative/tutorials/samegame/samegame4/content/samegame.js @@ -43,7 +43,7 @@ function startNewGame() { function createBlock(column, row) { if (component == null) - component = createComponent("content/BoomBlock.qml"); + component = Qt.createComponent("content/BoomBlock.qml"); // Note that if Block.qml was not a local file, component.isReady would be // false and we should wait for the component's statusChanged() signal to diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index d5910e3..2cae293 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -19,6 +19,8 @@ Animation: replace repeat with loops (loops: Animation.Infinite gives the old re AnchorChanges: use natural form to specify anchors (anchors.left instead of left) AnchorChanges: removed reset property. (reset: "left" should now be anchors.left: undefined) PathView: snapPosition replaced by preferredHighlightBegin, preferredHighlightEnd +createQmlObject: Moved to the Qt object, now use Qt.createQmlObject() +createComponent: Moved to the Qt object, now use Qt.createComponent() C++ API ------- diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index 9cd6b3c..53d830b 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -254,19 +254,19 @@ QDeclarativeScriptEngine::QDeclarativeScriptEngine(QDeclarativeEnginePrivate *pr qtObject.setProperty(QLatin1String("quit"), newFunction(QDeclarativeEnginePrivate::quit, 0)); qtObject.setProperty(QLatin1String("resolvedUrl"),newFunction(QDeclarativeScriptEngine::resolvedUrl, 1)); + if (mainthread) { + qtObject.setProperty(QLatin1String("createQmlObject"), + newFunction(QDeclarativeEnginePrivate::createQmlObject, 1)); + qtObject.setProperty(QLatin1String("createComponent"), + newFunction(QDeclarativeEnginePrivate::createComponent, 1)); + } + //firebug/webkit compat QScriptValue consoleObject = newObject(); consoleObject.setProperty(QLatin1String("log"),newFunction(QDeclarativeEnginePrivate::consoleLog, 1)); consoleObject.setProperty(QLatin1String("debug"),newFunction(QDeclarativeEnginePrivate::consoleLog, 1)); globalObject().setProperty(QLatin1String("console"), consoleObject); - if (mainthread) { - globalObject().setProperty(QLatin1String("createQmlObject"), - newFunction(QDeclarativeEnginePrivate::createQmlObject, 1)); - globalObject().setProperty(QLatin1String("createComponent"), - newFunction(QDeclarativeEnginePrivate::createComponent, 1)); - } - // translation functions need to be installed // before the global script class is constructed (QTBUG-6437) installTranslatorFunctions(); @@ -1019,7 +1019,7 @@ QScriptValue QDeclarativeEnginePrivate::createQmlObject(QScriptContext *ctxt, QS url = context->resolvedUrl(url); QObject *parentArg = activeEnginePriv->objectClass->toQObject(ctxt->argument(1)); - if(!parentArg) + if(!parentArg) return ctxt->throwError(QDeclarativeEngine::tr("parent object not found")); QDeclarativeComponent component(activeEngine); @@ -1027,10 +1027,21 @@ QScriptValue QDeclarativeEnginePrivate::createQmlObject(QScriptContext *ctxt, QS if(component.isError()) { QList errors = component.errors(); - QString errstr = QLatin1String("Qt.createQmlObject(): "); - foreach (const QDeclarativeError &error, errors) + QString errstr = QLatin1String("Qt.createQmlObject() failed to create object: "); + QScriptValue arr = ctxt->engine()->newArray(errors.length()); + int i = 0; + foreach (const QDeclarativeError &error, errors){ errstr += QLatin1String(" ") + error.toString() + QLatin1String("\n"); - return ctxt->throwError(errstr); + QScriptValue qmlErrObject = ctxt->engine()->newObject(); + qmlErrObject.setProperty("lineNumber", QScriptValue(error.line())); + qmlErrObject.setProperty("columnNumber", QScriptValue(error.column())); + qmlErrObject.setProperty("fileName", QScriptValue(error.url())); + qmlErrObject.setProperty("message", QScriptValue(error.description())); + arr.setProperty(i++, qmlErrObject); + } + QScriptValue err = ctxt->throwError(errstr); + err.setProperty("qmlErrors",arr); + return err; } if (!component.isReady()) @@ -1040,10 +1051,21 @@ QScriptValue QDeclarativeEnginePrivate::createQmlObject(QScriptContext *ctxt, QS if(component.isError()) { QList errors = component.errors(); - QString errstr = QLatin1String("Qt.createQmlObject(): "); - foreach (const QDeclarativeError &error, errors) + QString errstr = QLatin1String("Qt.createQmlObject() failed to create object: "); + QScriptValue arr = ctxt->engine()->newArray(errors.length()); + int i = 0; + foreach (const QDeclarativeError &error, errors){ errstr += QLatin1String(" ") + error.toString() + QLatin1String("\n"); - return ctxt->throwError(errstr); + QScriptValue qmlErrObject = ctxt->engine()->newObject(); + qmlErrObject.setProperty("lineNumber", QScriptValue(error.line())); + qmlErrObject.setProperty("columnNumber", QScriptValue(error.column())); + qmlErrObject.setProperty("fileName", QScriptValue(error.url())); + qmlErrObject.setProperty("message", QScriptValue(error.description())); + arr.setProperty(i++, qmlErrObject); + } + QScriptValue err = ctxt->throwError(errstr); + err.setProperty("qmlErrors",arr); + return err; } Q_ASSERT(obj); diff --git a/tests/auto/declarative/qdeclarativeqt/data/createComponent.qml b/tests/auto/declarative/qdeclarativeqt/data/createComponent.qml index 412c467..253a4e3 100644 --- a/tests/auto/declarative/qdeclarativeqt/data/createComponent.qml +++ b/tests/auto/declarative/qdeclarativeqt/data/createComponent.qml @@ -6,15 +6,15 @@ QtObject { property string relativeUrl property string absoluteUrl - property QtObject incorectArgCount1: createComponent() - property QtObject incorectArgCount2: createComponent("main.qml", 10) + property QtObject incorectArgCount1: Qt.createComponent() + property QtObject incorectArgCount2: Qt.createComponent("main.qml", 10) Component.onCompleted: { - emptyArg = (createComponent("") == null); - var r = createComponent("createComponentData.qml"); + emptyArg = (Qt.createComponent("") == null); + var r = Qt.createComponent("createComponentData.qml"); relativeUrl = r.url; - var a = createComponent("http://www.example.com/test.qml"); + var a = Qt.createComponent("http://www.example.com/test.qml"); absoluteUrl = a.url; } } diff --git a/tests/auto/declarative/qdeclarativeqt/data/createQmlObject.qml b/tests/auto/declarative/qdeclarativeqt/data/createQmlObject.qml index 8e6c58e..9fe169d 100644 --- a/tests/auto/declarative/qdeclarativeqt/data/createQmlObject.qml +++ b/tests/auto/declarative/qdeclarativeqt/data/createQmlObject.qml @@ -4,11 +4,11 @@ Item { id: root // errors resulting in exceptions - property QtObject incorrectArgCount1: createQmlObject() - property QtObject incorrectArgCount2: createQmlObject("import Qt 4.6\nQtObject{}", root, "main.qml", 10) - property QtObject noParent: createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13}", 0) - property QtObject notAvailable: createQmlObject("import Qt 4.6\nQtObject{Blah{}}", root) - property QtObject errors: createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13\nproperty int test: 13\n}", root, "main.qml") + property QtObject incorrectArgCount1: Qt.createQmlObject() + property QtObject incorrectArgCount2: Qt.createQmlObject("import Qt 4.6\nQtObject{}", root, "main.qml", 10) + property QtObject noParent: Qt.createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13}", 0) + property QtObject notAvailable: Qt.createQmlObject("import Qt 4.6\nQtObject{Blah{}}", root) + property QtObject errors: Qt.createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13\nproperty int test: 13\n}", root, "main.qml") property bool emptyArg: false @@ -16,16 +16,16 @@ Item { Component.onCompleted: { // errors resulting in nulls - emptyArg = (createQmlObject("", root) == null); + emptyArg = (Qt.createQmlObject("", root) == null); try { - createQmlObject("import Qt 4.6\nQtObject{property int test\nonTestChanged: QtObject{}\n}", root) + Qt.createQmlObject("import Qt 4.6\nQtObject{property int test\nonTestChanged: QtObject{}\n}", root) } catch (error) { console.log("RunTimeError: ",error.message); } - var o = createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13\n}", root); + var o = Qt.createQmlObject("import Qt 4.6\nQtObject{\nproperty int test: 13\n}", root); success = (o.test == 13); - createQmlObject("import Qt 4.6\nItem {}\n", root); + Qt.createQmlObject("import Qt 4.6\nItem {}\n", root); } } diff --git a/tests/auto/declarative/qdeclarativeqt/tst_qdeclarativeqt.cpp b/tests/auto/declarative/qdeclarativeqt/tst_qdeclarativeqt.cpp index 4d22b0a..24fad14 100644 --- a/tests/auto/declarative/qdeclarativeqt/tst_qdeclarativeqt.cpp +++ b/tests/auto/declarative/qdeclarativeqt/tst_qdeclarativeqt.cpp @@ -354,11 +354,11 @@ void tst_qdeclarativeqt::createQmlObject() QDeclarativeComponent component(&engine, TEST_FILE("createQmlObject.qml")); QString warning1 = component.url().toString() + ":7: Error: expected 2 or 3 parameters"; - QString warning2 = component.url().toString()+ ":10: Error: Qt.createQmlObject(): " + TEST_FILE("inline").toString() + ":2:10: Blah is not a type\n"; - QString warning3 = component.url().toString()+ ":11: Error: Qt.createQmlObject(): " + TEST_FILE("main.qml").toString() + ":4:1: Duplicate property name\n"; + QString warning2 = component.url().toString()+ ":10: Error: Qt.createQmlObject() failed to create object : " + TEST_FILE("inline").toString() + ":2:10: Blah is not a type\n"; + QString warning3 = component.url().toString()+ ":11: Error: Qt.createQmlObject() failed to create object : " + TEST_FILE("main.qml").toString() + ":4:1: Duplicate property name\n"; QString warning4 = component.url().toString()+ ":9: Error: parent object not found"; QString warning5 = component.url().toString()+ ":8: Error: expected 2 or 3 parameters"; - QString warning6 = "RunTimeError: Qt.createQmlObject(): " + TEST_FILE("inline").toString() + ":3: Cannot assign object type QObject with no default method\n"; + QString warning6 = "RunTimeError: Qt.createQmlObject() failed to create object: " + TEST_FILE("inline").toString() + ":3: Cannot assign object type QObject with no default method\n"; QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); -- cgit v0.12