Создание пользовательских типов Qt
Краткий обзор
При создании пользовательского интерфейса с помощью Qt, в частности, специализированных элементов управления и возможностей, разработчики иногда нуждаются в создании новых типов данных, которые могут быть использованы вместе или вместо существующего набора типов-значений Qt.
Стандартные типы, такие как QSize, QColor и QString, могут быть сохранены в объектах QVariant, используемых в качестве типов свойство в классах на основе QObject, а также работать в канале связи сигнал-слот.
В этом документе мы создадим пользовательский тип и опишем как интегрировать его в объектную модель Qt таким образом, чтобы его можно сохранять обычным для стандартных типов Qt способом. Затем покажем, как зарегистрировать пользовательский тип чтобы разрешить его использование в канале связи между сигналами и слотами.
Создание пользовательского типа
Прежде чем начать, нужно убедиться, что создаваемый пользовательский тип отвечает всем требованиям, предъявляемым QMetaType. Другими словами он должен предоставлять:
- открытый конструктор по умолчанию,
- открытый конструктор копирования и
- открытый деструктор.
Следующее определение класса Message включает в себя следующие члены:
class Message
{
public:
Message();
Message(const Message &other);
~Message();
Message(const QString &body, const QStringList &headers);
QString body() const;
QStringList headers() const;
private:
QString m_body;
QStringList m_headers;
};
Класс также предоставляет конструктор для обычного использовани и две открытых функции-члена, которые используются для получения закрытых данных.
Объявление типа с помощью QMetaType
Чтобы быть пригодным для использования классу Message требуется только подходящая реализация. Однако без некоторой помощи система типов Qt не будет понимать как хранить, извлекать и преобразовывать в последовательную форму экземпляры этого класса. Например, мы не сможем сохранять значения Message в QVariant.
Классом, отвечающим за пользовательские типы в Qt, является QMetaType. Чтобы тип стал известен этому классу, мы вызываем макрос Q_DECLARE_METATYPE() в классе в заголовочном файле, где он определяется:
Q_DECLARE_METATYPE(Message);
Это делает возможным сохранять значения Message в объектах QVariant и позднее получать их. Демонстрирующий это код смотрите в примере "Custom Type".
Макрос Q_DECLARE_METATYPE() также делает возможным использование этих значений в качестве аргументов сигналов, но только прямых соединениях сигнал-слот. Чтобы сделать пользовательский тип в целом пригодным для работы с механизмом сигналов и слотов, необходимо проделать некоторую дополнительную работу.
Создание и уничтожение пользовательских объектов
Хотя объявление в предыдущем разделе сделает тип доступным для использования в прямых соединениях сигнал-слот, его нельзя использовать для очереди соединений сигнал-слот, которые создаются между объектами в разных потоках. Это происходит потому, что мета-объектная система не знает, как обрабатывать во время выполнения создание и уничтожение объектов пользовательского типа.
Чтобы разрешить создание объектов во время выполнения, вызовите шаблонную функцию qRegisterMetaType() чтобы зарегистрировать его с помощью мета-объектной системы. Также это делает тип доступным для поставки в очередь соединения сигнал-слот прежде, чем вы сделает первое соединение, использующее этот тип.
Пример "Queued Custom Type" объявляет класс Block, который зарегистрирован в файле main.cpp:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
...
qRegisterMetaType<Block>();
...
return app.exec();
}
Позднее этот тип используется в соединении сигнал-слот в файле window.cpp:
Window::Window()
{
thread = new RenderThread();
...
connect(thread, SIGNAL(sendBlock(Block)), this, SLOT(addBlock(Block)));
...
setWindowTitle(tr("Queued Custom Type"));
}
Если тип используется в очереди подключений без регистрации, то в консоли будет выведено предупреждение; например:
QObject::connect: Cannot queue arguments of type 'Block'
(Убедитесь, что 'Block' зарегистрирован с помощью qRegisterMetaType().)
Создание типа с возможностью печати
Часто бывает очень полезно создать пользовательский тип, предназначенный для печати в целях отладки, как в следующем коде:
Message message(body, headers);
qDebug() << "Original:" << message;
Это достигается созданием для типа потокового оператора, который часто определяется в заголовочном файле типа:
QDebug operator<<(QDebug dbg, const Message &message);
Реализация типа Message в примере "Custom Type" потребовала некоторых усилий чтобы сделать представление, предназначенное для печати, как можно более читабельным:
QDebug operator<<(QDebug dbg, const Message &message)
{
QStringList pieces = message.body().split("\r\n", QString::SkipEmptyParts);
if (pieces.isEmpty())
dbg.nospace() << "Message()";
else if (pieces.size() == 1)
dbg.nospace() << "Message(" << pieces.first() << ")";
else
dbg.nospace() << "Message(" << pieces.first() << " ...)";
return dbg.maybeSpace();
}
Конечно, вывод, который может отправляться в поток отладки можно сделать столь простым или сложным как вам захочется. Обратите внимание на то, что возвращаемое этой функцией значение - это сам объект QDebug, однако это часто получают вызывая функцию-члена maybeSpace() класса QDebug, которая дополняет поток пробелами чтобы сделать его более читаемым.
Дополнительные материалы
Документация к макросу Q_DECLARE_METATYPE() и функции qRegisterMetaType() содержит более детальную информацию об их использовании и ограничениях.
Примеры "Custom Type", "Custom Type Sending" и "Queued Custom Type" показывают как реализовать пользовательский тип с возможностями описанных в общих чертах в этом документе.
Документ Техники отладки предоставляет обзор механизмов отладки, обсуждаемые выше.
Авторские права © 2010 Nokia Corporation и/или её дочерние компании |
Торговые марки |
Qt 4.6.4 |
|