0

Is there any way how to implement dynamical file-resolver for missing Qml components in QmlEngine? How to implement dynamically loading of external resources to QmlEngine?

I can use following snippet to load qml component from any data stream:

QUrl uri(...)
QByteArray data(...);
QQmlComponent::setData(data,uri);

but when passed component refers to another one (not already loaded), QmlEngine stopped because of missing resource.

Is there any event/callback where it is possible to handle such missing resource?

Added use-case scenario:

We're developing some QML visual components. Some components are implemented as .qml files and some as QQuickItem.

For example imagine following situation (it's very simplified):

  • QmlItem "DiagramPoint", implemented in point.qml
  • QmlItem "Line" implemented in line.qml, class is using "DiagramPoint" item
  • QQuickItem (c++) "ConnectedLine" which internally using "Line" object
  • QmlProject which using "ConnectedLine" components.

In case that point.qml and line.qml are located on hdd or stored inside Qt Resources, everything works automatically. But what we would like to achieve is to store these files in encrypted form in our internal .dat file and decode it only on demand.

We're able to decode and load "Line" object from "ConnectedLine" implementation. But in case that "Line" (line.qml) depends on another encrypted file "DiagramPoint" (point.qml), this solution doesn't work.

Another possible solution

Another solution could be to register all decrypted .qml files on application startup and than use it. Something simillar to qmlRegisterType which allows to register c++ QQuickItems to QmlEngine.

Unfortunately none of these methods allow to register Qml snippet from string buffer.

Ludek Vodicka
  • 1,610
  • 1
  • 18
  • 33
  • Did you have any success with the QQmlAbstractUrlInterceptor? I am also looking for a way to protect my QML code from plagiarism, and I considered the interceptor, but I assumed it only deals with urls, not with resolution of QML types which you use in QML declarative code. I consider a solution where `QQmlComponent`s are created from strings in memory, stored in the binary encrypted, but there doesn't seem to be a way to do that - it is like the QtQuick API was deliberately designed without a way to hide your code, not unless you pay for a commercial license and the QML compiler... – dtech Jan 26 '16 at 22:03
  • I mean while it is possible to create a `QQmlComponent` from data, it doesn't seem to be possible to register it as a QML type that can be used in QML sources. In my case different components also depend on each other. – dtech Jan 26 '16 at 22:43
  • Unfortunately there are too many difficulties when you will try to implement it with this way. Obviously Qt developers prepared all libraries only to single scenario - develop all in opened Qml and inject data from c++. For our purposes we decided to implement all base Qml objects as QQuickItems, register it to QmlEngine and than use Qml only as render engine and decorator for these QQuickItems. It seems to be the only efficient way. All other possibilities are based on creating objects from strings with QQmlComponent::setData but this looks absolutely wrong to me. – Ludek Vodicka Jan 27 '16 at 08:22
  • In case you will find any other way, could you please send me some sample? I spent tens of hours on internet when searching for reasonable solution but this was the only one. Unfortunately there is so little info about such advanced Qml usage. – Ludek Vodicka Jan 27 '16 at 08:24
  • I will be posting any progress here http://stackoverflow.com/questions/34814738/resolve-qml-types-using-a-custom-mechanism/35027003#35027003 – dtech Jan 27 '16 at 13:05
  • I have built the "inner core" in C++, but my project requires a lot of dynamism - rather than developing individual QML objects individually in C++ I have built a pool of functionality, which different QML components utilize. This allows for much faster development, and also to use code generation to create new QML types that are not "built in" - so in my case implementing different objects in C++ is unfortunately not an option. I am thinking of developing something akin to the `qmldir` approach, but using a single encrypted binary resource, I think it will work but it is not a priority yet. – dtech Jan 27 '16 at 13:13
  • Great, thanks for links and updates. Regarding your way, I think it's very similar. We also have several "behaviors" qml/c++ objects from which we're composeing the main application Qml objects. I think it's the best way. But what we're using is that each behavior object is defined as BehavorMovable.qml and part of logic is directly in Qml and part is in c++ as CBehaviorMovableImpl. This allow us to use the best of both worlds. For example MouseArea is a great Qml feature but other parts are better to do in c++. – Ludek Vodicka Jan 27 '16 at 17:48

1 Answers1

1

I'm still a bit unsure about how you would do this, but you might find QQmlAbstractUrlInterceptor useful:

QQmlAbstractUrlInterceptor is an interface which can be used to alter URLs before they are used by the QML engine. This is primarily useful for altering file urls into other file urls, such as selecting different graphical assets for the current platform.

Relative URLs are intercepted after being resolved against the file path of the current QML context. URL interception also occurs after setting the base path for a loaded QML file. This means that the content loaded for that QML file uses the intercepted URL, but inside the file the pre-intercepted URL is used for resolving relative paths. This allows for interception of .qml file loading without needing all paths (or local types) inside intercepted content to insert a different relative path.

Compared to setNetworkAccessManagerFactory, QQmlAbstractUrlInterceptor affects all URLs and paths, including local files and embedded resource files. QQmlAbstractUrlInterceptor is synchronous, and for asynchronous files must return a url with an asynchronous scheme (such as http or a custom scheme handled by your own custom QNetworkAccessManager). You can use a QQmlAbstractUrlInterceptor to change file URLs into networked URLs which are handled by your own custom QNetworkAccessManager.

To implement support for a custom networked scheme, see setNetworkAccessManagerFactory.

It says that its synchronous, so perhaps you could decode the QML files when the URLs are intercepted to ensure that they exist?

Community
  • 1
  • 1
Mitch
  • 23,716
  • 9
  • 83
  • 122
  • Thanks, I will try it. I didn't know about it. – Ludek Vodicka Jan 06 '16 at 22:38
  • It seems that QQmlAbstractUrlInterceptor together with custom QNetworkAccessManager is the correct way for this problem. Thanks reply. – Ludek Vodicka Jan 08 '16 at 14:30
  • Ha! Cool! So the decoding doesn't have any noticeable impact on the loading time of the QML then? – Mitch Jan 08 '16 at 15:22
  • To be honest, we made only small test-case which although works we didn't finally use. Whole hierarchy of Qml items was so complex and complicated that we decided to try another approach. Now we're implementing all base objects as c++ QQuickItems and everything seems to be a much easier. – Ludek Vodicka Jan 08 '16 at 19:45
  • Will likely be slightly faster, too. :) – Mitch Jan 08 '16 at 19:46
  • 1
    Definitely ;-). This is our first project which uses Qml and to find a correct way how to design the whole app architecture is very tricky. But it seems that to use Qml engine with c++ objects as core and than use Qml visual elements only to decorate objects is a good way to go. – Ludek Vodicka Jan 08 '16 at 20:53