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
setContextPropertyto 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
QAbstractListModelas 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_modelAnd in QML
Shortcut { id: getModelShortuct sequences: ["Ctrl+W"] onActivated: { console.log("getModelShortuct activated") console.log("bridge.get_video_model()= " + bridge.get_video_model()) } }Where
bridgeis 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 justQAbstractListModelAlternatively, I wonder if it's possible to expose a class through
setContextPropertyand 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
QObjectneitherQAbstractListModelnor 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+Wthe model instantly switches.This SO answer helped me figure out the missing pieces. Thank you @raven-worx for pointing me in the right direction.
-
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
setContextPropertyto 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
QAbstractListModelas 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_modelAnd in QML
Shortcut { id: getModelShortuct sequences: ["Ctrl+W"] onActivated: { console.log("getModelShortuct activated") console.log("bridge.get_video_model()= " + bridge.get_video_model()) } }Where
bridgeis 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 justQAbstractListModelAlternatively, I wonder if it's possible to expose a class through
setContextPropertyand 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() } }@Curtwagner1984
sure, ifModelWrapper.modelis a property with a notifier signal -
Thank you for the reply.
Do you mean like it's described here?
Also, do
SomeModelandAnotherModelneeds to be registered as a QML type, or is it enough to registerQAbstractListModelif 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
QObjectneitherQAbstractListModelnor 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+Wthe model instantly switches.This SO answer helped me figure out the missing pieces. Thank you @raven-worx for pointing me in the right direction.
-
QAbstractListModelas a property type or a return type should work, using QObject instead is not the correct solution.
Have you tried callingqRegisterMetaType? -
QAbstractListModelas 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. -
QAbstractListModelas a property type or a return type should work, using QObject instead is not the correct solution.
Have you tried callingqRegisterMetaType?@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.