IO/Archives /qt…

The QML engine is tightly integrated with the Qt Meta-Object System, and any member variable or function of a QObject-derived class can be accessed directly from QML with minimal (or no) processing — therefore, It is very simple to extend QML using C++ code.

The QML engine can implement introspect for QObject instances through the meta-object system. This means that any QML code can get the following members of a QObject derived class:

  • Properties
  • Method (either a public slot method or one marked with the Q_INVOKABLE macro)
  • signal

I didn’t think of how to translate Introspect. Introspect is similar to reflection in that it can obtain information about an object at run time, but it lacks the ability to modify the object.

In addition, enumeration types declared using the Q_ENUMS macro are also available. Read Data Type Conversion Between QML and C++ for more information

In general, a derived class of a QObject can be accessed from QML, whether or not it is registered with the QML type System. The class needs to be registered only if it needs the QML engine to obtain additional type information to use it — for example, if the class itself is to be used as a parameter or attribute of another function, or if the class’s enumerated type is to be used as such.

Some of the important concepts covered in this article are covered in the QML extension tutorial in C++, if necessary

Data type management and ownership issues

All data passed from C++ to QML — whether an attribute value, a method parameter, a return value, or a signal parameter value — must be of the type supported by the QML engine.

By default, the QML engine supports automatic conversion of some Qt C++ types to their corresponding QML types. In addition, C++ classes and enums registered with the QML type system can also be used as data types. See Data Type Conversion Between QML and C++ for more information.

Data ownership is also an issue to consider when transferring data from C++ to QML. When data is passed from C++ to QML, ownership of the data always remains in the C++ part. The exception is when the return value of an explicit C++ function call is QObject: In this case, unless explicitly in the c + + code by specifying QQmlEngine: : CppOwnership call QQmlEngine: : setObjectOwnership (), or other circumstances QML engine will have been returned to the ownership of the object.

Expose properties

A Property is a data member of a class that provides its own read (must) write (optional) function. We can use the Q_PROPERTY() macro to specify any QObject-derived class as a Property.

A property is a member of a class for which there are read and write functions. A look at the following code example might make it easier to understand

All attributes of QObject derived classes are accessible in QML.

Here is a Message class with an Author attribute. Author member variables can be read by the member function author() and new values can be written by the setAuthor() function. The author variable and its read and write functions are associated with calls to the Q_PROPERTY macro:

class Message: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a) {
        if(a! =m_author) { m_author = a;emit authorChanged(a); }}QString author(a) const {
        return m_author;
    }
singals:
    void authorChanged(a);
private:
    QString m_author;
}
Copy the code

If an instance of this class is set as a context attribute as follows when C++ loads myitem.qml

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    
    QQuickView view;
    Message msg;
    view.engine() - >rootContext() - >setContextProperty("msg", &msg);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show(a);return app.exec(a); }Copy the code

The author attribute can then be read in myitem.qml:

// MyItem.qml
import QtQuick 2.0

Text {
	width: 100; height: 100
	text: msg.author		// Call Message::author() to get the author value
	
	component.onCompleted: {
		msg.author = "Jonah"	/ / invoke Message: : setAuthor ()}}Copy the code

To maximize interoperability between C++ and QML, you can match all writable attributes with a NOTIFY signal that fires when the attribute value changes. Once such a signal is associated, the property can be used with property binding — a basic QML feature that forces the relationship between properties to be maintained by having the value of a property automatically change with the value of other properties on which it depends.

In the example above, the Q_PROPERTY() macro call specifies that authorChanged is the NOTIFY signal associated with the author attribute. This means that whenever a Message::setAuthor() call triggers a signal, it tells the QML engine: All other binding values associated with the author attribute must be updated once, and in turn, the QML engine uses Message::author() to update the text attribute once more.

If the author attribute is writable without setting the NOTIFY signal associated with it, the text value can still be changed by the initial value returned by Message::author() at initialization, but the value of the text attribute cannot be updated later when the author value changes. In addition, any attempt to bind the value from QML causes a runtime warning from the QML engine.

Note: It is recommended to name the NOTIFY signal Changed. Attribute change signals generated by the QML engine are all named on Changed, so naming custom signals according to the Changed rule avoids unnecessary confusion.

Precautions for using notification signals

To avoid loop evaluation and overevaluation, developers need to ensure that the property change signal is only triggered when the property value actually changes. Similarly, if a property or set of properties is not frequently used, you can actually have those properties share the same NOTIFY signal. Before you do this, you need to be careful not to cause serious performance problems.

It is important to note that using NOTIFY does incur some small performance overhead. In some cases, attribute values are fixed at object creation time and never change. For example, when a type uses group attributes, the group attribute object may be created once and then remain unchanged until the object itself is deleted. When declaring such an attribute, consider using CONSTANT instead of the NOTIFY signal.

The CONSTANT attribute should only be used for properties whose values are specified in the constructor. All other properties that need to be bound should still have a NOTIFY signal set.

Set of properties:

In some cases, an attribute may contain more than one subattribute, which can be assigned by dot or group. As an example, a Text type has a font group attribute, where the first Text object initializes font with a dot symbol and the second with a group symbol:

Text {
	/ / dot notation
	font.pixelSize: 12
	font.b: true
}

Text {
	/ / set of symbols
	font { pixelSize: 12; b: true}}Copy the code

A group attribute type is a basic type that contains multiple subattributes. Some of these basic types are provided by the QML language, while others may be provided by the imported Qt Quick module.

There are properties of object type

Properties of object types registered in the QML type system can be accessed directly in QML.

The Message type contains a child property MessageBody* :

class Message: public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
public:
    MessageBody* body() const;
    void setBody(MessageBody* body);
};

class MessageBody: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
    / /...
}
Copy the code

Assuming that the Message type is already registered in the QML type system, it can be used as an object type in QML code as follows:

Message {
	// ...
}
Copy the code

If the MessageBody type is also registered in the type system, we can assign MessageBody directly to the Message’s body property in QML code:

Message {
	body: MessageBody {
		text: "Hello, world!"}}Copy the code

There are properties of object list type

Attributes containing list types derived from QObject can also be exposed to QML. If you want to implement such an application, you need to use QQmlListProperty as the type of the attribute (only QQmlListProperty, not QList

) : This is because QList is not a derivative of QObject, so the Qt meta-object system does not provide enough attribute information for QML to do what it is supposed to do (for example, list is not derived from QObject, and therefore does not have the required notification signal).

QQmlListProperty is a template class that can be constructed using QList values.

For example, the following MessageBoard class has a messages property of type QQmlListProperty, which is a list of multiple Message instances:

class MessageBoard: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
public:
    QQmlListProperty<Message> messages();

private:
    static void append_message(QQmlListProperty<Message> *list, Message *msg);
    
    QList<Message *> m_messages;
}
Copy the code

The MessageBoard:: Messages () function creates a QQmlListProperty with the QList

m_messages member and returns it

QQmlListProperty<Message> MessageBoard::messages(a)
{
	return QQmlListProperty<Message>(this.0, &MessageBoard::append_message);
}

void MessageBoard::append_message(QQmlListProperty(Message) *list, Message *msg)
{
    MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
    if(msg)
        msgBoard->m_messages.append(msg);
}
Copy the code

Note that the template class type of QQmlListProperty (Message above) must be registered in the QML type system.

Grouped Properties

Any read-only object type attribute can be accessed from the QML as a Grouped property. This can be used to expose a set of interrelated properties of a type.

For example, suppose that the Message:: Author attribute is a MessageAuthor with the name and email sub-attributes, rather than a simple string, defined as follows:

class MessageAuthor: public QObject
{
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString email READ email WRITE setEmail)
public:... };class Message: public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageAuthor* author READ author)
public:
    Message(QObject *parent)
        : QObject(parent), m_author(new MessageAuthor(this))
    {
    }
    MessageAuthor *author(a) const {
        return m_author;
    }
private:
    MessageAuthor *m_author;
}
Copy the code

The author attribute can be assigned using QML’s group attribute syntax as follows:

Message {
	author.name: "Alexandra"
	author.email: "[email protected]"
}
Copy the code

Types exposed as group attributes differ from object type attributes in that:

  • The former is read-only and can only be initialized by the parent object during construction;
  • Subproperties of group properties can also be modified in QML, but the group properties themselves are not changed by such changes, as opposed to object type properties that can be modified at any time in QML.
  • Group attribute objects are strictly controlled by their C++ parent implementation, whereas object type attributes can be created and destroyed at any time in QML.

Exposure methods (including Qt slots)

Any function of qObject-derived type that meets the following criteria can be accessed from QML code:

  • useQ_INVOKABLE()The public function declared by the macro as a flag;
  • Public Qt slot functions

For example, the following MessageBoard class has a postMessage() function with a Q_INVOKABLE macro and a common slot function refresh() :

class MessageBoard: public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE bool postMessage(const QString &msg) {
        qDebug() < <"Called the C++ method with" << msg;
        return true;
    }
    
public slots:
    void refresh(a) {
        qDebug() < <"Called the C++ slot"; }}Copy the code

If an instance of MessageBoard is set to context data for myitem.qml, then the previous two functions can be called from myitem.qml as follows:

C++:

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    
    MessageBoard msgBoard;
    QQuickView view;
    view.engine() - >rootContext() - >setContextProperty("msgBoard", &msgBoard);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show(a);return app.exec(a); }Copy the code

QML:

// MyItem.qml
import QtQuick 2.0

Item {
	width: 100; height: 100
	
	MouseArea {
		anchors.fill: parent
		onClicked: {
			var result = msgBoard.postMessage("Hello from QML")
			console.log("Result of postMessage():", result)
			msgBoard.refresh()
		}
	}

}
Copy the code

If a C++ function has a parameter of type QObject*, the parameter value can also be passed to the function in QML as an object id or a JavaScript var variable that references that object.

QML also supports calling overloaded C++ functions. QML can call the correct C++ functions based on the number and type of arguments.

The return value of the C++ function after execution is converted to a JavaScript type value, which can be directly evaluated in QML.

Exposure signal

Any common signal of a qObject-derived type is accessible from QML.

The QML engine automatically creates a signal handler for the required signals. Signal handlers are named on

, where

is the Signal name with a capital letter. All parameters passed to the signal can be used directly by the parameter name in the signal handler.

For example, suppose the MessageBoard class has a newMessagePosted() signal with a subject parameter defined as follows:

class MessageBoard: public QObject
{
    Q_OBJECT
public:
    // ...
signals:
    void newMessagePosted(const QString &subject);
};
Copy the code

In the case that MessageBoard is already registered in the QML type system, An object declared by MessageBoard in QML can receive the newMessagePosted() signal through the onNewMessagePosted signal handler and value it from the parameters as follows:

MessageBoard {
	onNewMessagePosted: console.log("New message received:", subject)
}
Copy the code

Like attribute values and function parameters, signal parameters must be of the type supported by the QML engine, as described in data type conversions between QML and C++. Using an unregistered type directly does not get an error, but does not get a value.

A special note should be made in the case of multiple signals with the same name in a class: QML cannot distinguish between multiple signals with the same name and different parameters, and only the last of these signals can be retrieved by QML.