Recently I was tasked to come up with an architecture for remote real time instantiation and updating of arbitrary QML components.
This entry shows how you can use a simple variation of the factory method pattern in QML for instantiating arbitrary components. I’ve split my findings into 3 blog entries, each one covering a slightly different topic. Part 1 focuses on the software design pattern used to dynamically instantiate components. Part 2 shows how to layout these dynamic components by incorporating QML' s positioning and layout APIs. The last entry, consisting of Parts 3 and 4, addresses the anchors API and important safety aspects.
This is Part 1: Recursive Instantiation with Qt Quick and JSON.
The original factory method pattern made use of static methods to programmatically instantiate objects of different classes, instead of having to call their constructors. It achieved that by having the classes share a common ancestor. Our variation of the popular pattern uses a Loader to choose which component to load, and a Repeater to dynamically instantiate arbitrary instances of this loader using a model.
Here we specify which components with a JSON array and use a Repeater to load them.
To be able to instantiate any kind of item, you can use a Component with a Loader inside, as the Repeater's delegate. This allows you to load a different component based on the Repeater's model data.
To assign values from the model to the component, add a method that gets called when the Loader's onItemChanged event is triggered. I use this method to take care of anything that involves the component's properties:
Examples of components that loaderComp could load are defined below. To enable recursion, these components must contain a Repeater that instantiates children components, with loaderComp set as the delegate:
The Repeater inside of the components allows us to instantiate components recursively, by having a branch or more of children components in the model, like so:
Here we've seen how we can use a Repeater, a JSON model, a Loader delegate, and simple recursive definition to instantiate arbitrary QML objects from a JSON description. In my next entry I will focus on how you can lay out these arbitrarily instantiated objects on your screen.
Thanks to Kevin Krammer and Jan Marker whose insights helped improve the code you've seen here.
I hope you've found this useful! Part 2 may be found already or later by following this link.
Reference
Trusted software excellence across embedded and desktop platforms
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
2 Comments
11 - Apr - 2024
Robert
Quite interesting. Seeing all that switches, I wonder how the code would scale. My first thought was that doing it via QQmlComponent::create on the C++ side would be more efficient, and also allow you to use a more elegant code design in assigning properties using the created component's QObject property interface. But I guess there is simply no public API to create many standard QML components like 'Text' via C++
11 - Apr - 2024
Javier Cordero
Certainly. You could also implement a function or binding for each Component in QML, that gets set from onItemChanged, allowing the components to assign their own values. This would be even closer to the original factory methods pattern. The video being referenced at the end of this article shows a different approach, where the properties are dynamically acquired from C++.