Система свойствQt предоставляет сложную систему свойств, аналогичную поставляемой некоторыми поставщиками компиляторов. Тем не менее, как библиотека, не зависящая от компилятора и платформы, Qt не полагается на нестандартные возможности компилятора, такие как __property или [property]. Решение Qt работает с любым стандартным компилятором C++ на всех поддерживаемых Qt платформах. Оно основано на мета-объектной системе, которая предоставляет связь между объектами посредством сигналов и слотов. Требования для объявления свойствЧтобы объявить свойство используйте макрос Q_PROPERTY() в классе, который унаследован от QObject. Q_PROPERTY(type name READ getFunction [WRITE setFunction] [RESET resetFunction] [NOTIFY notifySignal] [REVISION int] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL]) Вот несколько типичных примеров объявлений свойств, взятых из класса QWidget. Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor) Свойство ведёт себя как член данных класса, но оно имеет дополнительные возможности, доступные через мета-объектную систему.
Функции READ, WRITE и RESET могут быть унаследованы. Также они могут быть виртуальными. Когда наследуются в классах, где используется множественное наследование, они должны идти в самом начале наследуемого класса. Тип свойства может быть любым, поддерживаемым QVariant, или это может быть определяемым пользователем типом. В данном примере, класс QDate задуман быть типом, определяемым пользователем. Q_PROPERTY(QDate date READ getDate WRITE setDate) Поскольку QDate определяется пользователем, вы должны включить заголовочный файл <QDate> в объявление свойства. Для свойств QMap, QList и QValueList, значение свойства будет QVariant, чьим значением является весь список или отображение. Обратите внимание на то, что строка Q_PROPERTY не может содержать запятые, поскольку запятые разделяют аргументы макроса. Поэтому вы должны использовать QMap в качестве типа свойства вместо QMap<QString,QVariant>. В целях совместимости, используйте также QList и QValueList вместо QList<QVariant> и QValueList<QVariant>. Чтение и запись свойств с помощью мета-объектной системыСвойство можно читать и записывать используя общие функции QObject::property() и QObject::setProperty(), без знания чего-либо о классе-владельце за исключением имени свойства. В фрагменте кода ниже, вызов QAbstractButton::setDown() и вызов QObject::setProperty(), оба установят свойство "down". QPushButton *button = new QPushButton; QObject *object = button; button->setDown(true); object->setProperty("down", true); Получение доступа к свойству через его функцию доступа WRITE лучше второго способа, поскольку быстрее и даёт лучшую диагностику во время компиляции, но установка свойства этим способом требует от вас знания класса на этапе компиляции. Получение доступа к свойствам по имени позволяет вам получить доступ к классам, которые вы не знаете во время компиляции. Вы можете раскрыть свойства класса во время выполнения запросив их QObject, QMetaObject и 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); ... } В вышеприведённом фрагменте, QMetaObject::property() использовался для получения мета-данных о каждом свойстве, определённом в некотором неизвестном классе. Имя свойства получаем из мета-данных и передаём его в QObject::property(), чтобы получить значение свойства в текущем объекте. Простой примерПредположим, у нас есть класс MyClass, который унаследован от QObject и использует макрос Q_OBJECT в своей закрытой секции. Мы хотим объявить свойство в MyClass для отслеживания значения приоритета. Имя свойства будет priority, а его типом будет тип перечисления с именем Priority, который определён в MyClass. Объявляем в закрытой секции класса свойство с помощью макроса Q_PROPERTY(). Требуемую функцию READ с именем priority, а также включаем функцию WRITE с именем setPriority. Тип перечисления должен быть зарегистрирован в мета-объектной системе используя макрос Q_ENUMS(). Регистрация типа перечисления делает имена перечисления доступными для использования в вызове QObject::setProperty(). Мы также должны предоставить наши собственные объявления для функций READ и WRITE. Декларация MyClass тогда может выглядеть так: class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) Q_ENUMS(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; } signals: void priorityChanged(Priority); private: Priority m_priority; }; Функция READ является константной и возвращает тип свойства. Функция WRITE возвращает void и имеет ровно один параметр типа свойства. Эти требования навязывает мета-объектный компилятор. Задав указатель на экземпляр MyClass или указатель на экземпляр QObject, который является экземпляром класса MyClass, у нас имеется два способа установить его свойство priority: MyClass *myinstance = new MyClass; QObject *object = myinstance; myinstance->setPriority(MyClass::VeryHigh); object->setProperty("priority", "VeryHigh"); В примере тип перечисления, который является типом свойства, объявлен в MyClass и зарегистрирован в метаобъектной системе с помощью макроса Q_ENUMS(). Это делает значения перечисления доступными в виде строк для использования в вызове setProperty(). Объявленный в другом классе, будет требоваться его полное уточнённое имя (т.е., OtherClass::Priority), и этот другой класс также унаследует от QObject и зарегистрирует тип перечисления, используя макрос Q_ENUMS(). Также доступен аналогичный макрос Q_FLAGS(). Подобно Q_ENUMS(), он регистрирует тип перечисления, но отмечает тип набором флагов, т.е. значения, которые могут быть соединены через ИЛИ. Класс ввода/вывода должен иметь значения перечисления Read и Write, а затем QObject::setProperty() может принять Read | Write. Q_FLAGS() будет использован для регистрации этого типа перечисления. Динамические свойстваQObject::setProperty() также может быть использована для добавления новых свойств к экземпляру класса во время выполнения. Когда она вызывается с именем и значением, если свойство с данным именем существует в QObject и если данное значение совместимо с типом свойства, то значение сохраняется в свойстве, а возвращается true. Если значение не совместимо с типом свойства, то свойство не меняется, и возвращается false. Но если свойства с данным именем не существует в QObject (т.е., если оно не было объявлено с помощью Q_PROPERTY(), новое свойство с данным именем и значением автоматически добавится к QObject, но всё равно будет возвращено false. Это означает, что возврат false не может быть использовано для определения было ли определённое свойство действительно установлено, за исключением случая когда вы заранее знаете что свойство уже существует в QObject. Обратите внимание на то, что динамические свойства добавляются в базу экземпляра, т.е., они добавляются к QObject, а не к QMetaObject. Свойство может быть удалено из экземпляра передав имя свойства и неверное значение QVariant в QObject::setProperty(). Конструктор по умолчанию QVariant создаёт неверный QVariant. Динамические свойства могут быть запрошены с помощью QObject::property(), также как свойства объявленные во время компиляции с Q_PROPERTY(). Свойства и пользовательские типыПользовательские типы используемые свойствами необходимо регистрировать используя макрос Q_DECLARE_METATYPE(), чтобы их значения можно сохранить в объектах QVariant. Это делает их подходящими для использования и со статическими свойствами, объявленными с использованием макроса Q_PROPERTY() в определениях классов, и с динамическими свойствами, созданными во время выполнения. Добавление дополнительной информации в классПрикреплённые к системе свойств дополнительным макросом, Q_CLASSINFO(), который может использоваться для присоединения дополнительных пар имя--значение к мета-объекту класса, например: Q_CLASSINFO("Version", "3.0.0") Как и другие мета-данные, информация класса доступна во время выполнения через мета-объект; за подробностями обращайтесь к QMetaObject::classInfo(). Смотрите также Мета-объектная система, Сигналы и слоты, Q_DECLARE_METATYPE(), QMetaType и QVariant. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |