How to expose a C++ Model to QML - qt

I'm writing a QML+Qt application .
I defined a class like this :
class MainClass : public QObject
{
Q_OBJECT
public:
rosterItemModel m_rosterItemModel;
.
.
.
}
rosterItemModel model is a class derived from QAbstractListModel.
I exposed MainClass to qml part using this function :
qmlRegisterType<MainClass>("CPPIntegrate", 1, 0, "MainClass");
Now I want to assign this model(m_rosterItemModel) from MainClass to model property of a ListView in QML.
I tried the following ways but none of them were helpful :(
I tried to declare m_rosterItemModel as a PROPERTY using Q_PROPERTY .
I couldn't do that because it said that QAbstractListModel is not
copy-able.
I tried to get a pointer to m_rosterItemModel in qml file using a
Q_INVOKABLE function in MainClass. But it wasn't helpful too.
Could someone help me?

There shouldn't be any metatype registration necessary.
All you need to is to call setContextProperty and pass the model by pointer:
QQmlContext* context = view->rootContext(); //view is the QDeclarativeView
context->setContextProperty( "_rosterItemModel", &mainClassInstance->m_rosterItemModel );
Use it in QML:
model: _rosterItemModel
By pointer is important, as QObject's are not copy-constructible and copying them would break their semantics anyway (as they have an "identity").
The alternative to registering the model directly is to register your main class' instance and using Q_INVOKABLE. In MainClass:
Q_INVOKABLE RosterItemModel* rosterItemModel() const;
Registering an instance of mainClass (mainClassInstance again is assumed to be a pointer):
context->setContextProperty( "_mainInstance", mainClassInstance );
In QML:
model: _mainInstance.rosterItemModel()

Related

No such slot when connecting widget signal with parent widget slot

I have the following classes:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QStringList pluginsToStart, QWidget *parent = 0);
~MainWindow();
// some other stuff
public slots:
void on_timeDataChanged(logging::TimeValueVector<bool>& aData);
void on_importStarted();
}
and
class DataImporterWidget : public PluginWidget
{
Q_OBJECT
public:
explicit DataImporterWidget(QWidget *parent = 0);
~DataImporterWidget();
void initConnections(QMap<QString, PluginWidget*> pluginWidgetMap);
in the method initConnections, I want the widget to init the signal-slot connections like so:
void DataImporterWidget::initConnections(QMap<QString, PluginWidget*> pluginWidgetMap)
{
for(Importer* importer : this->getImporterMap().values())
{
connect(importer, SIGNAL(signal_timeDataChanged(logging::TimeValueVector<bool>&)),
parentWidget(), SLOT(on_timeDataChanged(logging::TimeValueVector<bool>&)));
}
connect(this, SIGNAL(signal_importStarted()), parentWidget(), SLOT(on_importStarted()));
}
Importer is a QGroupBox and a base class for derived sub classes specifying concrete data importer types.
It works like so: If I press a button, an DataImporterWidget is created and added to a QMdiArea as a QMdiSubWindow. When creating the DataImporterWidget I call the initConnections() method which sets up the signal-slot connections.
Now, when I run the program, I get the following message:
QObject::connect: No such slot
QMdiSubWindow::on_timeDataChanged(logging::TimeValueVector<bool>&) in src/dataimporter/DataImporterWidget.cpp:81
QObject::connect: No such slot QMdiSubWindow::on_importStarted() in src/dataimporter/DataImporterWidget.cpp:85
QObject::connect: (sender name: 'DataImporterWidget')
I do not understand why I get it because the slot is there. Even if I cast the parentWidget to the MainWindow, I get the same error.
PluginWidget is just a base class deriving from QWidget that holds some common functionality for my used plugins.
I put Q_OBJECT on each base and derived class but still get this error. However, if I set up the connections in the MainWindow, it works just fine, but I wonder why the above solution won't work.
Don't create the connection from child object, instead create it from parent object code after creating the child object.
This way you won't need to cast any type.
You did not shown a huge chunk of important code (like creating DataImporterWidget, setting MainWindow as its parent, the place where you call initConnections...). However, you said
If I use the new signal slot syntax, my program crashes with a
segmentation fault...
If it crashes, than you have to find a reason why. Using old signal-slot connect syntax does not cure the disease, it just delay its manifestation. According to this, the reason why you get a segfault can be parentWidget() == nullptr or parent is not initialized yet.
My advice, check your code, and make user the parent of DataImporterWidget is created and specified before your call initConnections().
I've found the problem. The reason is, that the MainWidget class holds a QMdiArea where I add my PluginWidgets. So, when I create the PluginWidget, I set the MainWidget as its parent, but as soon as I add it to the QMdiArea, it also becomes a child of QMdiSubWindow. The parentWidget was never null but it was the wrong one ...

Create custom C++ Object in QML and store it in a C++ Model

I have written a customer Class (inheriting from QObject) in C++ and registered it's type successfully with QML. Currently I'm creating objects of this class statically in C++ and storing a pointer to them in a Model which implements QAbstractListModel. In QML in I can access the objects perfectly as items of the Model.
The customObject is a non-visual object.
I'm visualising in another part of the GUI application (QML) the objects in a ListView with a delegate.
However now I would like to create objects from my custom Class dynamically in QML and store them also in the Model. This is where I'm struggling. I hoped I could create a customObject like this:
import com.myProject.myCustomStuff 1.0
...
Button{
id: createObjBtn
text: "create new CustomObj"
onClicked:{
var obj = MyCustomObj;
myObjectManager.addObj(obj); // object holding the implemented QAbstactListModel
console.log(typeof(obj)); // returns [Object object]
console.log(Qt.isQtObject(obj)) // returns false
}
}
I would appreciate your thoughts. Maybe someone knows a way to do this correctly?
Thanks!
Update 1
As requested by Simon-Warta, here is the Constructor implementation of MyCustomObj.
MyCustomObj.cpp
MyCustomObj::MyCustomObj(QObject *parent) : QObject(parent)
{
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}
You are confusing the functionality intent of the classes. The QAbstractListModel is intended as a wrapper around a container, yes, you could put the container inside the QAbstractListModel derived class, but you don't really have to, the container can be just about any C++ class, not necessarily even QObject derived, it can be just a QVector<Something> that you can reach from the model via a pointer. Which is good for cases where you have many objects, and not all need to have models at all the time, since those models are pretty heavy.
You don't really need to concern yourself with the ownership, leave that at the C++ side, the same goes for the actual object creation, have a slot called that adds the new object to the container while also using the model's beginInsertRows() and endInsertRows() so that any views will be notified to update efficiently, the new object should also be created in that slot, you can pass any data needed for it from QML, just make sure all data is registered with the Qt meta system so it can work with QVariant for QML-C++ interop.
So it should be something like:
myObjectManager.create(/* any needed data goes here */)
And create() passes eventual data to the C++ side, where you create the object, call beginInsertRows(), add the object to the model's underlying storage, then call endInsertRows() and you are done.
I'd prefer to keep the ownership in the C++ side (and I don't mean explicitly), where I have control over it. Qt kind of sucks in a big way when dealing with object ownership shared between C++ and QML. Ideally, there should be a single shared pointer class that will work across both C++ and QML, so the object is deleted once all reference to it are gone. But that is just not the "Qt way" - the Qt shared pointers do not work with QML, nor do the standard C++ shared pointers, there is actually an entirely different shared reference class for QML, which is not even part of the public API IIRC, very ugly and shortsighted design that only widens the gap between C++ and QML and the associated inconvenience factor.
I don't know if this is the shortest way but this should do for you. I am starting with the basics for those other up-voters.
MyCustomObj.h
class MyCustomObj : public QObject
{
Q_OBJECT
public:
// ...
Q_INVOKABLE void funfunction();
MyCustomObj.cpp
void MyCustomObj::funfunction()
{
qDebug("Fun with QML");
}
main.cpp
qmlRegisterType<MyCustomObj>("com.myProject.myCustomStuff", 1, 0, "MyCustomObj");
app.qml
import com.myProject.myCustomStuff 1.0
ApplicationWindow {
id: mainWindow
Component {
id: myComponent
MyCustomObj {
}
}
Component.onCompleted: {
var obj = myComponent.createObject(mainWindow)
if (!obj) console.error("Error creating object")
console.log(typeof(obj))
console.log(Qt.isQtObject(obj))
obj.funfunction()
}
}
createObject optionally takes properties to be passed to the component.
Storing
Since you are responsible for deleting the objects now, I'd recommend to use shared pointers, such that the objects get destroyed when the List is destroyed.
Your implementation of QAbstactListModel, let's call it MyModel has an adder function like that:
#include <memory> // for std::shared_ptr
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
// ..
Q_INVOKABLE addObj(MyCustomObj* obj)
{
objectlist_.append(std::shared_ptr<MyCustomObj>(obj));
}
private:
QList<std::shared_ptr<MyCustomObj>> objectlist_
}

call QML function from C++ with another QML object as parameter

I have a QML file Dialog.qml which is ApplicationWindow and Tab.qml which is Item. There are TabView in Dialog.qml which tabs is formed from Tab.qml. I want to create Tab.qml in Qt, connect its signals to classes in Qt and add that tab to Dialog. Here is relevant part of code:
//Tab.qml
Item {
id: tb
anchors.fill: parent
//...here is signals and controls like RadioButtons and TextFields
}
//Dialog.qml
ApplicationWindow {
visible: true
title: "settings"
flags: Qt.Dialog
//...
function addTabfromCpp(tab){
frame.addTab("from c++", tab);
}
TabView {
id:frame
//...
}
}
I know how to call QML functions from Qt when function parameters are primitive types(via QVariant). But how to call function when its parameter is QQuickWindow or other and it is not possible to create QVariant from it?
You can pass QML object ids as function parameters just fine. In C++ those work as QObject *. Also, besides ids you can use the parent property, or children[indexOfChild]. Note that you don't pass the actual objects, since that would require invoking a copy constructor, and copying is disabled for all QObject derived classes, it is passed by reference, which works in JS with var and in C++ with QObject*. In a similar fashion, you can use QObject* to pass objects to QML. Additionally, you might want to add your custom types to the Qt metatypes with qRegisterMetaType() or Q_DECLARE_METATYPE, which will give you autocomplete for that type in QML.
In order to expose C++ functions in the QML context, you need to make those functions either slots or Q_INVOKABLE of a QObject derived class, instantiate the class and use setContextProperty() to expose it to QML.
Or alternatively, you can register that object to the QML engine so you can instantiate it, you can even share the same data across multiple instances of that object by following this example.
EDIT: OK, try this one:
QML:
ApplicationWindow {
objectName: "blah"
//...
function foo(obj) { console.log(obj) }
}
CPP:
QObject * root = engine.rootObjects().at(0);
QMetaObject::invokeMethod(root, "foo", Q_ARG(QVariant, QVariant::fromValue(root)));
and output is
qml: ApplicationWindow_QMLTYPE_11_QML_12(0x4121e70, "blah")
Seems you have to pass it as a Q_ARG(QVariant, QVariant::fromValue(QObject*)
However, TabView.addTab() expects not an object (which is an instance of a Component) but a component, i.e. a prototype for an object.
One way you can do that is:
QObject * root = engine.rootObjects().at(0);
QQmlComponent comp(&engine, QUrl("qrc:/Test.qml"));
QMetaObject::invokeMethod(root, "addTab", Q_ARG(QVariant, QVariant::fromValue(&comp)));

Multiple inheritance of QObject because of access to deleteLater() method

I use observer-observable pattern in my program. Everything worked before I had to change the code a little. If to be exact I changed the inheritance of IObserver class - right now it inherits QObject:
class IObserver : public QObject
{
...
I did it because of only one thing - I need deleteLater() method to be used in an observer, so I would be able to call implementation of virtual function deinitialization() of IObserver. Thus I could standardize every IObserver message handler.
The problem is, I already inherited QObject (indirectly) in some Observer classes. Like MainForm or AboutDialog. Everything is going fine until I try to call "connect" method in AboutDialog class.
What can I do? I really need this deleteLater() method since I can't use "delete this" in IObserver code - this will call IObserver destructor, not the MainForm or Storage classes for instance.
Thank you.
I really need this deleteLater() method since I can't use "delete this" in IObserver code - this will call IObserver destructor, not the MainForm or Storage classes for instance.
If you make your destructor virtual (and you should!) it will call the derived destructor just fine. But a problem is that destructing a object while it is handling some signal/slot might cause problems with the event loop. You would have to be very careful with delete this anyway.
The problem is, I already inherited QObject (indirectly) in some Observer classes.
One way you could implement this, not sure if the best thought:
template <typename Derived>
class IObserver
{
// Just to be sure: (C++11)
static_assert(is_base_of<Derived, QObject>::value,
"must inherit from QObject when using IObserver");
void deleteMe()
{
QObject* thisObject = dynamic_cast<QObject*>(this);
// no need for check if thisObject equals null. static assert does this.
thisObject->deleteLater();
}
};
class MainForm : public IObserver<MainForm>, public QMainWindow
{
// ...
};
I believe this pattern is called static polymorphism.
Abandon inheritance of QObject for IObserver. Instead of that add such method to interface.
class IObserver : public QObject {
public:
QObject *object() const = 0;
...
Then if implementation of interface inherits the QObject you will return this pointer from object() method. If implementation of interface doesn't inherit QObject you can simply return pointer to some simple QObject which will handle destruction of this object.
Then you can simply connect deleteLater for object returned by this method.
Off topic
Use of interfaces for observing in Qt usually is obsolete, slots and signals do this job perfectly and this is more flexible approach.

Exposing child's properties in the parent

I want to create a custom widget that contains a QSpinBox. I want the custom widget to expose some of QSpinBox's properties as its own so it can work conveniently in Designer.
Is there a convenient way to do this kind of property proxying in Qt?
I want to reiterate that the relationship between my custom widget and the QSpinBox is containment, not inheritance. I am using pyqt, but will happily accept a pure qt answer.
To clarify, I want to create variations of standard widgets that have some extra decorations. For example, I will be adding a toggleable icon to sit beside my QSpinBox. However I still want to be able to configure the QSpinBox in designer (ie, set the suffix, upper and lower limits, increment, etc).
Alrite, so here's a way to do it.
It's better than writing a function manually for each property. Basically, you write a macro that expands the code for you.
I have a doubleSpinBox in my widget and its value is changed whenever I change the property. And you emit a signal whenever the property changes, which is connected to the doubleSpinBox->setValue(). Tested and works perfectly.
Complete code at this link.
#ifndef MYWIDGET_H_
#define MYWIDGET_H_
#include <QtGui/QWidget>
#include <QtGui/QLabel>
#include "ui_mywidgetform.h"
#define MYPROPERTY_SET_FUNC(_PROPCLASS_, _PROP_PARAM_NAME_, _PROP_SET_FUNC_NAME_, _PROP_NOTIFY_) \
void _PROP_SET_FUNC_NAME_(_PROPCLASS_ _PROP_PARAM_NAME_) \
{ \
emit _PROP_NOTIFY_(_PROP_PARAM_NAME_); \
}
#define MYPROPERTY_GET_FUNC(_PROPCLASS_, _PROP_READ_FUNC_NAME_, _PROP_VARIABLE_)\
_PROPCLASS_ _PROP_READ_FUNC_NAME_() const \
{ return _PROP_VARIABLE_; }
class MyWidget : public QWidget
{
Q_OBJECT
public:
MyWidget(QWidget *parent = 0);
~MyWidget();
enum Accuracy {Good, Bad};
signals:
void doubleSpinBoxValueChanged(double);
void accuracyChanged(Accuracy);
private:
Q_PROPERTY(Accuracy accuracy READ accuracy WRITE setAccuracy NOTIFY accuracyChanged)
Q_PROPERTY(double doubleSpinBoxValue READ doubleSpinBoxValue WRITE setDoubleSpinBoxValue)
private:
Accuracy m_accuracy; //custom class property
Ui::Form m_ui;
public:
//Getter functions
MYPROPERTY_GET_FUNC (double, doubleSpinBoxValue, m_ui.doubleSpinBox->value())
MYPROPERTY_GET_FUNC (Accuracy, accuracy, m_accuracy)
//Setter functions
MYPROPERTY_SET_FUNC(Accuracy, accuracy, setAccuracy, accuracyChanged)
MYPROPERTY_SET_FUNC(double, doubleSpinBoxValue, setDoubleSpinBoxValue, doubleSpinBoxValueChanged)
};
#endif // MYWIDGET_H_
Here is a PyQt version inspired by blueskin. The main operational difference here is that the custom widget class is composed at runtime rather than compile time. The advantage to this is that we can programmatically inspect the target metaobject and use that to generate the proxy properties.
Now PyQt is compiled from c++, so I have to believe that it is also possible to generate qt types at runtime in c++. I am betting that would be hellish, however.
from PyQt4 import QtGui, QtCore
def makeProxyProperty(childname, childtype, childpropname):
metaobject = childtype.staticMetaObject
metaproperty = metaobject.property(metaobject.indexOfProperty(childpropname))
def getter(self):
return metaproperty.read(getattr(self, childname))
def setter(self, val):
return metaproperty.write(getattr(self, childname), val)
return QtCore.pyqtProperty(metaproperty.typeName(), fget=getter, fset=setter)
class Widget1(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.spinbox = QtGui.QSpinBox()
self.checkbox = QtGui.QCheckBox()
layout = QtGui.QHBoxLayout()
layout.addWidget(self.checkbox)
layout.addWidget(self.spinbox)
self.setLayout(layout)
spinbox_suffix = makeProxyProperty("spinbox", QtGui.QSpinBox, "suffix")
spinbox_prefix = makeProxyProperty("spinbox", QtGui.QSpinBox, "prefix")
spinbox_minimum = makeProxyProperty("spinbox", QtGui.QSpinBox, "minimum")
spinbox_maximum = makeProxyProperty("spinbox", QtGui.QSpinBox, "maximum")
So to make things a bit clear,
you will be using this custom widget in the designer.
You need the custom widget to have properties that expose QSpinBox properties as the widget's properties
So,
basically you would want to create a property in your custom widget
using 'Q_PROPERTY' and use setter and getter methods to change the
QSpinBox's properties I hope this example would help
http://doc.qt.nokia.com/latest/properties.html#a-simple-example

Resources