Spec-Zone.ru › Qt 6.1

Создание пользовательских типов Qt

Обзор

При создании пользовательских интерфейсов с Qt, особенно тех, которые содержат специализированные элементы управления и функции, разработчикам иногда требуется создавать новые типы данных, которые могут использоваться наряду с или вместо существующего набора типов значений Qt.

Стандартные типы, такие как QSize, QColor и QString, могут храниться в объектах QVariant, использоваться в качестве типов свойств в классах, основанных на QObject, и передаваться в коммуникации "сигнал-слот".

В этом документе мы рассмотрим пользовательский тип и опишем, как интегрировать его в модель объектов Qt, чтобы он мог храниться так же, как стандартные типы Qt. Затем мы покажем, как зарегистрировать пользовательский тип, чтобы он мог использоваться в подключениях "сигнал-слот".

Создание пользовательского типа

Прежде чем начать, необходимо убедиться, что создаваемый пользовательский тип соответствует всем требованиям, налагаемым QMetaType. Другими словами, он должен предоставлять:

  • публичный конструктор по умолчанию,
  • публичный конструктор копирования и
  • публичный деструктор.

Следующее определение класса Message включает эти члены:

class Message
{
public:
    Message() = default;
    ~Message() = default;
    Message(const Message &) = default;
    Message &operator=(const Message &) = default;

    Message(const QString &body, const QStringList &headers);

    QStringView 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 и извлекать их позже. См. пример Пример пользовательского типа для кода, который демонстрирует это.

Макрос Q_DECLARE_METATYPE() также позволяет использовать эти значения в качестве аргументов сигналов, но только в прямых подключениях "сигнал-слот". Чтобы сделать пользовательский тип общедоступным для механизма сигналов и слотов, нам нужно выполнить дополнительные действия.

Создание и уничтожение пользовательских объектов

Хотя объявление в предыдущем разделе делает тип доступным для использования в прямых подключениях "сигнал-слот", он не может использоваться для подключений "сигнал-слот" в очереди, таких как те, которые создаются между объектами в разных потоках. Это происходит потому, что метаобъектная система не знает, как обрабатывать создание и уничтожение объектов пользовательского типа во время выполнения.

Чтобы включить создание объектов во время выполнения, вызовите шаблонную функцию qRegisterMetaType(), чтобы зарегистрировать его в метаобъектной системе. Это также делает тип доступным для связи "сигнал-слот" в очереди, при условии, что вы вызываете её до создания первого подключения, использующего этот тип.

Пример Пример пользовательского типа в очереди объявляет класс Block, который регистрируется в файле main.cpp:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    ...
    qRegisterMetaType<Block>();
    ...
    return app.exec();
}

Этот тип используется позднее в подключении "сигнал-слот" в файле window.cpp:

Window::Window(QWidget *parent)
    : QWidget(parent), thread(new RenderThread(this))
{
    ...
    connect(thread, &RenderThread::sendBlock,
            this, &Window::addBlock);
    ...
    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 в примере Пример пользовательского типа прилагает усилия, чтобы сделать представление в удобочитаемом формате:

QDebug operator<<(QDebug dbg, const Message &message)
{
    QList<QStringView> pieces = message.body().split(u"\r\n", Qt::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() содержат более подробную информацию об их использовании и ограничениях.

Примеры Пример пользовательского типа и Пример пользовательского типа в очереди демонстрируют, как реализовать пользовательский тип с функциями, описанными в этом документе.

Документ Методы отладки предоставляет обзор механизмов отладки, описанных выше.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/custom-types.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API