Система свойств Qt
Qt предоставляет сложную систему свойств, аналогичную поставляемой некоторыми поставщиками компиляторов. Тем не менее, как библиотека, не зависящая от компилятора и платформы, Qt не полагается на нестандартные возможности компилятора, такие как __property или [property]. Решение Qt работает с любым стандартным компилятором C++ на всех поддерживаемых Qt платформах. Оно основано на мета-объектной системе, которая предоставляет связь между объектами посредством сигналов и слотов.
Требования для объявления свойств
Чтобы объявить свойство используйте макрос Q_PROPERTY() в классе, который унаследован от QObject.
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool])
Вот несколько типичных примеров объявлений свойств, взятых из класса 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. Для чтения значения свойства. Она должна быть константной и должна возвращать либо тип свойства либо указатель или ссылку на этот тип. Например, QWidget::focus - свойство только для чтения с функцией READ, QWidget::hasFocus().
- Функция доступа WRITE является необязательной. Для установки значения свойства. Она должна возвращать void и должна принимать ровно один аргумент, либо тип свойства, либо указатель или ссылку на этот тип. Например, QWidget::enabled имеет функцию WRITE, QWidget::setEnabled(). Свойствам только для чтения функции WRITE не нужны. Например, QWidget::focus не имеет функции WRITE.
- Функция RESET является необязательной. Она необходима для установки свойства в своё исходное конекстно-зависимое значение по умолчанию. Например, QWidget::cursor имеет типичные функции READ и WRITE, QWidget::cursor() иQWidget::setCursor(), и он также имеет функцию RESET, QWidget::unsetCursor(), так как отсутствие вызова QWidget::setCursor() может означать сброс к контекстно-зависимому курсору. Функция RESET должна возвращать void и не принимать параметров.
- Сигнал NOTIFY является необязательным. Если он определён, сигнал будет испускаться всякий раз, когда изменяется значение свойства. Сигнал должен получать один параметр, который должен быть того же типа, что и свойства; параметр получит новое значение из свойства.
- Атрибут DESIGNABLE указывает, будет ли свойство видимым в редакторе свойств инструмента проектирования ГПИ (например, Qt Designer). Многие свойства являются DESIGNABLE (значение по умолчанию - true). Вместо значений true или false, вы можете указать булеву функцию-член.
- Атрибут SCRIPTABLE указывает, будет ли это свойство доступно из механизма сценариев (значение по умолчанию - true). Вместо значений true или false, вы можете указать булеву функцию-член.
- Атрибут STORED указывает, будет ли свойство рассматриваться как существующее на своём или как зависимое от других значений. Он также указывает, должно ли записываться значение свойства при сохранении состояния объекта. Большинство свойств являются STORED (значение по умолчанию - true), но, например QWidget::minimumWidth() имеет STORED равным false, поскольку его значение просто получается из ширины компонента свойства QWidget::minimumSize(), которое является QSize.
- Атрибут USER указывает, является ли свойство обозначенным как показываемым пользователю или редактируемым пользователем свойством класса. Обычно, имеется только одно свойство USER в классе (значение по умолчанию - false). Например, QAbstractButton::checked является редактируемым пользователем свойством для (отмечаемых) кнопок. Обратите внимание на то, что QItemDelegate получает и устанавливает свойство USER виджета.
Функции 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)
Q_ENUMS(Priority)
public:
MyClass(QObject *parent = 0);
~MyClass();
enum Priority { High, Low, VeryHigh, VeryLow };
void setPriority(Priority priority);
Priority priority() const;
};
Функция READ является константной и возвращает тип свойства. Функция WRITE возвращает void и имеет ровно один параметр типа свойства. Эти требования навязывает мета-объектный компилятор.
Задав указатель на экземпляр MyClass или указатель на экземпляр QObject, который оказался экземпляром MyClass, мы имеем два способа установить его свойство priority.
MyClass *myinstance = new MyClass;
QObject *object = myinstance;
myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
В примере, используемый для типа свойства тип перечисления был локально объявлен в MyClass. Объявленный в другом классе, будет требоваться его полное уточнённое имя (т.е., 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.
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) |
Торговые марки |
Qt 4.5.3 |
|