Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Thorbjørn Lindeijer
On Fri, Jun 24, 2022, at 11:34, Ulf Hermann wrote:
> Indeed, in QQmlEngine the global object is frozen. You cannot replace
> its properties. Apparently you can still add to it, though. But see
> https://codereview.qt-project.org/c/qt/qtdeclarative/+/418536/1 for a
> possible solution.

Since you mentioned I can add members to the Qt object, my current workaround 
is as follows:

#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
QJSValue qtObject = globalObject.property(QStringLiteral("Qt"));
auto  = Qt::staticMetaObject;
for (int i = qtNamespace.enumeratorCount(); i >= 0; --i) {
auto metaEnum = qtNamespace.enumerator(i);
for (int k = metaEnum.keyCount(); k >= 0; --k)
qtObject.setProperty(QString::fromLatin1(metaEnum.key(k)), 
metaEnum.value(k));
}
#endif

This works in QQmlEngine as well, so I think that'll be fine until these are 
added back properly. :-)

Thanks,
Thorbjørn
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Thorbjørn Lindeijer
On Fri, Jun 24, 2022, at 12:50, Ulf Hermann wrote:
>> Right, normally when evaluating a script, any error can be caught and 
>> reported however you wish, but this does not work when the script execution 
>> is trigger through a signal connection. In this case, when using QJSEngine, 
>> errors are always printed to the standard out, whereas the QQmlEngine allows 
>> reporting these through its "warnings" signal.
>
> Well, I would like to see a bug report with a minimal reproducer for 
> this. How do you produce a signal connection in pure JS?

You just expose a QObject-derived class instance to JS which has a signal, and 
then in JS you can do:

someObject.someSignal.connect(() => {});

See 
https://doc.qt.io/qt-6.2/qtqml-syntax-signals.html#connecting-signals-to-methods-and-signals

But if you trigger a runtime error, like:

someObject.someSignal.connect(() => {
FooDoesNotExist.Bar;
});

And then emit "someSignal" from C++, then the connected JS function gets 
executed, but without any way of catching and reporting the resulting error, 
unless you use QQmlEngine and connect to the "warnings" signal.

Cheers,
Thorbjørn
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Ulf Hermann

Right, normally when evaluating a script, any error can be caught and reported however 
you wish, but this does not work when the script execution is trigger through a signal 
connection. In this case, when using QJSEngine, errors are always printed to the standard 
out, whereas the QQmlEngine allows reporting these through its "warnings" 
signal.


Well, I would like to see a bug report with a minimal reproducer for 
this. How do you produce a signal connection in pure JS?


best regards,
Ulf
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Thorbjørn Lindeijer
On Fri, Jun 24, 2022, at 11:34, Ulf Hermann wrote:
> Indeed, in QQmlEngine the global object is frozen. You cannot replace
> its properties. Apparently you can still add to it, though. But see
> https://codereview.qt-project.org/c/qt/qtdeclarative/+/418536/1 for a
> possible solution.

Thanks a lot for this patch! Unfortunately I'm about to leave on holiday and 
will have no time to test it out before I leave. I hope you can find somebody 
else for review in the meantime.

> I would also like to know how you trigger the QQmlEngine warning signals
> and methods from pure JavaScript. Those methods could just as well be
> moved to QJSEngine if they make sense there. However, so far they're
> actually meant for QML warnings.

Right, normally when evaluating a script, any error can be caught and reported 
however you wish, but this does not work when the script execution is trigger 
through a signal connection. In this case, when using QJSEngine, errors are 
always printed to the standard out, whereas the QQmlEngine allows reporting 
these through its "warnings" signal.

Cheers,
Thorbjørn
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Ulf Hermann

This is very promising, but unfortunately I could only get it to work
with QJSEngine and not with QQmlEngine. With the latter, the Qt
object stubbornly still only provides various functions.


Indeed, in QQmlEngine the global object is frozen. You cannot replace
its properties. Apparently you can still add to it, though. But see
https://codereview.qt-project.org/c/qt/qtdeclarative/+/418536/1 for a
possible solution.

I would also like to know how you trigger the QQmlEngine warning signals
and methods from pure JavaScript. Those methods could just as well be
moved to QJSEngine if they make sense there. However, so far they're
actually meant for QML warnings.

best regards,
Ulf
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-24 Thread Thorbjørn Lindeijer
Dear Ulf,

On Thu, Jun 23, 2022, at 23:21, Ulf Hermann wrote:
> In Qt5 if we have a QQmlEngine, each and every Qt namespace enum value 
> is added as a property to the Qt object, keyed by a newly created 
> QString, wrapped into a JS string, and the whole thing every time you 
> query one of those enums (until you query something that doesn't exist). 
> You may guess why I've removed this particular piece of code.

I fully understand why it would be desirable to remove such a kludge, though I 
might have expected a bit more consideration for applications that relied on 
these values. But maybe my use-case is pretty rare. Thanks a lot for your 
elaborate explanation and the suggested workarounds!

> If you want to work around the problem, there are a number of ways to 
> extend the JavaScript environment. For example:
>
>  QJSEngine engine;
>  engine.installExtensions(QJSEngine::AllExtensions);
>  engine.globalObject().setProperty(
>  QStringLiteral("QtNamespace"),
>  engine.newQMetaObject(::staticMetaObject));
>  engine.evaluate(QStringLiteral("console.log(QtNamespace.Checked)"));

That worked, thanks! However, for compatibility I really need both the 
functions and the enums, so I also tried the neat trick:

>  QJSEngine engine;
>  engine.installExtensions(QJSEngine::AllExtensions);
>  QJSValue qtObject = engine.globalObject().property("Qt");
>  QJSValue qtNamespace = engine.newQMetaObject(::staticMetaObject);
>  qtNamespace.setPrototype(qtObject);
>  engine.globalObject().setProperty("Qt", qtNamespace);
>  engine.evaluate(QStringLiteral("console.log(Qt.Checked)"));

This is very promising, but unfortunately I could only get it to work with 
QJSEngine and not with QQmlEngine. With the latter, the Qt object stubbornly 
still only provides various functions.

I'm relying on QQmlEngine rather than a QJSEngine, because only the QQmlEngine 
provides basic functions to control where output goes, like 
QQmlEngine::setOutputWarningsToStandardError (which I use to disable this) and 
the QQmlEngine::warnings signal (which I use to route any JS errors to a 
Console window inside the application).

Would there be any way to get this working with a QQmlEngine?

Alternatively, I guess I could open a feature request for the errors/warnings 
related API to be made available at the QJSEngine level. Though I would 
eventually like to support loading QML extensions as well.

> Now, it wouldn't be all that hard to do the above prototype trick 
> already when adding the Qt object to the JS global object. I need to 
> check if it opens new compatibility pitfalls, though.

That'd be nice, and hopefully it would work for both QJSEngine and QQmlEngine. 
But yeah, it's a bit hard to foresee the consequences.

Cheers,
Thorbjørn
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-23 Thread Ulf Hermann

Thanks for pointing this out!


 .import QtQml 2.15 as CoreQML
 console.log(CoreQML.Qt.Checked)

yields:

  At line 2: ReferenceError: CoreQML is not defined

Is that something I should report as a bug? I'd appreciate any other potential 
workarounds.


The .import dance as shown above only works if the JS file is itself 
imported from a QML file. Otherwise any .import statement simply does 
nothing. If that's a bug then it has been a bug for a long time. It 
should probably just be documented to work that way. If you're importing 
the JS file from a QML file, then most likely QtQml is already in scope, 
which makes the whole thing pointless.


There are some interesting aspects to the availability and members of 
the Qt object in Qt5 and Qt6:


With QJSEngine, you generally don't get a Qt object at all. You can 
trigger the creation of the Qt object by installing 
QJSEngine::TranslationExtension, though. In Qt5 that gives you an object 
with pretty much only the "uiLanguage" property. In Qt6 you get a Qt 
object with all properties and methods, but without the Qt namespace enums.


With QQmlEngine, in Qt5 you always get the complete Qt object, while in 
Qt6 at first you get the same thing as with QJSEngine and the 
translation extension. Once the QtQml module is imported, you get the 
namespace enums, too.


Now, how did this happen?

In Qt5 if we have a QQmlEngine, each and every Qt namespace enum value 
is added as a property to the Qt object, keyed by a newly created 
QString, wrapped into a JS string, and the whole thing every time you 
query one of those enums (until you query something that doesn't exist). 
You may guess why I've removed this particular piece of code.


In Qt6 once the QtQml module is in scope, the "Qt" name refers to 
something else: The QtQml module exposes the same Qt object as a 
singleton also named "Qt", with an extension that is the Qt namespace. 
Singletons rank above the global object in precedence. That's the secret 
sauce on how the enums are added.


If you want to work around the problem, there are a number of ways to 
extend the JavaScript environment. For example:


QJSEngine engine;
engine.installExtensions(QJSEngine::AllExtensions);
engine.globalObject().setProperty(
QStringLiteral("QtNamespace"),
engine.newQMetaObject(::staticMetaObject));
engine.evaluate(QStringLiteral("console.log(QtNamespace.Checked)"));

You may also want to use QJSEngine::registerModule() if you're working 
with ECMAScript modules.


Finally, I just came up with this neat trick:

QJSEngine engine;
engine.installExtensions(QJSEngine::AllExtensions);
QJSValue qtObject = engine.globalObject().property("Qt");
QJSValue qtNamespace = engine.newQMetaObject(::staticMetaObject);
qtNamespace.setPrototype(qtObject);
engine.globalObject().setProperty("Qt", qtNamespace);
engine.evaluate(QStringLiteral("console.log(Qt.Checked)"));

This pretty much gives you the Qt5 Qt object, probably at a lower cost.

I haven't checked how fast the above is, but it probably doesn't 
repeatedly create strings for all enum values in the Qt namespace.


Now, it wouldn't be all that hard to do the above prototype trick 
already when adding the Qt object to the JS global object. I need to 
check if it opens new compatibility pitfalls, though.


best regards,
Ulf
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-23 Thread Thorbjørn Lindeijer
On Thu, Jun 23, 2022, at 14:25, Andrei Golubev wrote:
> AFAIK this was done on purpose [1]. You need to import the QtQml module 
> inside your JS file e.g.
>
>   .import QtQml 2.15 as CoreQML
>
> Then accessing the Qt object via CoreQML.Qt should give you the enumerations.
>
> [1]: https://codereview.qt-project.org/c/qt/qtdeclarative/+/319662 
> merged as cbe1869fbe7048f34513b201a1d9111a0a64257a

This method was suggested on the Discord as well, but unfortunately it does not 
work. Trying to evaluate this script using QJSEngine::evaluate:

.import QtQml 2.15 as CoreQML
console.log(CoreQML.Qt.Checked)

yields:

 At line 2: ReferenceError: CoreQML is not defined

Is that something I should report as a bug? I'd appreciate any other potential 
workarounds.

Thanks,
Thorbjørn


>
> --
> Best Regards,
> Andrei
> *From:* Interest  on behalf of 
> Thorbjørn Lindeijer 
> *Sent:* Monday, June 20, 2022 5:02 PM
> *To:* ;interest@qt-project.org;;; 
> *Subject:* [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no 
> longer available 
> 
> Dear list,
>
> I'd like to upgrade the releases of my application from Qt 5.15 to Qt 
> 6.2, but I'm met with a loss in functionality that I'm not sure how to 
> resolve.
>
> My application is scriptable using QQmlEngine, and with Qt 5, the 
> loaded JS files had the global Qt namespace values available as part of 
> the "Qt" variable:
>
>> Object.keys(Qt)
> Asynchronous,Synchronous,color0,color1,black,white,darkGray,gray,lightGray,red,green,blue,cyan,magenta,yellow,darkRed,darkGreen,darkBlue,etc.
>
> The list goes on and on, so I've truncated it. But now with Qt 6, only 
> a handful of members remain, basically none of the global namespace 
> values are available anymore:
>
>> Object.keys(Qt)
> objectName,application,platform,inputMethod,styleHints,callLater,uiLanguage,objectNameChanged,include,isQtObject,color,rgba,hsla,hsva,colorEqual,rect,point,size,vector2d,vector3d,vector4d,quaternion,matrix4x4,lighter,darker,alpha,tint,formatDate,formatTime,formatDateTime,locale,url,resolvedUrl,openUrlExternally,font,fontFamilies,md5,btoa,atob,quit,exit,createQmlObject,createComponent,binding
>
> I did a quick test by loading a QML file, and in that file the value of 
> Qt.Checked is available:
>
> module.js:
>
> const f = __filename
> const c = Qt.createComponent(`${FileInfo.path(f)}/test.qml`)
> let window = c.createObject()
> tiled.log(`from JS module: ${Qt.Checked}`)
>
> test.qml:
>
> import QtQml
> QtObject {
> Component.onCompleted: {
> tiled.log(`from QML component: ${Qt.Checked}`)
> }
> }
>
> The output:
>
> Importing module '/home/bjorn/.config/tiled/extensions/modules.mjs'
> from QML component: 2
> from JS module: undefined
>
> Does anybody have any idea how I can make the Qt namespace values 
> available in JS files when using Qt 6? This is critical since I depend 
> on a number of these values in my scripting API.
>
> Thanks,
> Thorbjørn
> ___
> Interest mailing list
> Interest@qt-project.org
> https://lists.qt-project.org/listinfo/interest
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest


Re: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer available

2022-06-23 Thread Andrei Golubev
Hi,

AFAIK this was done on purpose [1]. You need to import the QtQml module inside 
your JS file e.g.

  .import QtQml 2.15 as CoreQML

Then accessing the Qt object via CoreQML.Qt should give you the enumerations.

[1]: https://codereview.qt-project.org/c/qt/qtdeclarative/+/319662 merged as 
cbe1869fbe7048f34513b201a1d9111a0a64257a

--
Best Regards,
Andrei

From: Interest  on behalf of Thorbjørn 
Lindeijer 
Sent: Monday, June 20, 2022 5:02 PM
To: ;interest@qt-project.org;;; 
Subject: [Interest] Qt 5 -> 6: In QQmlEngine, Qt.Checked, etc. are no longer 
available

Dear list,

I'd like to upgrade the releases of my application from Qt 5.15 to Qt 6.2, but 
I'm met with a loss in functionality that I'm not sure how to resolve.

My application is scriptable using QQmlEngine, and with Qt 5, the loaded JS 
files had the global Qt namespace values available as part of the "Qt" variable:

> Object.keys(Qt)
Asynchronous,Synchronous,color0,color1,black,white,darkGray,gray,lightGray,red,green,blue,cyan,magenta,yellow,darkRed,darkGreen,darkBlue,etc.

The list goes on and on, so I've truncated it. But now with Qt 6, only a 
handful of members remain, basically none of the global namespace values are 
available anymore:

> Object.keys(Qt)
objectName,application,platform,inputMethod,styleHints,callLater,uiLanguage,objectNameChanged,include,isQtObject,color,rgba,hsla,hsva,colorEqual,rect,point,size,vector2d,vector3d,vector4d,quaternion,matrix4x4,lighter,darker,alpha,tint,formatDate,formatTime,formatDateTime,locale,url,resolvedUrl,openUrlExternally,font,fontFamilies,md5,btoa,atob,quit,exit,createQmlObject,createComponent,binding

I did a quick test by loading a QML file, and in that file the value of 
Qt.Checked is available:

module.js:

const f = __filename
const c = Qt.createComponent(`${FileInfo.path(f)}/test.qml`)
let window = c.createObject()
tiled.log(`from JS module: ${Qt.Checked}`)

test.qml:

import QtQml
QtObject {
Component.onCompleted: {
tiled.log(`from QML component: ${Qt.Checked}`)
}
}

The output:

Importing module '/home/bjorn/.config/tiled/extensions/modules.mjs'
from QML component: 2
from JS module: undefined

Does anybody have any idea how I can make the Qt namespace values available in 
JS files when using Qt 6? This is critical since I depend on a number of these 
values in my scripting API.

Thanks,
Thorbjørn
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest
___
Interest mailing list
Interest@qt-project.org
https://lists.qt-project.org/listinfo/interest