CopperSpice API  1.7.2
Property System

CopperSpice provides a sophisticated property system. It is based on the Meta Object System.

Requirements for Declaring Properties

To declare a property use the CS_PROPERTY() macros in a class which inherits QObject.

CS_PROPERTY_READ(name, getFunction)
CS_PROPERTY_WRITE(name, setFunction)
CS_PROPERTY_RESET(name, resetFunction)
CS_PROPERTY_NOTIFY(name, notifySignal)
CS_PROPERTY_REVISION(name, int)
CS_PROPERTY_DESIGNABLE(name, bool)
CS_PROPERTY_SCRIPTABLE(name, bool)
CS_PROPERTY_STORED(name, bool)
CS_PROPERTY_USER(name, bool)
CS_PROPERTY_CONSTANT(name)
CS_PROPERTY_FINAL(name)

Here are some typical examples of property declarations taken from the QWidget.

CS_PROPERTY_READ(focus, hasFocus)
CS_PROPERTY_READ(enabled, isEnabled)
CS_PROPERTY_WRITE(enabled, setEnabled)
CS_PROPERTY_READ(cursor, cursor)
CS_PROPERTY_WRITE(cursor, setCursor)
CS_PROPERTY_RESET(cursor, unsetCursor)

A property behaves like a class data member but it has additional features accessible through the Meta Object System.

READ The read method is required. The read method must be const, take no arguments and return the property's type. For example QWidget::focus is a read-only property with read method QWidget::hasFocus().
WRITE The write method is uses for setting the property value. If no write method is specified the property is read only. It must return void and must take exactly one argument. For example, QWidget::enabled has the write method QWidget::setEnabled(bool).
RESET The reset method is optional. It is used to set the property back to its default value. The reset method must return void and take no parameters.
NOTIFY The signal is optional. If defined notify specifies one existing signal in that class that is emitted whenever the value of the property changes.
REVISION The number is optional. If included revision defines the property to be used in a particular revision of the API that is exposed to QML.
DESIGNABLE The designable attribute indicates whether the property should be visible in the property editor of GUI design tool. Most properties are designable. Instead of true or false, you can specify a boolean method or expression.
SCRIPTABLE The scriptable attribute indicates whether this property should be accessible by a scripting engine. The default is true. Instead of true or false, you can specify a boolean method or expression.
STORED The stored attribute indicates whether the property should exists on its own or is computed from other values. It also indicates whether the property value must be saved when storing the object's state. Most properties are stored. The default is true. For example, QWidget::minimumWidth() has stored set to false because its value is taken from the width component of property QWidget::minimumSize(), which is a QSize.
USER The user attribute indicates whether the property is designated as the user visible or user editable property for the class. Normally there is only one user property per class. The default is false. For example, QAbstractButton::checked is the user editable property for checkable buttons. Note that QItemDelegate gets and sets a widget's user property.
CONSTANT The presence of the constant attribute indicates that the property value is constant. For a given object instance, the read method of a constant property must return the same value every time it is called. This constant value may be different for different instances of the object. A constant property can not have a write method or a notify signal.
FINAL The presence of the final attribute indicates that the property will not be overridden by a derived class. This can be used for performance optimizations in some cases, but is not enforced. Do not override a final property.

The property type can be any type. In this example class QDate is considered to be a user-defined type. Because QDate is user-defined you must include the QDate header file with the property declaration.

CS_PROPERTY_READ(date getDate)
CS_PROPERTY_WRITE(date, setDate)

Reading and Writing Properties with the Meta Object System

A property can be read and written using the generic functions QObject::property() and QObject::setProperty() without knowing anything about the owning class except the property's name. In the code snippet below, the call to QAbstractButton::setDown() and the call to QObject::setProperty() both set property "down".

QPushButton *button = new QPushButton;
QObject *object = button;
button->setDown(true);
object->setProperty("down", true);

Accessing a property through its WRITE accessor is the better of the two, because it is faster and gives better diagnostics at compile time, but setting the property this way requires that you know about the class at compile time. Accessing properties by name lets you access classes you do not know about at compile time. You can discover the class properties at run time by querying its QObject, QMetaObject, and QMetaProperties.

QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i = 0; i<count; ++i) {
QMetaProperty metaproperty = metaobject->property(i);
const char *name = metaproperty.name();
QVariant value = object->property(name);
...
}

In the above snippet QMetaObject::property() is used to get metadata about each property defined in some unknown class. The property name is fetched from the metadata and passed to QObject::property() to get the value of the property in the current object.

A Simple Example

In the following example MyClass inherits from QObject and uses the CS_OBJECT macro. We want to declare a property in MyClass to keep track of a priority value. The name of the property will be priority and its type will be an enumeration named Priority.

We declare the property with the CS_PROPERTY() macro in the private section of the class. The READ function is named priority and the WRITE function is named setPriority. The enumeration type must be registered with the Meta Object System using the CS_ENUM() macro. Registering an enumeration type makes the enumerator names available for use in calls to QObject::setProperty(). We must also provide our own declarations for the READ and WRITE functions. The declaration of MyClass then might look like this:

class MyClass : public QObject
{
CS_OBJECT(MyClass)
CS_PROPERTY_READ(priority, priority)
CS_PROPERTY_WRITE(priority, setPriority)
CS_PROPERTY_NOTIFY(priority, priorityChanged)
CS_ENUM(Priority)
public:
MyClass(QObject *parent = 0);
~MyClass();
enum Priority { High, Low, VeryHigh, VeryLow };
void setPriority(Priority priority)
{
m_priority = priority;
emit priorityChanged(priority);
}
Priority priority() const
{ return m_priority; }
CS_SIGNAL_1(Public, void priorityChanged(Priority data))
CS_SIGNAL_2(priorityChanged, data)
private:
Priority m_priority;
};

The READ function is const and returns the property type. The WRITE function returns void and has exactly one parameter of the property type. The meta object compiler enforces these requirements.

Given a pointer to an instance of MyClass or a pointer to a QObject which is an instance of MyClass, we have two ways to set the priority property.

MyClass *myinstance = new MyClass;
QObject *object = myinstance;
myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");

In the example the enumeration type which is the property data type is declared in MyClass and registered with the Meta Object System using the CS_ENUM() macro. This makes the enumeration values available as strings for use as in the call to setProperty(). Had the enumeration type been declared in another class, its fully qualified name (i.e., OtherClass::Priority) would be required, and that other class would also have to inherit QObject and register the enumeration type there using the CS_ENUM() macro.

A similar macro, CS_FLAG(), is also available. Like CS_ENUM() it registers an enumeration type, but it marks the type as being a set of flags, i.e. values that can be OR'd together. An I/O class might have enumeration values Read and Write and then QObject::setProperty() could accept Read | Write. CS_FLAG() should be used to register this enumeration type.

Dynamic Properties

QObject::setProperty() can also be used to add new properties to an instance of a class at runtime. When it is called with a name and a value, if a property with the given name exists in the QObject, and if the given value is compatible with the property's type, the value is stored in the property, and true is returned. If the value is not compatible with the property's type, the property is not changed, and false is returned. However, if the property with the given name does not exist in the QObject, a new property with the given name and value is automatically added to the QObject. The return value will still be false. This means a return of false ca not be used to determine whether a particular property was actually set, unless you know in advance the property already exists in the QObject.

Note that dynamic properties are added on a per instance basis. A property can be removed from an instance by passing the property name and an invalid QVariant value to QObject::setProperty(). The default constructor for QVariant constructs an invalid QVariant.

Dynamic properties can be queried with QObject::property() just like properties declared at compile time with CS_PROPERTY().

Properties and Custom Types

Custom types used by properties need to be registered using the Q_DECLARE_METATYPE() macro so that their values can be stored in QVariant objects. This makes them suitable for use with both static properties declared using the CS_PROPERTY() macro in class definitions and dynamic properties created at run-time.

Adding Additional Information to a Class

Connected to the property system is an additional macro, CS_CLASSINFO(). This macro can be used to attach additional name / value pairs to a class meta object, for example:

CS_CLASSINFO("Version", "1.2.0")

Like other meta data, class information is accessible at runtime through the meta object. Refer to the QMetaObject::classInfo() for details.

See also
Meta Object System, Signals and Slots, CS_DECLARE_METATYPE(), QVariant.