Use Q_PROPERTY's from a costum class in QML - c++

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.

Related

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.

I would like to use the span slider from Qxt without having to install it. Is this possible?

I stumbled across a widget that provides you with a slider with two handles so you can select a range between an upper and lower limit.
I would like to use it without having to install all of Qxt though, since I am pretty sure this is the only thing that I need.
How can I deal with qxt_p() in order to use this widget as a standalone?
You need to add the following in the main class:
private:
QxtSpanSliderPrivate* d_ptr;
friend class QxtSpanSliderPrivate;
And in the following in the private class:
private:
QxtSpanSlider* q_ptr;
friend class QxtSpanSlider;
Also you should remove Qwt macros usage and replace qxt_d() and qxt_q() with direct access to q_ptr and d_ptr.
Each constructor of the main class should initialize both pointers:
QxtSpanSlider::QxtSpanSlider(Qt::Orientation orientation, QWidget* parent) :
QSlider(orientation, parent),
d_ptr(new QxtSpanSliderPrivate())
{
d_ptr->q_ptr = this;
//...
}
In case I forgot something, here is the gist. This code allowed me to successfully use QxtSpanSlider in Qt5.

Qt Extending my own widget

To put it simply, I want a new class that extends a custom widget that I've made, and thus have full access to it's UI.
I've tried several different methods so far based on how one would normally subclass/extend classes, but I'm somehow failing horribly for so many different reasons.
Additionally, simply using my widget as a member in my new class wouldn't do for this situation.
Can someone illustrate a quick example of how I would do this? I have done a bunch of searching but I can't seem to find any hits relating to exactly what I'm trying to do
If all else fails I will simply copy over the code and make an actual new widget, which technically would have saved me lots time, but it just doesn't feel right doing that.
My first instinct was to do something like this ( Qwe being my new class, Asd being the widget ):
class Qwe : Asd {public: ...}
And I even made the widget's Ui public, but then I just got the error :
use of undefine type Ui::Asd
whenever I tried to access the Ui's elements.
Let's say we have a custom widget named BaseWidget and a child widget named ChildWidget. Declare BaseWidget as usually, but make its ui member protected instead of private, like this:
protected:
Ui::BaseWidget *ui;
Declare ChildWidget as an ordinary widget derived from BaseWidget. Make sure you include ui_BaseWidget.h in the ChildWidget.cpp file, just as you do it in BaseWidget.cpp (this include and the header itself is generated by Qt).
Header:
#include "BaseWidget.h"
class ChildWidget : public BaseWidget {
Q_OBJECT
public:
explicit ChildWidget(QString text, QWidget *parent = 0);
};
Source:
#include "ChildWidget.h"
#include "ui_BaseWidget.h"
ChildWidget::ChildWidget(QString text, QWidget *parent) :
BaseWidget(parent)
{
ui->label->setText(text);
}

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

How to set user data for a QWidget?

I would like to set an integer number to be stored in a QWidget, and I think the setUserData member function would do the trick but I can't find any documentation. Any hints?
You might be looking for QObject::setProperty() (which is of course inherited by QWidget).
I am not an expert in QT but why not just create a class that inherits from QWidget and has an integer? Like so:
class MyDerivedWidget : public QWidget
{
public:
MyDerivedWidget();
private:
Data *myUserData;
};
Or if you insist on using the setUserData checkout the last post here.