Мне нужно собственное окно Windows, отображаемое в приложении QML.
Рабочее решение - использовать методы 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 (Применение), но это тоже не работает в моем конкретном окне. Я всегда получаю белое изображение, подобное Нерабочему решению выше. (Возможная причина).





Используя 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, который помещаю в свое приложение, и на его основе строю свои свойства эскиза.
родной OpenGL не прорисовывает поверхности, которые вы можете захватить с помощью Qt, вот почему вы получаете прямоугольник цвета фона. даже с QOpenGLWidget, который не работает, если вы не имплантируете его в QGraphicsScene. Вы должны работать с закадровым рендерингом и рендерить изображение. Обратите внимание, что QML изначально использует OpenGL в Windows для рендеринга контента в конфигурации по умолчанию.