Создание пользовательских типов 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' (Make sure 'Block' is registered using 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" показывают как реализовать пользовательский тип с возможностями описанных в общих чертах в этом документе. Документ Техники отладки предоставляет обзор механизмов отладки, обсуждаемые выше. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |