I am new both QT and QML. I have a class, which instantiates subclasses. I would like to expose the methods within these subclasses to my UI with some qml binding.
For example
class MainClass: QObject
{
Q_OBJECT
Subclass subclass;
MainClass();
}
class Subclass:QObject
{
Q_Object
Subclass();
public slots:
void someMethod();
}
Main.C
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQuickView *view = new QQuickView;
MainClass mainclass;
view->rootContext()->setContextProperty("MainClass", &mainClass);
view->setSource(QUrl("qrc:/main.qml"));
view->show();
}
In this case I would like to access mainclass.subclass.someMethod() from within the QML UI.
Is there a good way to do this? Should I provide wrapper methods in MainClass for each function/property I would like to access.
It is possible by making SubClass a property of MainClass. For example:
class MainClass : public QObject
{
Q_OBJECT
Q_PROPERTY(SubClass *subClass MEMBER subClass CONSTANT)
public:
MainClass();
private:
SubClass *subclass;
};
These are called "grouped properties" in QML.
Related
i have a class like this:
class MyClass : public QObject
{
Q_OBJECT
public:
CircularList<unsigned char> buffer_[2];
explicit MyClass(QObject *parent = 0);
signals:
void dataReady(short *buff,int len);
};
and the other one is:
class WaveItem:public QQuickItem
{
Q_OBJECT
public:
WaveItem(QQuickItem *parent = 0);
public slots:
void setSamples(short *buff,int len);
protected:
QSGNode * updatePaintNode(QSGNode *node, UpdatePaintNodeData *data);
};
i need to connect this class in qml with signal(dataReady)/slot(setSamples). how is it possible?
If you check qt docs about exposing signals it describes very well.
First, you need to register your QObject derived class to QML engine.
qmlRegisterType<Myclass>("MyclassLib", 1, 0, "Myclass");
This way, you can create Myclass objects in QML.
But if you like to create objects in C++ and use that particular object in QML then you need to use QQmlContext::setContextProperty
QQuickView view;
Myclass myClass;
view.engine()->rootContext()->setContextProperty("myclass", &myClass);
After you registered type or set your object to QML, you can now use them.
Myclass {
onDataReady: waveItem.setSamples(buff, len);
}
Alternatively, you can also use connect(),
Myclass {
id: myClass
Component.onCompleted: myClass.dataReady.connect(waveItem.setSamples);
}
Note: You might also look to Connections.
I have a QWidget application that makes use of QML. I have a class that I'm using to expose some of our organizations utility functions.
I have boiled the problem down to the following code (I'll explain my problem below the code):
First, here is the main.cpp file (I've excluded most of the includes for brevity):
#include "main.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
Here is the included main.h:
class MyUtils : public QObject
{
Q_OBJECT
public:
MyUtils(QObject* parent = nullptr)
: QObject(parent)
{
}
virtual ~MyUtils() = default;
Q_INVOKABLE QString doSomething()
{
return QString("I did something!");
}
static QObject* MyUtilsProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
qDebug() << "MyUtils Invoked!";
return new MyUtils();
}
};
class MyView : public QQuickWidget
{
Q_OBJECT
public:
MyView(QWidget* parent = nullptr)
: QQuickWidget(parent)
{
setResizeMode(QQuickWidget::SizeRootObjectToView);
setSource(QUrl("qrc:/main.qml"));
}
virtual ~MyView() = default;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
QTabWidget _tabView;
public:
MainWindow(QWidget * parent = 0)
: QMainWindow(parent)
{
qmlRegisterSingletonType<MyUtils>("MyUtilities", 1, 0, "myutils", &MyUtils::MyUtilsProvider);
setCentralWidget(&_tabView);
_tabView.addTab(new MyView(), "Tab 1");
}
};
And last, here is my QML file:
import QtQuick 2.1
import MyUtilities 1.0
Rectangle
{
Text
{
text: myutils.doSomething()
anchors.centerIn: parent
}
}
What I am trying to do is register the MyUtils class as a singleton that I can then include in my QML and use. The problem is that when I run this, I get the following message from the Application's output:
QML debugging is enabled. Only use this in a safe environment.
Qml debugging is enabled. Only use this in a safe environment!
qrc:/main.qml:8: ReferenceError: myutils is not defined
I have tried putting qmlRegisterSingletonType in main(), before the instantiation of the QApplication object (and in various other places for giggles) but so far I have not been able to get this to work.
I have noticed that if I put a breakpoint or qDebug() message in the MyUtils::MyUtilsProvider method, it never gets called. This leads me to think that maybe my MyView class is using a different QQmlEngine object than the one in which the qmlRegisterSingletonType is registering the singleton with. But if that's the case, then I don't know how to get that engine to then pass to the MyView constructor.
Can someone please tell me what I am doing wrong and how I can get this to work?
Thank you!
QML component names must begin with capital letters:
qmlRegisterSingletonType<MyUtils>("MyUtilities", 1, 0, "Myutils",
&MyUtils::MyUtilsProvider);
and thus
text: Myutils.doSomething()
I figured out how to expose and bind an instance of a QAbstractListModel derived listmodel to/in QML.
But what I really want to do is to expose an object to QML and bind a member which is a QAbstractListModel derived listmodel as Q_PROPERTY.
I've tried it this way:
class MyObject : public QObject
{
Q_OBJECT
Q_PROPERTY(MyListModel myListModel READ myListModel NOTIFY myListModelChanged)
public:
explicit MyObject(QObject *parent = 0);
MyListModel *myListModel();
signals:
void myListModelChanged();
public slots:
private:
MyListModel *m_myListModel;
};
MyObject::MyObject(QObject *parent) :
QObject(parent)
{
m_myListModel = new MyListModel(this);
}
MyListModel *MyObject::myListModel()
{
return m_myListModel;
}
class MyListModel : public QAbstractListModel {
Q_OBJECT
//...
//...
}
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
QQuickView *view = new QQuickView();
MyObject *myObject = new MyObject();
view->engine()->rootContext()->setContextProperty("myObject", myObject);
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
return a.exec();
}
Rectangle {
width: 200
height: 200
//...
//...
ListView {
id: myListView
anchors.fill: parent
delegate: myDelegate
model: myObject.myListModel
}
}
But I'm getting an compile error:
E:\Qt\Qt5\5.1.1\mingw48_32\include\QtCore\qglobal.h:946: error: 'QAbstractListModel& QAbstractListModel::operator=(const QAbstractListModel&)' is private
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
^
How is the right way to do this?
QObjects like QAbstractItemModels cannot be copied, you must use a pointer. I’d use:
Q_PROPERTY(MyListModel* myListModel READ myListModel CONSTANT)
As you don’t replace the model itself, just its content, you don’t need the myListModelChanged() signal and can mark it as CONSTANT.
Your getter already has the right type, although it should be const:
MyListModel *MyObject::myListModel() const
I want to do something like this
QML app:
{
signal qmlSignal
function qmlFunction
}
And
c++ Hnadler:
{
c++ slot
c++ signal
}
Want to have two way communication with the same qml object.
I am referring http://qt-project.org/doc/qt-4.8/qtbinding.html
for changing a value in qml from C++, we can do
QDeclarativeEngine engine;
QDeclarativeComponent component(&engine, "MyItem.qml");
QObject *object = component.create();
QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
qDebug() << "QML function returned:" << returnedValue.toString();
delete object;
can be used to call qml function. But with this we cannot use QT method
And
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}
This can be used for qmlsignal and c++ slot.
Is there a way both of these can be done in one object?
=== Updated question ===
You can call slots or Q_INVOKABLE methods just fine in QML to change "C++ properties" from QML. You will need to expose the C++ object as a context property though. You would need to write something like this below:
myclass.h
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject *parent) : QObject(parent) { ... }
Q_INVOKABLE void myInvokable();
public slots:
void mySlot();
...
}
main.cpp
...
MyClass myClassObject;
QQuickView view;
view.rootContext()->setContextProperty("myClassContextProperty", &myClassObject;
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
...
main.qml
Button {
...
// You can replace onClicked with your own custom signal
onClicked: myClassContextProperty.myslot()
// or
onClicked: myClassContextProperty.myInvokable()
...
}
=== Further questions in the comment ===
You would have a Q_PROPERTY in C++, and you bind that property to your property in QML, or catch the onFooChanged signal handler for the property called "foo".
myclass.h
class MyClass : public QObject
{
Q_OBJECT
Q_PROPERTY(foo READ foo NOTIFY fooChanged)
public:
MyClass(QObject *parent) : QObject(parent) { ... }
int foo() const;
public signals:
void fooChanged();
public slots:
void mySlot() { foo = 5; emit fooChanged(); };
private:
int foo;
...
}
main.cpp
...
MyClass myClassObject;
QQuickView view;
view.rootContext()->setContextProperty("myClassContextProperty", &myClassObject;
view->setSource(QUrl::fromLocalFile("main.qml"));
view->show();
...
main.qml
...
// Bind foo to bar
property int bar: myClassContextProperty.foo
=== Original question ===
It seems that you are trying to ask if you can declare the same both in C++ and QML simultaneously, i.e. some parts of it in QML and then the rest in QML.
I think you would need to register (qmlRegisterMetaType, etc) your C++ type with the Qt meta type system, and then you could use that class as an item in QML (maybe with a parent?) to achieve what you wish.
This is the relevant documentation for further reading:
Registering an Instantiable Object Type
For your convenience, here goes some code inline:
message.h
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
// ...
};
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");
...
main.qml
import com.mycompany.messaging 1.0
Message {
author: "Amelie"
creationDate: new Date()
}
I have a big problem with my classes: I will use the Q_OBJECT macro in my subclasses. But if I define Q_OBJECT in my subclasses, it throws an exception. This is the exception:
undefined reference to `vtable for SubClassOne'
undefined reference to `vtable for SubClassTwo'
My SubClassOne and SubClassTwo inherits from BaseClass.
Here some Code: (All #includes are correct)
\\baseclass.h
class BaseClass
{
public:
BaseClass(QWidget *widget=0);
QHBoxLayout *mainLayout;
};
\\subclassone.h
class SubClassOne : public BaseClass, public QWidget
{
Q_OBJECT
public:
explicit SubClassOne(QWidget *widget=0);
};
\\subclasstwo.h
class SubClassTwo : public BaseClass, public QDialog
{
Q_OBJECT
public:
explicit SubClassTwo(QWidget *dialog=0);
};
Here comes the .cpp Files
//baseclass.cpp
BaseClass::BaseClass(QWidget *widget)
{
mainLayout = new QHBoxLayout();
}
//subclassone.cpp
SubClassOne::SubClassOne(QWidget *widget):BaseClass(widget)
{
setWindowTitle("Widget");
}
//subclasstwo.cpp
SubClassTwo::SubClassTwo(QWidget *dialog):BaseClass(dialog)
{
setWindowTitle("Dialog");
QPushButton *btn = new QPushButton();
QObject::connect(btn,SIGNAL(clicked()),SLOT(close()));
mainLayout->addWidget(btn);
setLayout(mainLayout);
}
main.cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SubClassTwo *s = new SubClassTwo();
s->show();
return a.exec();
}
How can I use Q_OBJECT in my subclasses?
Your Q_OBJECT placement is fine in your code.
What you are hitting is that you forgot to include the generated moc file in your source such as:
baseclass.cpp (at the end of the file)
#include "baseclass.moc"
subclassone.cpp (at the end of the file)
#include "subclassone.moc"
subclasstwo.cpp (at the end of the file)
#include "subclasstwo.moc"
You need to make sure moc is generating these files for you though. You have not shown your buildsystem yet.
Also, please make sure to have one header and source file per "Q_OBJECT" classes. It is not strictly necessary, but it is a good practice.
You can of course resolve that at link time as well, but you will need to do either of those.
Moreover, once you have multiple inheritance, at least with Qt 4, you will need to inherit from the QObject subclass first, which is QWidget in your case. You can find the correct inheritance below.
subclassone.h
class SubClassOne : public QWidget, public BaseClass
subclasstwo.h
class SubClassTwo : public QDialog, public BaseClass
etc. Hope it helps.
Q_OBJECT must be the firs declaration in your class and any class inheriting QObject must be first in the list
class SubClassOne : public QWidget, public BaseClass
{
Q_OBJECT
public:
explicit SubClassOne(QWidget *widget=0);
};
\\subclasstwo.h
class SubClassTwo : public QDialog, public BaseClass
{
Q_OBJECT
public:
explicit SubClassTwo(QWidget *dialog=0);
};
you also need to run the moc on the header to generate the code needed to implement the slots and signals