Как отобразить собственное окно в приложении QML? - PullRequest
0 голосов
/ 24 мая 2018

Проблема

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

Ограничения

  • Приложение в собственном окне (NW) отрисовывается с помощью OpenGL
  • NW предназначено только для отображения и не требует ввода данных пользователем
  • No 3 rd разрешены сторонние библиотеки
  • У вас есть доступ к исходному коду 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());

Это решение работает для каждого окна, которое я передаю, за исключением того, которое я хочу.У меня осталось пустое белое изображение вместо желаемого.

Extra

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

Среда

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

1 Ответ

0 голосов
/ 01 сентября 2018

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

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

win32:LIBS += -ldwmapi.dll

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...