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


    在上一文中本人尝试将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);
    }
    
  • 相关阅读:
    分布式环境下限流方案的实现redis RateLimiter Guava,Token Bucket, Leaky Bucket
    将bloomfilter(布隆过滤器)集成到scrapy-redis中
    Redis Cluster 分区实现原理
    OEM status|start|stop
    oracle 11g SQL Developer instead of isqlplus
    oracle Instance status: READY–lsnrctl status|start|stop
    oracle tns in linux
    Oracle 多实例如何通过EM进行访问-portlist.ini
    oracle command
    oracle .bash_profile
  • 原文地址:https://www.cnblogs.com/blueroses/p/9151094.html
Copyright © 2020-2023  润新知