在上一文中本人尝试将Ue4嵌入到Qt中,但依然有一些问题没有去尝试解决。今天因为帮助知乎专栏作者@大钊的关系,顺便进行补完。

2018.7.18更新:

正好在参加杭州UnrealCircle的时候见到了EPIC上海的工程师李锋,之后我通过邮件询问了他这个问题,以下是他给我的回复:

问题所在原因是当你把虚幻引擎的窗口作为子窗口挂在Qt后,SWindow::GetPositionInScreen()中返回的坐标是错误的。

当你单独启动虚幻引擎的窗口,这里返回的是当前窗口左上角在屏幕中的位置。而当你把虚幻引擎挂在为Qt子窗口后,SWindow::GetPositionInScreen()中的ScreenPosition的值会由于WM_MOVE更新为0.

我看了你的代码中 SetParent后:

MoveWindow(hwnWindow,rect.x(),rect.y(), rect.width(), rect.height(), true);
::SetWindowPos( hwnWindow, nullptr,rect.x(), rect.y(), rect.width(), rect.height(), SWP_NOZORDER | SWP_FRAMECHANGED| SWP_NOCOPYBITS );

但是这两句执行后,最后系统还是会收到WM_MOVE消息进而把窗口的坐标设置为0. 说明Qt在后面某一时刻把你的窗口重新Move回(0,0). 这个需要你去Qt中查看了.

(其实这里你的MoveWindow和SetWindowPos 在后面都被覆盖掉了,没有生效。原因是当你单独创建两个Windows窗口,不适用Qt框架,你会看到子窗口相对于父窗口左上角的位置就是你MoveWindow中设置的 rect.x(),rect.y()的值,然而在你写的Qt代码运行后,子窗口>这里不管写什么值,最终都是贴着父窗口Client Area左上角的)

总结原因是因为SetParent后,SWindow就无法接受到WM_MOVE消息了。突破思路有这么几条,但限于本人技术的问题暂时不会去尝试:

1.在Qt中手动发送WM_MOVE消息给Ue4窗口

2.在Qt中通过Socket之类的方法更新ScreenPosition的数值

3.修改UE4源代码,将SWindow::GetPositionInScreen()的获取方式进行修改。

2018.6.16更新:

1、更新了一下代码,删除了一些错误的东西

小贴士

Ue4项目设置中的Use Borderless Window

这个选项会让Ue4窗口变成没有外框与标题栏的状态,这样在嵌入Qt的时候就不要设置WindowStyle了,这样看起来效果会更好(设置WindowStyle会让窗口短暂显现出来,这样或许就不需要做静默启动了)

解除Ue4对鼠标的限制

Set Input Game And UI 来解除Ue4对鼠标限制

让Ue4重新获得焦点

BringWindowToTop (HWND);

已知的坑

1、因为启动的exe进程并非游戏进程,所以通过QProcess的状态来判断Ue4是否启动是不对的,推荐使用WINAPI来获取对应线程。

2、可以在项目设置中修改窗口显示标题,可以把讨厌的(32-bit, PCD3D_SM5)去掉,强烈推荐使用窗口句柄查看工具,我是网上下了句柄精灵。(窗口标题后面都是有空格的)

3、使用嵌入方法回导致Ue4客户区(不太确定这个叫什么)坐标异常(固定在左上角),会导致Umg按钮无法点击,最大化窗口后可以点击到一部分。使用原生的win32窗口项目嵌入也是会有这个问题的,如果有对winapi了解人麻烦告知我解决方案。

本人尝试了许多方法,都失败了:

1、在Ue4中使用AdjustWindowRectEx

2、SetWindowPos与MoveWindow

3、在Ue4中手动调用Ue4包装的MoveWindow函数

4、先移动Ue4窗口再嵌入Qt

5、向ue4发送Resize消息

目前的补救方案就是,在Ue4中计算鼠标坐标,再使用虚拟点击来点击按钮。

关于静默启动外部程序与枚举进程与窗体句柄

因为本人对WINAPI不熟所以只找了一些资料

打开一个外部程序,一般是通过ShellExecute/Ex或CreateProcess或WinExec等API。

至于隐藏主窗口以及控制窗口上的控件,这个根据不同的程序会有一定的复杂程度,首先是找到所谓的主窗口,然后再枚举窗口上的子窗口,通过一系列API模拟点击或读取/设置文本内容等。

参考:http://bbs.csdn.net/topics/240049935

https://www.cnblogs.com/zjutlitao/p/3889900.html

代码

这里我直接上代码了,一看就懂。

#include "calculateandmove.h"
#include "ui_calculateandmove.h"
#include <QDebug>
#include <QtConcurrent>
#include <QThread>
#include <QWindow>
#include <QTimer>
CalculateAndMove::CalculateAndMove(QWidget *parent) :
QWidget(parent),
ui(new Ui::CalculateAndMove),process(nullptr)
{
ui->setupUi(this);
//setAttribute(Qt::WA_NativeWindow, true);
} CalculateAndMove::~CalculateAndMove()
{
if(hwnWindow!=0)
SendMessage(hwnWindow,WM_CLOSE,0,0);
if(process){
if(process->state()==QProcess::Running){
process->terminate();
process->waitForFinished(30000);
}
delete process;
}
delete ui;
} void CalculateAndMove::on_insetUe4_clicked()
{
startUe4();
} void CalculateAndMove::on_deleteUe4_clicked()
{
if(hwnWindow==0)
return;
SendMessage(hwnWindow,WM_CLOSE,0,0);
} void CalculateAndMove::startUe4(){
//启动程序
QString unreal4Path{"D:/QtProject/QtWithUnreal4/WindowsNoEditor/DemoGame.exe"};
QStringList arguments;
arguments << "-WINDOWED";
process=new QProcess;
process->start(unreal4Path,arguments); QtConcurrent::run([this]{
while (true) {
//通过窗口名称取得窗口句柄
//hwnWindow=FindWindow(NULL,L"DemoGame ");
hwnWindow=FindWindow(NULL,L"DemoGame (32-bit, PCD3D_SM5) ");
qDebug()<<hwnWindow;
if(hwnWindow!=0)
{
connect(this,SIGNAL(insetUe4Complete()),this,SLOT(insetUe4()));
emit insetUe4Complete();
break;
}
}
});
} void CalculateAndMove::insetUe4(){
////----------------------------------------------
////方法一,进qt后需要调用解除客户区锁定函数(Ue4
// ue4Window=QWindow::fromWinId(WId(hwnWindow));
// ue4Window->setParent( this->windowHandle());
// ue4Window->setGeometry(0,0,ui->label->width(),ui->label->height());
// ue4Window->show(); //----------------------------------------------
//方法二,进qt后需要调用解除客户区锁定函数(Ue4
QRect rect=ui->label->geometry();
QPoint pos=ui->label->mapToGlobal(ui->label->pos());
qDebug()<<"rect:"<<rect;
qDebug()<<"worldPos:"<<pos;
QRect worldRect={rect.x()+pos.x(),rect.y()+pos.y(),rect.width()+pos.x(),rect.height()+pos.y()}; SetParent(hwnWindow,(HWND)QWidget::winId()); //MoveWindow(hwnWindow,rect.x(),rect.y(), rect.width(), rect.height(), true);
::SetWindowPos( hwnWindow, nullptr,rect.x(), rect.y(), rect.width(), rect.height(), SWP_NOZORDER | SWP_FRAMECHANGED| SWP_NOCOPYBITS ); //---------------------------------------------- LPRECT lprect;
GetClientRect(hwnWindow,lprect);
qDebug()<<(int)lprect->left<<(int)lprect->top<<(int)lprect->right<<(int)lprect->bottom;
this->repaint();
} void CalculateAndMove::on_pushButton_clicked()
{
BringWindowToTop (hwnWindow);
SetForegroundWindow(hwnWindow);
}

对《将Unreal4打包后的工程嵌入到Qt或者桌面中》一文的补充的更多相关文章

  1. 对《分享一下自己用c++写的小地图》一文的补充

    在写完上一篇文章后,发现了一个问题: 那就是编写的插件无法实时预览. 在学习了Slate之后,我找到了方法: 重写SynchronizeProperties函数 头文件中添加: #if WITH_ED ...

  2. 分享一下自己用c++写的小地图

    http://www.unrealchina.com/forum.php?mod=viewthread&tid=451&extra=&from=portal&page= ...

  3. 如何优雅的写一篇安利文-以Sugar ORM为例

    前言 我最近喜欢把写的十分优美的技术文章叫做安利文.首先,文章必须是原创而非软广:其次,阅读之后不仅能快速吸纳技术要点并入门开发,还能感同身受的体会作者热情洋溢的赞美和急于分享心得体验的心情,让人感觉 ...

  4. Docz 用 MDX 写 React UI 组件文档

    Docz 用 MDX 写 React UI 组件文档 前言 为了提升开发效率,创建一套 UI 组件库是一种较为有效的方式之一:可以减少重复工作.提高可复用,所以现在越来越多团队开始创建自己的 UI 组 ...

  5. 如何基于WPF写一款数据库文档管理工具(二)

    系列目录 基于WPF重复造轮子,写一款数据库文档管理工具(一) 本篇重点 上次发表了基于WPF重复造轮子,写一款数据库文档管理工具(一) 得到不少人支持,文章一度上到了博客园推荐表首页,看来大家对这个 ...

  6. 分享一个很早之前写的小工具DtSpyPlus

    几年前写的一个获取windows窗体基本信息和屏幕取色的小工具 ,一直在用. 下载地址 http://files.cnblogs.com/dint/SpyPlus.zip

  7. 【原创】大叔问题定位分享(16)spark写数据到hive外部表报错ClassCastException: org.apache.hadoop.hive.hbase.HiveHBaseTableOutputFormat cannot be cast to org.apache.hadoop.hive.ql.io.HiveOutputFormat

    spark 2.1.1 spark在写数据到hive外部表(底层数据在hbase中)时会报错 Caused by: java.lang.ClassCastException: org.apache.h ...

  8. 【转】分享一份C语言写的简历

    个人观点:文章想法很棒,作者的编码风格也很赞,可以从中学到不少东西.转载的文章是我都用心看过的,而且希望后续再可以回过头看的文章,努力让自己的能力越来越强,加油 这里黑客新闻吗?作者用代码更新了自己的 ...

  9. 【原创】大叔问题定位分享(15)spark写parquet数据报错ParquetEncodingException: empty fields are illegal, the field should be ommited completely instead

    spark 2.1.1 spark里执行sql报错 insert overwrite table test_parquet_table select * from dummy 报错如下: org.ap ...

  10. 分享一篇最近新写的jquery注册页面表单校验的程序,仅供参考

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

随机推荐

  1. Forget Guava: 5 Google Libraries Java Developers Should Know

    Forget Guava: 5 Google Libraries Java Developers Should Know Published on 2016 7 13 Somenath PandaFo ...

  2. 飞旋treap

    虽然叫做非旋treap但是飞旋treap很带感所以就用这个名字了(SB) 这个东西是真的好写...... 主要的两个函数只有两个,rotate和splay,split和merge. merge就是大家 ...

  3. velocity 新手用小常识--开源,简单易上手

    项目中经常用到的 .vm 后缀文件是什么? 基于 java 的 velocity 模版引擎的一种页面控制文件,是一些类似 html 语句和一种叫 VLT 的语句构成   velocity --美 [v ...

  4. ArcMap修改粘滞移动容差防止要素在选择时无意拖动移动

    粘滞移动容差将设置一个最小像素数,鼠标指针必须在屏幕上移动了此最小距离时,所选要素才会实际发生移动. 设置粘滞移动容差的结果是延迟移动所选要素,直到指针至少移动了这段距离.此方法可用于在使用“编辑”工 ...

  5. [再寄小读者之数学篇](2014-06-22 发散级数 [中国科学技术大学2012年高等数学B考研试题])

    设 $a_n>0$, $S_n=a_1+a_2+\cdots+a_n$, 级数 $\dps{\vsm{n}a_n}$ 发散, 证明: $\dps{\vsm{n}\cfrac{a_n}{S_n}} ...

  6. spring和mybatis的整合开发(基于MapperFactoryBean的整合开发(方便简单不复杂))

    MapperFactoryBean是mybati-spring团队提供的一个用于根据mapper接口生成mapper对象的类. 在spring配置文件中可以配置以下参数: 1.mapperInterf ...

  7. wget无法正确下载jdk解决方案

    1 去官网复制下载链接 https://download.oracle.com/otn-pub/java/jdk/8u201-b09/42970487e3af4f5aa5bca3f542482c60/ ...

  8. (二)Java工程化--Maven实践

    Maven项目版本号 默认版本号: 1.0-SNAPSHOT 最佳实践是约定该版本为不稳定版本,如果发布一定要删除; 建议的版本规则: 主版本号.次版本号.增量版本号- 如:1.0.0-RELEASE ...

  9. pom文件报错org.apache.maven.archiver.mavenarchiver.getmanifest

    eclipse导入新的maven项目时,pom.xml第一行报错: org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.mav ...

  10. ActiveMQ之topic主题模式

    开发环境我们使用的是ActiveMQ 5.11.1 Release的Windows版,官网最新版是ActiveMQ 5.12.0 Release,大家可以自行下载,下载地址.需要注意的是,开发时候,要 ...