АК
Александр КузьминыхҚар. 28, 2017, 7:59 Т.Ж.

Теңшелетін Qt 3D аспектісін жазу - 1 бөлім

Введение

Qt 3D имеет гибкую и расширяемую архитектуру, которая позволяет нам легко добавлять в нее свои новые функциональные возможности, не нарушая при этом существующие свойства. Функциональность Qt 3D разделена на так называемые аспекты, каждый из которых инкапсулирует конкретную предметную область, такую как рендеринг (Render Aspect), ввод (Input Aspect) или анимация (Animation Aspect).

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


Обзор

Приложение, которое мы соберем, выглядит так. Это - очень простая Qt 3D-сцена, и окно, показывающее текущую среднюю частоту кадров.

Существует несколько частей, которые нам нужно учитывать при добавлении нового функционала:

  • Аспект - отвечает за организацию любых заданий, требующих выполнения каждого фрейма, и за управление хранением любых внешних объектов.
  • Компоненты - предоставляются вашей библиотекой или приложением, которые в совокупности придают конечному объекту новое поведение. Компоненты - это основной API, который вам нужно создавать.
  • Узлы (Nodes на рисунке) - как и для компонентов, за исключением того, что подклассы QNode обычно предоставляют вспомогательные данные, требуемые компонентом. Например QAnimationClipLoader является узлом, который предоставляет ключевые данные анимации кадров компоненту QClipAnimator.
  • Бэкэнд-узлы (Backend Nodes на рисунке) - Бэкэнд-копии для любых внешних компонентов или узлов, предоставляемых вашей библиотекой / приложением. Это объекты, которые обычно обрабатываются заданиями, запущенными в пуле потоков, как это продиктовано самим аспектом.
  • Mapper - Пользовательские карты, которые регистрируются с учетом этого аспекта и отвечают за создание, выборку и уничтожение внутренних узлов по требованию. Mapper используется QAbstractAspect и QAspectEngine для синхронизации времени жизни внешних и внутренних объектов.
  • Задания - создаются и планируются аспектом, обрабатывают базовые узлы. Задания могут также отправлять события во внешние узлы и компоненты, если свойства изменяются.
  • Арбитр изменений - отвечает за доставку событий между фронтэнд и бэкэнд объектами. С ним ничего не нужно делать, но имейте в виду его существование.

Добавление аспекта

Написание исходного аспекта действительно тривиально. Просто унаследуйте QAbstractAspect и зарегистрируйте его с помощью QAspectEngine:

customaspect.h

class CustomAspect : public Qt3DCore::QAbstractAspect
{
    Q_OBJECT
public:
    explicit CustomAspect(QObject *parent = nullptr)
        : Qt3DCore::QAbstractAspect(parent)
    {}

protected:
    QVector<Qt3DCore::QAspectJobPtr> jobsToExecute(qint64 time) override
    {
        qDebug() << Q_FUNC_INFO << "Frame time =" << time;
        return {};
    }
};

main.cpp

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);
    Qt3DExtras::Quick::Qt3DQuickWindow view;

    view.engine()->aspectEngine()->registerAspect(new CustomAspect);

    view.setSource(QUrl("qrc:/main.qml"));
    view.show();
    return app.exec();
}

В QAbstractAspect есть виртуальные методы, которые вы можете переопределить для инициализации и удаления, если они вам понадобятся. Однако для этого простого примера все, что нам нужно сделать, это реализовать виртуальный метод jobsToExecute(). Позже мы используем его, чтобы запланировать выполнение задания в пуле потоков на каждый кадр, но сейчас мы просто выводим отладочный текст, чтобы вернуть пустой вектор (нет заданий готовых к выполнению). Обратите внимание, что эти виртуальные функции будут вызываться только после регистрации аспекта с помощью QAspectEngine (см. Выше), чтобы он был частью моделирования. Мы вернемся и завершим этот аспект чуть позже.

Компонент FpsMonitor

Мы хотим добавить функционал, который бы выводил информацию о средней частоте кадров, усредненной по заданному числу кадров. Мы можем написать API-интерфейс примерно так:

fpsmonitor.h

class FpsMonitor : public Qt3DCore::QComponent
{
    Q_OBJECT
    Q_PROPERTY(int rollingMeanFrameCount READ rollingMeanFrameCount WRITE setRollingMeanFrameCount NOTIFY rollingMeanFrameCountChanged)
    Q_PROPERTY(float framesPerSecond READ framesPerSecond NOTIFY framesPerSecondChanged)

public:
    explicit FpsMonitor(Qt3DCore::QNode *parent = nullptr);

    float framesPerSecond() const;
    int rollingMeanFrameCount() const;

public slots:
    void setRollingMeanFrameCount(int rollingMeanFrameCount);

signals:
    void framesPerSecondChanged(float framesPerSecond);
    void rollingMeanFrameCountChanged(int rollingMeanFrameCount);

private:
    float m_framesPerSecond;
    int m_rollingMeanFrameCount;
};

Обратите внимание, что мы используем декларативный API на основе свойств, чтобы этот класс можно было легко использовать как из QML, так и из C ++. Свойство rollingMeanFrameCount является обычным свойством чтения и записи, а реализация функций setter и getter полностью стандартная. Это свойство будет использоваться для управления количеством кадров, по которым мы вычисляем скользящую среднюю частоту кадров. Свойство framesPerSecond - это свойство только для чтения, которое позже будет установлено из задания, которое мы будем писать для обработки компонентов FpsMonitor в пуле потоков Qt 3D .

Создание небольшого тестового приложения

Прежде чем мы сможем использовать наш пользовательский компонент из QML, мы должны зарегистрировать тип с помощью системы типов QML. Это простой однострочный код, который мы можем добавить к нашей основной функции:

main.cpp

qmlRegisterType<FpsMonitor>("CustomModule", 1, 0, "FpsMonitor");
rootContext->setContextProperty("_window", &view);

где мы также воспользовались возможностью экспортировать окно в QML-контекст (это нам понадобится в определенный момент).

Как только это будет сделано, мы можем импортировать модуль CustomModule QML и использовать компонент FpsMonitor так же, как любой из встроенных типов.

main.qml

Entity {
    components: [
        FpsMonitor {
            rollingMeanFrameCount: 20
            onFramesPerSecondChanged: {
                var fpsString = parseFloat(Math.round(framesPerSecond * 100) / 100).toFixed(2);
                _window.title = "CustomAspect: FPS = " + fpsString
                        + " (" + rollingMeanFrameCount + " frame average)"
            }
        }
    ]
}

Конечно, на данный момент FpsMonitor не делает ничего, кроме установки значения свойства rollingMeanFrameCount. Как только мы закончим бэкэнд, приведенный выше код обновит заголовок окна, чтобы показать текущую среднюю частоту кадров и количество кадров, используемых для ее расчета.

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

Статья написана: Sean Harmer | Суббота, Ноябрь 25, 2017г.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз