Subclass of QAbstractListModel only available as superclass in QML - c++

I have a subclass of QAbstractListModel called ObjectListModel which contains a method remove():
class ObjectListModel : public QAbstractListModel
{
public:
Q_INVOKABLE void remove(int index);
}
However, when I try to call remove() from within QML, I get the following error:
TypeError: Property 'remove' of object QAbstractListModel(0x13c3c0) is not a function
I expose an instance of ObjectListModel through a property of MyApplication:
Q_PROPERTY(ObjectListModel *uploadModel
MEMBER _uploadModel
NOTIFY uploadModelChanged)
and I register the type as follows in main():
qRegisterMetaType<ObjectListModel*>("ObjectListModel*");
Any idea what is happening here?

You are missing the Q_OBJECT macro.
Also, you are registering incorrectly, you need something like:
qmlRegisterType<List>("ModuleName", 1, 0, "ObjectListModel");
QObjects are implicitly meta types, since they get the moc treatment (if you don't forget the macro). So there is no need to registering them as meta types.

The Q_OBJECT macro is missing.

Related

Use Q_PROPERTY's from a costum class in QML

I'm stuck with a "design/implemantation" problem in Qt.
At the moment I'm not even sure if thats a smart design...
That's my first post here and I don't really know where to start...
So I'll try is this way...
At the moment I have something like this:
class NewProperty : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
.
.
.
public:
NewProperty(const QString &name, QObject *parent = 0);
QString name()const;
void setName(const QString &name);
.
.
.
private:
QString m_s_name;
};
That's a "NewProperty" Class I want to have in "MyClass" cause in the end there will be much more than just a "name" Property ... The NewProject.cpp file is pretty basic at the moment...
And there will be also several MyClasses in the project.
My "MyClass" will have several "NewProperty" 's elements in the end...
But I'm not sure how to pass the "NewProperty" to QML in the/a right/nice way.
I tried to do something like this:
class QML_EMail : public Base_Output
{
Q_OBJECT
public:
NewProperty prop1;
NewProperty prop2;
.
.
.
};
main.cpp
...
qmlRegisterType<NewProperty> ("NewProperty", 1, 0, "NewProperty");
QML_EMail email
ctx->setContextProperty("email", QVariant::fromValue(&email));
...
If I try to call something like this in the QML file:
import NewProperty 1.0
Rectangle {
id: emailStart
Component.onCompleted:
{
console.log(email.prop1.name)
}
I only get this Message: TypeError: Cannot read property 'name' of undefined
I would appreciate any help or hints for better coding...
regards,
Moe
Welcome to Stack Overflow.
I don't think Qt properties can be used like that. If you want to access properties from QML the class (QObject based) members have to be defined with Q_PROPERTY itself to be exposed by Qt's meta object system. So you can't simply use another class that also has properties in it like that.
Essentially you have nested objects with properties, so you also have to flag them as such, if you want to use them in QML. An easy solution is to use the MEMBER keyword if you don't need getters and setters:
Q_PROPERTY(NewProperty prop1 MEMBER prop1)
NewProperty prop1;
You still might have to expose your custom NewProperty class to the meta system if you want to use it like that as a property. See Creating Custom Qt Types for more info about custom types.

Should I use QVariant or MyCustomType* to pass an object from Qml to C++?

I have C++ class User
class User : public QObject
{
Q_PROPERTY(QString login READ login WRITE setLogin NOTIFY loginChanged)
Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
...
}
Also I have Qml SignIn form with button which calls this code when clicked:
var user = userComponent.createObject()
user.login = loginTextField.text
user.password = passwordTextField.text
signInInteractor.signIn(user)
SignInInteractor is a C++ class
class SignInInteractor : public QObject
{
Q_INVOKABLE void signIn(User* user);
Q_INVOKABLE void signIn(QVariant user);
...
}
And my question is should I use User* or QVariant as argument type? What advantages and disadvantages they have?
Your custom type is QObject derived, so you can easily work on it on a QObject * level from QML. You will be able to access properties, slots or invokables, and functions directly, without having to do anything extra.
You will however have to add the Q_OBJECT macro, which is currently missing from your code, so the types get the MOC treatment that will generate the necessary meta data for them, which is what QtQuick will be using for introspection.
If you pass it as a variant, it will be like an opaque pointer, you won't able to do much with it from QML other than to pass it around. It only makes sense when the type is unsupported by QML, and QObject is more like a first class citizen there.

QObject::sender() doesn't work properly in a slot

I want to make a button to stay pushed after a click. So I made a slot make_pushed which I try to use for that purpose. The button which was clicked on is identified by QObject::sender() method. But something goes wrong since it doesn't work.
QPushButton * size=new QPushButton("size",this);
connect(size, SIGNAL(clicked()), this, SLOT(make_pushed()));
void Window4::make_pushed()
{
QObject* sender = this->sender();
QPushButton* button = qobject_cast<QPushButton*>(sender);
button->setDown(true);
button->setText("Yep");
}
class Window4 : public QWidget
{
public:
Window4(QWidget * parent=0);
private slots:
void make_pushed();
};
There's a mistake in application output "QObject::connect: No such slot QWidget::make_pushed() in" , although everything compiles and the window appears. The problem is the slot is apparently not found, although it is in the same cpp file and in the header. And therefore when clicked, the botton neigher changes its text nor stays pushed.
You just forgot Q_OBJECT macro in class declaration http://doc.qt.io/qt-5/qobject.html:
Notice that the Q_OBJECT macro is mandatory for any object that implements signals, slots or properties. You also need to run the Meta Object Compiler on the source file. We strongly recommend the use of this macro in all subclasses of QObject regardless of whether or not they actually use signals, slots and properties, since failure to do so may lead certain functions to exhibit strange behavior.
http://doc.qt.io/qt-5/qobject.html#Q_OBJECT:
The Q_OBJECT macro must appear in the private section of a class definition that declares its own signals and slots or that uses other services provided by Qt's meta-object system.
Note: This macro requires the class to be a subclass of QObject. Use Q_GADGET instead of Q_OBJECT to enable the meta object system's support for enums in a class that is not a QObject subclass.
Just use it like this every time you subclass QObject/QWidget/...:
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
// ...
}

QML property of property of C++ object

I am new to QML and have a problem in accessing a property.property of a C++ object:
C++, frequency and station both are Qt metatype registered objects:
CStation *station = new CStation(...); // QObject
CFrequency *frequency = new CFrequency(..); // QObject
QQmlContext *qmlContext = viewer.rootContext();
qmlContext->setContextProperty("myatcstation", station);
qmlContext->setContextProperty("myfrequency", frequency);
QML:
RowLayout { ....
TextField {
text: myatcstation.toQString(true)
}
}
.... text: myfrequency.toQString(true)
This works, but when I write: text: myatcstation.frequency.toQString(true) I do get TypeError: Object [object Object] has no method 'toQString'
frequency is a property of class CStation set as Q_PROPERTY(CFrequency frequency READ getFrequency)
Crosscheck in C++ works:
CFrequency test = station->property("frequency").value<CFrequency>();
-- Edit: Frank's answer --
Both classes are derived from QObject, and it is not as per textbook as they are made copyable. I am aware of the Identity vs value situation.
Basically frequency is a value object, but I have made it QObject based so I am able to use properties with it (see Any chance to use non QObject classes with QML ). In the example, toString is Q_INVOKABLE, frequency in the non-working case returns a copy of a QObject derived CFrequency object.
-- Edit: Further findings --
When I change the frequency property to return CFrequency* instead of CFrequency it does not work either. As I get TypeError: Cannot call method 'toQString' of undefined the situation seems to be the same. CFrequency alone works, but QML does not understand that myatcstation.frequency is a frequency object which has toString.
CFrequency isn't a QObject I assume, otherwise you wouldn't return it by value but by pointer. To make `toQString() callable from QML, it must be either Q_INVOKABLE or a slot, which means that CFrequency must be a QObject as well.
If a station has only one frequency, consider moving the relevant information into the station object, i.e. add the frequency information you need as properties to CStation.
To get updates when the frequency changes, consider to use a property such as Q_PROPERTY(QString frequencyAsString READ frequencyAsString NOTIFY frequencyAsStringChanged) instead of toQString(). Properties have the update mechanism "built-in" via property bindings, while there's no good way to tell QML that it should call toQString again because the frequency changed.
I solved a similar problem:
class TopObject : public QObject
{
Q_OBJECT
Q_PROPERTY(ValueObject* theValue ...
...
}
class ValueObject : public QObject
{
Q_OBJECT
Q_PROPERTY(QString value ...
...
}
In the main application:
qRegisterMetaType<ValueObject>("ValueObject");
qmlRegisterType<ValueObject>("com.name.comp", 1, 0, "ValueObject");
...->setContextProperty("topObject", new TopObject());
And in the qml code:
import com.name.comp 1.0
... {
text: topObject.theValue.value
...
It needed both, returning the property (ValueObject) as pointer and registering it with qmlRegisterType.
See also Exchange Data and Objects between C++ and QML and vice versa

Qt4 using a Class to create a SLOT to control a QWebView's setTextSizeMultiplier using CONNECT with a QSLIDER

Using Qt4, how do I create a Class to create a SLOT to control a QWebView's setTextSizeMultiplier using CONNECT with a QSLIDER.
My code: (thesliderbar is QSlider and vweb is QWebView)
class webextras
{
Q_OBJECT::Ui_ywr *pui;
public slots:
void wtresize(int wtr)
{
pui->vweb->setTextSizeMultiplier(wtr);
}
};
connect(thesliderbar,SIGNAL(valueChanged(int)),webextras,SLOT(wtresize(int)));
I'm getting errors for the connect();.
Errors:
ywr.cpp:31: error: expected primary-expression before ‘,’ token
ywr.cpp:-1: In constructor ‘ywr::ywr(QWidget*)’:
First in order your class to support signals/slots it must inherit from QObject.
Second you should use the macro Q_OBJECT which is needed from the moc tool:
The Meta-Object Compiler, moc, is the program that handles Qt's C++
extensions.
The moc tool reads a C++ header file. If it finds one or more class
declarations that contain the Q_OBJECT macro, it produces a C++ source
file containing the meta-object code for those classes. Among other
things, meta-object code is required for the signals and slots
mechanism, the run-time type information, and the dynamic property
system.
So your class should be:
class webextras : public QObject
{
Q_OBJECT;
public slots:
void wtresize(int wtr)
{
pui->vweb->setTextSizeMultiplier(wtr);
}
};
What is the Ui_ywr *pui? Q_OBJECT is a macro, you cannot declare it like the way you did.