Unable to handle unregistered datatype - Qt - c++

everyone!
I am developing a code with Qt and many of my components have a similar behavior regarding changing colors. Therefore, I have made a class called ComponentState which inherits from QObject.
class ComponentState : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor ui_state_color READ getStateColor WRITE setStateColor NOTIFY onStateColorChanged)
}
One of my components that inherits from this class is called PneumaticLine.
class PneumaticLine : public ComponentState
{
Q_OBJECT
...
}
One of the views in my app (made in QML), has many of these PneumaticLine elements, so I decided to make a class called Controller to have a QList<PneumaticLine*> and export this using QQmlListProperty.
class Controller : public QObject
{
Q_OBJECT
Q_PROPERTY(QQmlListProperty<PneumaticLine> anti_ice_pneumatic_lines READ getAntiIcePneumaticLines)
...
QQmlListProperty<PneumaticLine> getAntiIcePneumaticLines() {
return QQmlListProperty<PneumaticLine> (this, &antiIcePneumaticLines);
}
private:
QList<PneumaticLine*> antiIcePneumaticLines;
}
Even though I declare the qml types in my main file, I still get the error:
QMetaProperty::read: Unable to handle unregistered datatype 'QQmlListProperty'<PneumaticLine'>' for property 'Controller::anti_ice_pneumatic_lines'
As a consequence of this error, I get the following in my qml file:
TypeError: Cannot read property '2' of undefined
I would like to access the List<PneumaticLine*> in my qml files. How can I do that?
OBS: I have declared the types PneumaticLine, ComponentState and Controller:
qmlRegisterType<ComponentState>("ECS", 1, 0, "ComponentState");
qmlRegisterType<PneumaticLine>("ECS", 1, 0, "PneumaticLine");
qmlRegisterType<Controller>("ECS", 1, 0, "Controller");
Controller controller;
engine.rootContext()->setContextProperty("Controller", &controller);
Thank you!

You need to include the newly created type in the meta object system:
Q_DECLARE_METATYPE(QQmlListProperty<PneumaticLine>)

You need to set function as Q_INVOKABLE for Q_PROPERTY can invoke that as:
Q_INVOKABLE QQmlListProperty<PneumaticLine> getAntiIcePneumaticLines();

Related

Access QML root object from C++ QML type

I have a custom QML type written in C++, the classname is MyCustomType and it's located in the files mycustomtype.h and mycustomtype.cpp.
In the main.cpp file the QML type is made available:
qmlRegisterType<MyCustomType>("MyCustomType", 1, 0, "MyCustomType");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("../qml/main.qml")));
In the main.cpp file I can get access to the engine's root object like this:
rootObject = static_cast<QQuickWindow *> (engine.rootObjects().first());
My issue is that I need access to that rootObject from within the MyCustomType class in the mycustomtype.cpp file. Is that possible?
The only way I could think of was to pass the rootObject to the constructor. But since the MyCustomType is instatiated in the QML document, and not in C++ code, this solution won't work.
Any ideas?
I found a solution based on GrecKo's comment.
Instead of making MyCustomType extend QObject, I made it extend QQuickItem. Then it's possible to call window() from anywhere in that class and get the root object. This is simple and it works.
mycustomtype.h:
class MyCustomType : public QQuickItem
{
Q_OBJECT
public:
explicit MyCustomType(QQuickItem *parent = 0);
}
mycustomtype.cpp
MyCustomType::MyCustomType(QQuickItem *parent) : QQuickItem(parent)
{
QQuickWindow *rootObject = window();
}
Method 1: Passing the root object
You can use setContextProperty to make a variable globally available in QML:
engine.rootContext ().setContextProperty("rootObject", QVariant::fromValue(engine.rootObjects().first()));
Now you can access the root object in QML using rootObject and pass it to MyCustomType during construction.
Method 2: Make the root object statically available
You can make the root object statically available. One clean solution is to create a singleton class with all your global data members.

How to use a custom Qt C++ type with a QML slot?

I want to use my C++ type registered in QML as parameter in QML slot. So I already have C++ signal which has QString type parameter:
emit newMessage(myString);
and QML slot:
onNewMessage: {
console.log(myString);
}
Now I want to pass C++ type registered in QML as parameter of this slot.
I found similar question, but I don't understand how I can use this vice versa. Any help will be useful.
So Lets say you wanted to pass a QObject derived file back and forth from C++ to QML via a signal/slot setup.
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = 0);
signals:
void sendSignal(MyClass* anotherMyClass)
public slots:
void send_signals() { emit sendSignal(this); }
};
If you have a C++ type called MyClass which is a QObject-based type, and you successfully did
qmlRegisterType<MyClass>("com.myapp", 1, 0, "MyClass");
Then In QML all you have to do is this:
import QtQuick 2.0
import com.myapp
Item {
MyClass {
id: myInstance
Component.OnCompleted: {
// connect signal to javascript function
myInstance.sendSignal.connect( myInstance.receiveSignal);
}
function receiveSignal(mySentObject) {
// here is your QObject sent via Signal
var receivedObject = mySentObject;
// Now to send it back
// (will cause an endless loop)
myInstance.sendSignal(receivedObject);
// or you could do this to perform
// the same endless loop
receivedObject.send_signals();
// or you could do this to perform
// the same endless loop
receivedObject.sendSignal(myInstance)
}
}
}
Summary:
You can just send any QObject-derived object as long as its registered with the QML engine as the actual object, you just treat it like any other type in QML
Hope this helps

Expose abstract type as Q_PROPERTY to QML

I am using Qt 4.8 with BB10.
I defined a base interface for classes to implement:
class AbstractImageProcessor : public QObject
{
public:
AbstractImageProcessor(QObject * parent) : QObject(parent) {}
virtual QImage process(const QByteArray &data) = 0;
virtual ~AbstractImageProcessor(){ }
};
One such implementation that I want usable from QML looks like this:
class WebImageProcessor : public AbstractImageProcessor
{
Q_OBJECT
Q_PROPERTY(int maxHeight READ getMaxHeight WRITE setMaxHeight NOTIFY maxHeightChanged)
Q_PROPERTY(int maxWidth READ getMaxWidth WRITE setMaxWidth NOTIFY maxWidthChanged)
Q_PROPERTY(bool fit READ isFit NOTIFY fitChanged)
public WebImageProcessor(QObject * parent = 0) : AbstractImageProcessor(parent) {}
virtual ~WebImageProcessor() {}
/* rest of code omitted */
};
I want to expose this AbstractImageProcessor as a property on another QML type:
class WebImageView : public bb::cascades::ImageView {
Q_OBJECT
Q_PROPERTY(AbstractImageProcessor* processor READ getProcessor WRITE setProcessor NOTIFY processorChanged)
WebImageView(bb::cascades::Container * parent) : bb::cascades::ImageView(parent) {}
virtual WebImageView() {}
/* rest of code omitted */
};
So I register my custom types with QML
//application.cpp
qmlRegisterUncreatableType<AbstractImageProcessor>("foo.controls", 1, 0, "AbstractImageProcessor", ""); ;
qmlRegisterType<WebImageProcessor>("foo.controls", 1, 0, "WebImageProcessor");
qmlRegisterType<WebImageView>("foo.controls", 1, 0, "WebImageView");
How I want to use it in QML
//main.qml
import foo.controls 1.0
/* omitted containers */
WebImageView {
processor: WebImageProcessor {
maxHeight: 500
maxWidth: 300
}
/* rest of properties omitted */
}
But once I launch my application it fails to parse the qml document.
bb::cascades::QmlDocument: error when loading QML from: QUrl( "asset:///main.qml" )
--- errors: (asset:///main.qml:138:57: Cannot assign object to property)
bb::cascades::QmlDocument:createRootObject document is not loaded or has errors, can't create root
In fact if I hover over the WebImageProcessor class in the editor, it says:
The super type of the component WebImageProcessor is unknown, some of its properties are not validated.
Now the thing is that the for example the built in cascades ListView exposes an abstract type as a Q_PROPERTY:
http://developer.blackberry.com/native/reference/cascades/bb_cascades_listview.html#property-datamodel
Event inspecting the header files of bb::cascades::ListView and bb::cascades::DataModel gives me no other clues because it's done essentially the same way.
Could it be that I have to register the types in a different way? If so how?
If I use WebImageProcessor in the Q_PROPERTY instead of the AbstractImageProcessor then it works as expected, but I want to expose the abstract type, and given that cascades does it then it's definitely possible somehow
Thanks
Your AbstractImageProcessor declaration is lacking a Q_OBJECT macro, which is necessary to export the class to QtQuick.

Why can't I access my other form's widgets in Qt?

So, I have the following code in my main window's Qt C++ form (under a button click slot):
newform *nf = new newform(this);
nf->show();
I want to be able to access a webview control I placed on the new form. After some research, I figured that calling nf->ui would be my best bet in order to gain access to all of newform's controls. So I went into newform.h and changed the *ui variable to public:
#ifndef NEWFORM_H
#define NEWFORM_H
#include <QMainWindow>
namespace Ui {
class newform;
}
class newform : public QMainWindow
{
Q_OBJECT
public:
explicit newform(QWidget *parent = 0);
~newform();
Ui::newform *ui;
};
#endif // NEWFORM_H
Yet, whenever I try calling nf->ui, a dropdown menu doesn't appear and I still can't get access to my webview. When I type my code anyway and try to run, I get:
error: invalid use of incomplete type 'class Ui::newform'
error: forward declaration of 'class Ui::newform'
What's going on? Am I doing something wrong? Any help is appreciated. Thanks in advance.
The errors are because you will need access to the ui class definition to call member functions and access the widgets it contains and that is a bad solution to cause such a dependency on that class internals.
So, don't try to access the ui (or the other members) directly, those are private and it's recommended that they stay that way, instead code the functionality you need into the newform class and make that class do the work that you need to be triggered from mainwindow class, something like:
class newform : public QMainWindow
{
Q_OBJECT
public:
explicit newform(QWidget *parent = 0);
~newform();
//code a member function (or a slot if you need a signal to trigger it)
//example:
void loadUrlInWebView(QUrl url);
private:
Ui::newform *ui; //leave this private - it's not a good solution to make it public
};
//and in the .cpp file
void newform::loadUrlInWebView(Qurl url)
{
//you can access the internal widgets here
ui->WEBVIEWNAME->load(url);
//do whatever you need here and you call this public function from other form
}

How to set event listner on a QML object received say from QDeclarativeView?

We had an object that we instatniated in our main programm and passed it into QML View. There were defined a Q_PROPERTY. I wonder how to set event listner on to NOTIFY signal?
If I understand correctly, this Qt DevNet forum post has a setup similar to yours. Only in that case they are defining the READ function which in turn emits the somethingHappened signal. Be sure to scroll all the way down to the last comments for the working example.
In summary, you have something like this defined in your C++ header file:
class SomeClass : public QObject {
Q_OBJECT
Q_PROPERTY(sometype someProperty READ getSomeProperty NOTIFY somePropertyChanged)
signals:
void somePropertyChanged();
};
QML_DECLARE_TYPE(SomeClass)
Something like this in your C++ main method:
qmlRegisterType<SomeClass>("SomeModule", 1, 0, "SomeClass");
SomeClass myObj;
QDeclarativeView view;
view.rootContext()->setContextProperty("rootItem", (SomeClass *)&myObj);
Then on the QML side you would handle it like this:
import SomeModule 1.0
SomeClass {
onSomePropertyChanged: {
// do stuff
}
}