Pass whole QAbstractTableModel to QML - c++

I have subclassed the QAbstractTableModel. Now I would like to pass to to the QML side. All examples I found expose the override methods of the class using Q_INVOKABLE, ie data or setData. Can the whole QAbstractTableModel object be passed as Q_INVOKABLE? If yes, how to do that exactly?

Q_INVOKABLE is meant for exposing methods of QObject derived types to QML. You can use Qt property system for exposing your QAbstractTableModel from your "global object" which you have made available to QML through QML context (as you commented under your question).
You can read from the documentation more about exposing Attributes of C++ Types to QML.
MyTableModel deriving from QAbstractTableModel:
class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
};
MyGlobalObject exposing MyTableModel member variable through the property system:
class MyGlobalObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QAbstractTableModel* myTableModel READ myTableModel CONSTANT)
public:
MyGlobalObject(QObject *parent = nullptr) : QObject(parent), m_myTableModel(new MyTableModel) { }
MyTableModel *myTableModel() { return m_myTableModel.data(); }
private:
QScopedPointer<MyTableModel> m_myTableModel;
};
MyGlobalObject instance set as a context property in main:
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
MyGlobalObject model;
engine.rootContext()->setContextProperty("myGlobalObject", &model);
}
MyTableModel used as model for QML TableView:
import QtQuick 2.12
TableView {
model: myGlobalObject.myTableModel
}

It is not safe to pass pointers from C++ to Qml via Q_INVOKABLE as the Javascript garbage collecter will try to delete those objects from QML even though they are supposed to be owned by C++. Instead, you should pass your objects (pointers) as properties using Q_PROPERTY macro.

Related

Handling with pages in qml

I've just begun to learn Qml.Altought i read too many qt tutorial, still struggling with some problems.
I want to make multipages desktop application using OpenGL.
First of all, in main function of the program, i am transmitting class instance so that i could access them in qml by using below code snippet.
QQmlApplicationEngine engine;
Foo foo;
engine.rooContext()->setContextProperty(QStringLiteral("foo"),&foo);
But if i must instance all of classes that i want to use in qml, it means there will be a hundred of instances in main function. I think there must be more proper way to do it.
Secondly, if we register object by using qmlRegisterType and import in qml file, can i reach property of bar class after active qml changed ? Because as far as i know, object of bar class is created when corresponding qml is loaded.
Project.cpp
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
qmlRegisterType<bar>("MyLib", 1, 0, "Comp");
qmlRegisterType<bar2>("MyLib2", 1, 0, "Comp2");
QQmlApplicationEngine engine;
Foo foo;
engine.rooContext()->setContextProperty(QStringLiteral("foo"),&foo);
.
.
.
}
GlWindow.qml
import QtQuick 2.0
import MyLib 1.0
Comp
{
id:sample
}
GlWindow2.qml
import QtQuick 2.0
import MyLib2 1.0
Comp2
{
id:sample2
}
bar.h
class bar: public QObject
{
Q_OBJECT
public:
Product* product;
void initialize();//Initialize Gl
void render(); //paint product's context
}
bar2.h
class bar2: public QObject
{
Q_OBJECT
public:
Product* product2;
void initialize();//Initialize Gl
void render(); //paint product's context
}
I painted content of product on GlWindow.qml after that closed this qml and showed GlWindow2.qml. My problem starts here, how to transmit content of product to product2?
For your first concern you can create a "main model" with properties for each of your models you would otherwise have added to the rootContext:
Class MainModel : public QObject
{
Q_OBJECT
Q_PROPERTY(Foo1 *foo1 READ foo1 CONSTANT)
Q_PROPERTY(Foo2 *foo2 READ foo2 CONSTANT)
....
Q_PROPERTY(FooN *fooN READ fooN CONSTANT)
}
If you fancy some design patterns, you can also go the kinda dependency injection rout:
Class Injector : public QObject
{
public:
Q_INVOKABLE QObject* getFoo(const QString& fooName);
}
This has the downside of losing the strong typed return value, but on the plus side you can use caching etc.
About your second question: you are correct, programming it like that it's hard to gain access from the C++ side (assuming that's what you mean) unless you make it a singleton:
class bar : public QObject
{
public:
bar *instance()
{
static bar theInstance;
return &theInstance;
}
};
//main.cpp
qmlRegisterSingleton<bar>("MyLib", 1, 0, "Comp", [](QQmlEngine *eng, QJSEngine *js) -> QObject*
{
return bar::instance();
});
But you probably tried that route because you didn't see the "main model" idea.
Also your last question about passing information between models can likely be easily solved by having a nice "main model" where classes know about each other, or can signal and the "main model" connects the signals and slots.

How to access QML loaded items from the callback for qmlRegisterSingletonType?

In my main QML file, I have defined a MediaPlayer. To have a low level access to the media buffer (through QAudioProbe), I need to obtain a reference to its mediaObject. My C++ backend interfaces with UI through a class registered by qmlRegisterSingletonType.
main.cpp
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterSingletonType<BackendInterface>("_", 0, 1, "Backend", backendInterfaceProvider);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
And here's the callback:
static QObject *backendInterfaceProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new BackendInterface(/* need a QMediaPlayer* here*/);
}
Question
How to access the QML heirarchy when I am creating my back-end interface (i.e. BackendInterface)?
Since you have a singleton type object, it wil be created on first usage, at which time your MediaPlayer object might not exist yet.
Instead of trying to retrieve the MediaPlayer from QML, make QML "register" the object with C++, i.e. my passing the object to the singleton.
Something like
class BackgroundInterface : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void registerMediaPlayer(QObject *player);
};
and
MediaPlayer {
id: mediaPlayer
Component.onCompleted: Backend.registerMediaPlayer(mediaPlayer)
}

Binding QList<Type*> to QListView

I have a custom type, called Patients (please ignore the plural mistake). I want to create a QList<Patients*> in my cpp and consume that from my QML. I am following the patterns from here, but it is not working.
Here is my patients.h (probably more info than needed) . . .
class Patients : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ getName)
Q_PROPERTY(QString email READ getEmail)
Q_PROPERTY(int id READ getId)
public:
explicit Patients(QObject *parent = 0);
explicit Patients(int id, QString name, QString email, QObject *parent = 0);
QString getName() const;
QString getEmail() const;
int getId() const;
private:
QString email, name;
int id;
};
Here is the main cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
QList<Patients*> lst;
lst.append(new Patients(0, QString("abe"), QString("albert")));
QQmlContext *ctx = engine.rootContext();
ctx->setContextProperty("pLst", QVariant::fromValue(lst));
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
Here is the qml . . .
ListView{
id: lst
height: 100; width: 100
anchors.fill: parent
x: 100; y: 100
model: pLst
delegate: Text{
text: model.modelData.name
}
}
It works when I bind a single object, but not as a list. Even presenting the index inside Text does not work. No error messages or anything.
There is no general support for the QList container in QML even if the type it is holding is registered to Qt's meta system. Some special variants (e.g. QStringList QList<QObject*>) are supported by default so you can use QList<QObject*> if your class is derived from QObject.
If your type is not derived from QObject or if you want to keep your interface in C++ clean and typesafe I suggest to use QQmlListProperty. Mind that you don't have to implement the append() or clear() method if you do not want to make the list modifiable from inside QML.
It turns out I must declare the list as
QList<QObject*> lst;
You will define new class with QQmlListProperty, and define a container data object(this class will inherit from QObject), in the class of QQmlListProperty you will define the container QList of pointers of QObject. I wrote two posts about this topic:
Show, change data and keep QQmlListProperty in qml
Use QQmlListProperty to show and modify QList in Qml

How to move class in the same thread

I'm trying the add threading in my app write in C++ and QT.
I have a class Framework and one name DeviceMngr.
The framework.h is defined as below:
class Framework : public QObject
{
Q_OBJECT
QThread FrameWorkThread;
public:
Framework();
The framework is initialized by the main. My Main is just doing :
QApplication app(argc, argv);
QThread FrameWorkThread;
Framework *DeviceFramework = new Framework;
DeviceFramework->moveToThread(&FrameWorkThread);
QObject::connect(&FrameWorkThread, SIGNAL(finished()), DeviceFramework, SLOT(deleteLater()));
After, the main in it the main Windows and give the DeviceFramework as argument.
MainUI MyWindows(*DeviceFramework);
MyWindows is discussing with DeviceFramework using Signal/slots.
Framework based is access to an android device using class DeviceMngr and methode.
How is it possible for me to add the DeviceMngr in the same Thread than the Framework.
Can I do something like this in the framework.cpp:
Framework::Framework()
{
Device = new DeviceMngr();
Device->moveToThread(&FrameWorkThread);
}
And the device manager declared as below :
class DeviceMngr : public QObject
{
QThread FrameWorkThread;
public:
DeviceMngr();
~DeviceMngr();
Is this method place the framework and device manager in the FrameWorkThread ?
Thanks
Sebastien
It is possible to have your DeviceMngr and Framework instances in the same thread. To do this, you'll need to keep the QThread instance you want to have in common between them in one place (or pass a pointer).
Do you have a reason for not making your DeviceMngr instance a child of Framework? The documentation for QObject says the following about the thread affinity of a child of a QObject instance:
The QObject::moveToThread() function changes the thread affinity for
an object and its children (the object cannot be moved if it has a
parent).
http://doc.qt.io/qt-5/threads-qobject.html
This would be the simplest way of getting both objects on the FrameWorkThread.
main.cpp
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QThread FrameWorkThread;
Framework *DeviceFramework = new Framework();
DeviceFramework->moveToThread(&FrameWorkThread);
QObject::connect(&FrameWorkThread, SIGNAL(finished()), DeviceFramework, SLOT(deleteLater()));
}
framework.hpp
class Framework : public QObject
{
Q_OBJECT
public:
Framework();
}
framework.cpp
Framework::Framework()
{
Device = new DeviceMngr(this);
}

Qt: How to put collection of GUI-Elements into independent SubClass (with seperate *.ui file)

I'm trying to collect an often used subset of GUI-Elements together into one Subclass, which can be "included" into the real GUIs later without rewriting the given functionality (don't ask why, I wanna learn it for later use). The Subclass should use it's own *.ui-File and should be put into an QWidget resding in the real GUI. After this, it would be nice to access some methods of the Subclass from the real GUI -- like the state of a button or so.
But how do I do this right?
In the moment, my Subclass works and is instantiated in main, but cannot be accessed from the real GUI because its only declared in main.
My Subclass Header-File:
class logger : public QWidget, private Ui::loggerWidget {
Q_OBJECT
public:
logger(QWidget *parent = 0);
virtual ~logger();
// some more stuff...
}
The corresponding constructor. I had to run setupUI with "parent" instead of "this", but I'm not sure that this is correct -- anyways, it works... otherwise, the subelements from the subclass are not shown in the main-window of the real GUI.
logger::logger(QWidget *parent) : QWidget(parent){
setupUi(parent);
//ctor
}
Inside the main.cpp the main-window is constructed, which uses it's own *.ui-File (containing one widget "widget_loggerArea") aswell. Doing so, I can not access methods of "logger" from within "loggerTest":
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
loggerTest window;
logger myLog(window.widget_loggerArea);
window.show();
return app.exec();
}
I can't put the constructor of "logger" into the constructor of the main-window "loggerTest", since it will be destroyed immidiately and never enters the event-loop.
I'm sure I'm missing some concept of object-oriented programming, or the way qt handles its stuff... I would be gratefull if someone could put my nose to this ;-)
I was so stupid... using a pointer with new and delete does the job... this is so silly, I can't believe it! I'm more used to VHDL recently, this weakens my C++-karma...
So, the answer is in the real GUI class. The Constructor:
testLogger::testLogger(QMainWindow *parent) : QMainWindow(parent){
setupUi(this);
myLog = new logger(widget_loggerArea);
}
In main.cpp:
QApplication app(argc, argv);
testLogger window;
window.show();
And in constructor of logger, setupUi works with "this":
dfkiLogger::dfkiLogger(QWidget *parent) : QWidget(parent){
setupUi(this);
}
Yes, thats it... Just for completebility, maybe someone needs a similar "push in the right direction"...
EDIT: In the header of the SubClass the scope of the ui-Elements has to be updated to "public", too:
class logger : public QWidget, public Ui::loggerWidget {
Q_OBJECT
public:
logger(QWidget *parent = 0);
virtual ~logger();
}