Qt窗口中嵌入UE程序在windows开发时的问题最新解决方案(附代码)
·

本文解决了将UE程序嵌入Qt窗口时遇到的两个关键问题:输入事件无法传递和鼠标位置偏移。通过Windows API修改UE窗口样式并设置父窗口实现嵌入,同时重载Qt窗口的resizeEvent实现同步缩放。针对鼠标偏移问题,在UE中通过循环获取所有父窗口坐标并累加,最终设置正确的屏幕位置。解决方案提供了完整的C++代码实现,并指出需注意可执行程序路径匹配、窗口标题设置等细节问题。该方法有效实现了UE程序与Qt界面的无缝集成。
项目场景
需要将UE程序窗口作为Qt软件的一部分,将UE窗口去除系统标题栏和边框等作为一个子窗口嵌入到Qt布局中
问题描述
问题1:UE嵌入Qt窗口时,会出现鼠标和键盘无法输入到UE
问题2:UE获取的鼠标与界面UI的位置产生偏移
原因分析
问题1分析:QWindow::fromWinId得到的QWindow是对UE窗口的代理,但是事件会被阻塞传递不到UE窗口内,同时UE窗口也就无法被激活
问题2分析:UE引擎源码中在Windows系统对UE屏幕位置是通过GetWindowRect获得,默认认为UE窗口的父窗口是桌面窗口,未考虑作为子窗口的情况
解决方案
Qt正确嵌入UE程序步骤:
QProgress启动ue的exe程序- 通过Windows的
FindWindowW函数以窗口标题查找窗口句柄 GetWindowLong(handle, GWL_STYLE);函数获取样式,修改后重新设置样式SetParent设置外部窗口的父窗口widgetSetWindowPos设置窗口大小和进行重绘- 父窗口
widget重载resizeEvent,通过MoveWindow跟随大小变化
void MyWidget::start(const QString &exePath, const QString &strTiltle)
{
if(!exePath.isEmpty())
{
// 启动外部程序
_process = new QProcess(this);
_process->start(exePath);
// 检查应用是否成功启动
if(_process->waitForStarted())
{
// 获取外部程序的窗口句柄 可以增加获取最大时间或次数超过退出的代码
while(!_exHWhd)
{
_exHWhd = FindWindowW(NULL, reinterpret_cast<LPCWSTR>(strTitle.utf16()));
// 短暂休眠,避免CPU占用过高
QThread::msleep(50);
}
}
}
if (_exHWhd)
{
// 修改窗口样式以去掉标题栏和边框 采用此方法才可正常获取激活响应键盘
LONG style = GetWindowLong(_exHWhd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_OVERLAPPED); // 移除标题栏、可调整大小的边框和重叠窗口样式
SetWindowLong(_exHWhd, GWL_STYLE, style | WS_POPUP); // 设置为无边框的子窗口
SetParent(_exHWhd, (HWND)winId());
SetWindowPos(_exHWhd, HWND_TOPMOST, 0, 0, geometry().width(),
geometry().height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOCOPYBITS);
}
else
{
QMessageBox::information(this, "Tip", QString("未成功打开窗口:%1").arg(strTitle), QMessageBox::Yes);
qDebug() << "Failed to find external application window.";
}
}
// 重载resizeEvent
void MyWidget::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
if(_exHWhd)
{
MoveWindow(_exHWhd,0, 0, event->size().width(),event->size().height(),false);
}
}
UE正确更新屏幕坐标方案
- UE需要通过获取窗口句柄后循环
GetParent直到nullptr或者桌面窗口句柄,累加left和top值 - UE通过
SetCachedScreenPosition设置屏幕位置 - UE将更新屏幕位置代码封装成函数以定时器设置时间间隔调用更新
// UE修复嵌入窗口坐标偏移问题
// 获取主窗口
TSharedPtr<SWindow> MainWindow = FSlateApplication::Get().GetActiveTopLevelWindow();
if (MainWindow.IsValid()) {
// 获取平台窗口
TSharedPtr<FGenericWindow> GenericWindow = MainWindow->GetNativeWindow();
if (GenericWindow.IsValid()) {
// 转换为 Windows 平台特定的窗口类型
FWindowsWindow* WindowsWindow = static_cast<FWindowsWindow*>(GenericWindow.Get());
// 获取 HWND 句柄
HWND WindowHandle = WindowsWindow->GetHWnd();
// 最终屏幕坐标
FVector2D FinalPosition(0, 0);
// 获取桌面窗口句柄(作为终止条件)
HWND DesktopHandle = GetDesktopWindow();
// 获取窗口位置矩形
RECT WindowRect;
// 循环所有父窗口
while(WindowHandle != nullptr && DesktopHandle!= DesktopHandle)
{
if (GetWindowRect(WindowHandle, &WindowRect))
{
// 窗口左上角坐标(屏幕坐标)
FinalPosition.X += static_cast<float>(WindowRect.left);
FinalPosition.Y += static_cast<float>(WindowRect.top);
}
WindowHandle = GetParent(WindowHandle);
}
// 获取DPI缩放因子
float DPIScale = FSlateApplication::Get().GetDPIScaleFactorAtPoint(FinalPosition.X, FinalPosition.Y);
// 转换为 Slate 坐标
FinalPosition.X /= DPIScale;
FinalPosition.Y /= DPIScale;
// 设置屏幕位置
SetCachedScreenPosition(FinalPosition);
}
}
注意事项
- 需要准确匹配可执行程序
文件位置和窗口标题 - 获取的ue程序窗口标题尽量设置成不含特殊字符如空格等,简短不动态变化
- ue游戏模式依赖鼠标移动完成,则窗口化时进入游戏模式需限制鼠标隐藏,需通过某个键位响应后显示鼠标退出
更多推荐
所有评论(0)