Is it possible to pass an instance of a derived `QAbstractListModel` from backend to QML without using `setContextProperty`?
-
Hello,
I'm trying to figure out how to pass an instance of a model to QML from the backend, but I don't want to use
setContextProperty
to expose said model.
Because I want to dynamically change the model the view is using.I know that I can register the model as a QML type and then instantiate it on the QML side, but doing this will lock me out of changing the model data from the backend,
because as far as I know, the backend doesn't have references to QML instantiated classes.I tried registering
QAbstractListModel
as a QML type and then pass it as a return value of a slot, like so:@Slot(result=VideoModel) def get_video_model(self): return self.video_model
And in QML
Shortcut { id: getModelShortuct sequences: ["Ctrl+W"] onActivated: { console.log("getModelShortuct activated") console.log("bridge.get_video_model()= " + bridge.get_video_model()) } }
Where
bridge
is the class that's exposed throughsetContextProperty
.
When trying this, I get "Error: Unknown method return type: QAbstractListModel* ". This also happens when registering the video model class specifically as a QML type, not justQAbstractListModel
Alternatively, I wonder if it's possible to expose a class through
setContextProperty
and have one of its class variables be a model and then change the value of the said class variable. For exampleclass ModelWrapper(QObject): def __init__(self): super().__init__() self.model = SomeModel() def switch_model(self): self.model = AnotherModel()
And in QML:
GridView{ id:myGridview model:ModelWrapper.model } Button{ id:modelSwitchButton onClicked{ ModelWrapper.switch_model() } }
-
So, answering my own question:
Do you mean like it's described here?
Yes, it is indeed how it's described here.
Also, do SomeModel and AnotherModel needs to be registered as a QML type, or is it enough to register QAbstractListModel if both of them derive from it?
No. Apparently, you need to specify that the 'return' type is just a
QObject
neitherQAbstractListModel
nor its derived classes need to be registered.
This is what I ended up doing to test this:
class CurrentView(QObject): def __init__(self): super().__init__() self.person_model: PersonModel = PersonModel() self.video_model: VideoModel = VideoModel() self.search_backend: SearchBackend = SearchBackend(self.person_model, self.video_model) self._current_model = self.person_model # setting current model to this for test self.search_backend.video_search("", "Name", False) #fills models with data def _current_model(self): return self._current_model @Signal def current_model_changed(self): pass @Slot() def swap_model(self): #called from QML logger.info(f"Swapping Model...") if self._current_model == self.video_model: self._current_model = self.person_model else: self._current_model = self.video_model self.current_model_changed.emit() #the return type needs to be QObject. #At first I tried QAbstractListModel or it's derived classes. Didn't work. current_model = Property(QObject, _current_model, notify=current_model_changed)
In QML:
Shortcut { id: swapModels sequences: ["Ctrl+W"] onActivated: { console.log("Swapping Models...") current_view.swap_model() } } GridView { id: gridview anchors.fill: parent model: current_view.current_model delegate: MyDelegate{} }
CurrentView is exposed through:
cw = CurrentView() engine.rootContext().setContextProperty("current_view", cw)
It works like a charm, when I press
Ctrl+W
the model instantly switches.This SO answer helped me figure out the missing pieces. Thank you @raven-worx for pointing me in the right direction.
-
@Curtwagner1984
sure, ifModelWrapper.model
is a property with a notifier signal -
Thank you for the reply.
Do you mean like it's described here?
Also, do
SomeModel
andAnotherModel
needs to be registered as a QML type, or is it enough to registerQAbstractListModel
if both of them derive from it? -
So, answering my own question:
Do you mean like it's described here?
Yes, it is indeed how it's described here.
Also, do SomeModel and AnotherModel needs to be registered as a QML type, or is it enough to register QAbstractListModel if both of them derive from it?
No. Apparently, you need to specify that the 'return' type is just a
QObject
neitherQAbstractListModel
nor its derived classes need to be registered.
This is what I ended up doing to test this:
class CurrentView(QObject): def __init__(self): super().__init__() self.person_model: PersonModel = PersonModel() self.video_model: VideoModel = VideoModel() self.search_backend: SearchBackend = SearchBackend(self.person_model, self.video_model) self._current_model = self.person_model # setting current model to this for test self.search_backend.video_search("", "Name", False) #fills models with data def _current_model(self): return self._current_model @Signal def current_model_changed(self): pass @Slot() def swap_model(self): #called from QML logger.info(f"Swapping Model...") if self._current_model == self.video_model: self._current_model = self.person_model else: self._current_model = self.video_model self.current_model_changed.emit() #the return type needs to be QObject. #At first I tried QAbstractListModel or it's derived classes. Didn't work. current_model = Property(QObject, _current_model, notify=current_model_changed)
In QML:
Shortcut { id: swapModels sequences: ["Ctrl+W"] onActivated: { console.log("Swapping Models...") current_view.swap_model() } } GridView { id: gridview anchors.fill: parent model: current_view.current_model delegate: MyDelegate{} }
CurrentView is exposed through:
cw = CurrentView() engine.rootContext().setContextProperty("current_view", cw)
It works like a charm, when I press
Ctrl+W
the model instantly switches.This SO answer helped me figure out the missing pieces. Thank you @raven-worx for pointing me in the right direction.
-
QAbstractListModel
as a property type or a return type should work, using QObject instead is not the correct solution.
Have you tried callingqRegisterMetaType
? -
@GrecKo said
using QObject instead is not the correct solution
and why?
Context properties also dont't require a type registration and still can be used as models. Since the cast / type-check anyway happens on the qobject instance. -
Because you lose the type information than can be used by the QML engine/compiler and Qt Creator.
Also root context properties should not be used anymore, access lookup is heavy for them. -
@GrecKo said in Is it possible to pass an instance of a derived `QAbstractListModel` from backend to QML without using `setContextProperty`?:
Have you tried calling qRegisterMetaType?
No, I have not. I tried
qmlRegisterType(QAbstractListModel, "com.videoModel", 1, 0, "VideoModel")
-
@GrecKo Could you elaborate on how to use qRegisterMetaType? And on what you mean by
root context properties should not be used anymore, access lookup is heavy for them.