Как отобразить собственное окно в приложении QML?

Проблема

Мне нужно собственное окно Windows, отображаемое в приложении QML.

Ограничения

  • Приложение с собственным окном (NW) отрисовывается с помощью OpenGL
  • NW предназначен только для отображения и не требует ввода данных пользователем.
  • Партийные библиотеки 3rd не разрешены.
  • У вас есть доступ к исходному коду NW
    • Примечание: перенос этого конкретного исходного кода на QOpenGLWidget не одобряется

Рабочее решение

Рабочее решение - использовать методы QWindow :: fromWinId и QWidget :: createWindowContainer, чтобы «бросить» окно в QWidget. Затем мы берем элемент из QML и заставляем этот виджет «отражать» размеры и положение элемента.

Следующие ниже фрагменты кода демонстрируют возможную реализацию. (Примечание: эти фрагменты перефразированы и лишены проверки на ошибки)

main.qml

Прямоугольник с идентификатором target передается в свойство targetObject MyInterfaceObject.

Rectangle {
    id: app
    .
    .
    Rectangle {
        id: target

        MyInterfaceObject {
            targetObject: target
            .
            .
        }
}

MyInterfaceObject.cpp

MyInterfaceObject - это QObject, у которого есть свойство QVariant targetObject. Он берет нужный дескриптор окна и «бросает» его на виджет. Затем он запускает таймер, который сопоставляет встроенный виджет с позицией targetObject.

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

// Find the window by it's title
HWND windowHandle = ::FindWindow(0, L"Your Window Title");

// "Throw" window into a widget
QWindow *embeddedWidgetWindow = QWindow::fromWinId((WId)windowHandle);
// the quickWidget is created in main.cpp
QWidget *embeddedWidget = QWidget::createWindowContainer(embeddedWidgetWindow, quickWidget);

embeddedWidget->show();
embeddedWidgetWindow->show();
quickWidget->show();

// Start a timer that will enforce the "mirroring" effect
QTimer* timer = new QTimer;
timer->setInterval(1);
timer->start();

timer->connect(timer, &QTimer::timeout, [this](){
    // targetObject is the QVariant form of the target QML object from our main.qml
    auto quickItem = this->targetObject().value<QQuickItem*>();

    // Map our widgets position/dimensions to our target qml object
    auto scenePos = quickItem->mapToItem(0, quickItem->position());
    auto myX = scenePos.x() - quickItem->position().x();        
    auto myY = scenePos.x() - quickItem->position().y();

    embeddedWidget->setGeometry(myX, myY, quickItem->width(), quickItem->height());
});

main.cpp

// The widget that will parent our native window widget
QQuickWidget* quickWidget= new QQuickWidget;
quickWidget->setSource(QUrl(QStringLiteral("qrc:/main.qml")));
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);

// Give our InterfaceObject access to this quickWidget
InterfaceObject::setQuickWidget(quickWidget);

Встроенный виджет будет успешно следовать за моим объектом QML. Однако при установке на Flickable виджет будет временно немного отставать при прокрутке или перетаскивании.

Нерабочее решение

Мое второе решение аналогично предыдущему и включает в себя метод QScreen :: grabWindow и QuickQuickPaintedItem.

MyInterfacePaintedItem.cpp

// Do this for every paint event

// Find the window by it's title
HWND windowHandle = ::FindWindow(0, L"Your Window Title");

auto quickItem = this->targetObject().value<QQuickItem*>();

// can also "throw" the native window into a widget like above and
// use that widgets winId (widget->winId())

auto pixmap = QGuiApplication::primaryScreen()->grabWindow((WId)windowHandle)

painter->drawPixmap(QRect(0, 0, quickItem->width(), quickItem->height()), 
                          pixmap,
                          pixmap.rect());

Это решение работает для каждого окна, которое я прохожу, ЗА ИСКЛЮЧЕНИЕМ того, которое я хочу. У меня осталось пустое белое изображение вместо желаемого.

Дополнительный

Я попытался использовать BitBlt в сочетании с QtWin :: fromHBITMAP (Применение), но это тоже не работает в моем конкретном окне. Я всегда получаю белое изображение, подобное Нерабочему решению выше. (Возможная причина).

Среда

  • Qt: 5.10.0
  • Платформа: Windows 7
  • Компилятор: MSVC2015 32bit

родной OpenGL не прорисовывает поверхности, которые вы можете захватить с помощью Qt, вот почему вы получаете прямоугольник цвета фона. даже с QOpenGLWidget, который не работает, если вы не имплантируете его в QGraphicsScene. Вы должны работать с закадровым рендерингом и рендерить изображение. Обратите внимание, что QML изначально использует OpenGL в Windows для рендеринга контента в конфигурации по умолчанию.

Swift - Friday Pie 24.05.2018 20:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
1 215
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используя DwmRegisterThumbnail, вы можете отображать собственное окно OpenGL в любом месте вашего приложения QML. Совершенно непонятно, как это сделать:

Добавление DWM dll в QMake

win32:LIBS += -ldwmapi.dll

Регистрация миниатюры собственного приложения в приложении QML

DWM API позволяет зарегистрировать эскиз приложения в другом приложении, передав дескрипторы окон (HWND, а не WId) исходного (владельца эскиза) и окон назначения.

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

DwmRegisterThumbnail((HWND)window()->winId(), hNativeHandle, hThumbnail);

Обновление размера и положения эскиза

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

Другие вопросы по теме