Qt如何将外部窗口嵌入部件中

一、简述

今天给大家讲解的是使用QWindow类通过窗口句柄将外部的应用程序嵌入到我们的部件中来显示。在讲解之前可以延伸一下,当时项目中使用QProcess启动一些本地软件或者执行脚本时,需要将启动的第三方窗口嵌入到我们自己写的窗口中,此时我们通过QProcess类的start方法执行相应的启动命令,并获取到启动程序的进程id(pid),然后通过pid获取到窗口对应的句柄,最后通过QWindow类创建容器将外部应用程序嵌入到我们的界面中来。

所以讲解之前,会先讲解下如何通过WinApi根据进程id(pid)获取到窗口的句柄,然后再通过窗口句柄使用QWindow来嵌入到我们的界面中来。

二、代码之路

我们先使用WinApi通过进程id(pid)获取到窗口句柄,具体接口以及示例代码如下,大家可直接拷贝即可:

cpp 复制代码
struct FindWindowData
{
    DWORD processId;
    HWND hWnd;
};

BOOL FindWindowCB(HWND hWnd, LPARAM lParam)
{
    DWORD processId = 0;
    if (GetWindowThreadProcessId(hWnd, &processId)) {
        // 分配足够大的缓冲区来存储窗口标题
        const int MAX_TITLE_LENGTH = 255;
        WCHAR windowTitle[MAX_TITLE_LENGTH];

        // 调用 GetWindowTextW 来获取窗口标题
        int result = GetWindowTextW(hWnd, windowTitle, MAX_TITLE_LENGTH);
        // qDebug() << "the text:" << QString::fromWCharArray(windowTitle) << result << hWnd << processId;

        QString title = QString::fromWCharArray(windowTitle);

        FindWindowData* dataPtr = (FindWindowData*)lParam;
        // 这里的筛选条件可能需要继续优化
        if (processId == dataPtr->processId && IsWindowVisible(hWnd) && title.length() > 0) {
            qDebug() << QString("FindWindow  title:%1, hWnd:%2, pid:%3")
                .arg(title).arg((WId)hWnd).arg(processId);
            dataPtr->hWnd = hWnd;
        }

        return TRUE;
    }

    return FALSE;
}

// 寻找特定PID的窗口句柄
HWND findWindowByPID(DWORD dwProcessId)
{
    FindWindowData winData = { dwProcessId, 0 };
    LPARAM p = (LPARAM)&winData;
    EnumWindows((WNDENUMPROC)FindWindowCB, (LPARAM)p); // 遍历系统上打开的窗口

    return winData.hWnd;
}

HWND getWinIdByPid(int pid)
{
    return findWindowByPID(pid);
}

// 测试示例
void testFunc()
{
	 // 创建进程
    QProcess* process = new QProcess(this);
    connect(process, QOverload<int>::of(&QProcess::finished), this, [=]() {
        process->close();
        process->deleteLater();
    });

    QString command = "xxx.exe";
    process->start(command);
	// 这里等待500毫秒是执行完命令等待窗口显示出来,具体时间得看程序启动的快慢;
    QThread::msleep(500);

	// 获取到pid之后通过接口转为窗口句柄;
    int pid = process->processId();
	HWND hWnd = getWinIdByPid(pid);
}

参考自 https://blog.csdn.net/joyopirate/article/details/140928311


下方代码将获取到的窗口具体通过QWindow::fromWinId 方法将窗口句柄转为QWindow对象,通过QWidget::createWindowContainer方法创建一个QWidget,可以将刚刚的窗口对象嵌入到基于QWidget的应用程序中,可以在测试方法中看到具体用法,拿到最终QWidget对象大家就可以嵌入到我们界面的布局中去了。

cpp 复制代码
QWidget* getWindowContainerWgt(int pid)
{
	QWidget* pContainerWgt = nullptr;
    HWND hWnd = getWinIdByPid(pid);
    if (hWnd != nullptr) {
		pContainerWgt = new QWidget;
		// 这里也可以设置具体的大小;
		pContainerWgt->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

        QWindow* pDestWindow = QWindow::fromWinId((WId)hWnd);
        pDestWindow->setFlags(pDestWindow->flags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
        QWidget* pChildWidget = QWidget::createWindowContainer(pDestWindow, pContainerWgt);
        pChildWidget->setObjectName("WindowContainer");

		QHBoxLayout* hLayout = new QHBoxLayout(pContainerWgt);
		hLayout->addWidget(pChildWidget);
		hLayout->setMargin(0);
    }
    else {
        qDebug() << "Can not find winId by pid";
    }

	return pContainerWgt;
}

void testFunc()
{
 	// 创建进程
    QProcess* process = new QProcess(this);
    connect(process, QOverload<int>::of(&QProcess::finished), this, [=]() {
        process->close();
        process->deleteLater();
    });

    QString command = "xxx.exe";
    process->start(command);
	// 这里等待500毫秒是执行完命令等待窗口显示出来,具体时间得看程序启动的快慢;
    QThread::msleep(500);

	// 获取到pid之后通过接口转为窗口句柄;
    int pid = process->processId();
	QWidget* windowWgt = getWindowContainerWgt(pid)
	// 如果获取到的widget不为空,说明已经成功将窗口嵌入到widget中
	if(windowWgt != nullptr){
		// todo
		// ...
	}
}
相关推荐
m0_699659564 小时前
QT知识点复习
开发语言·qt
深蓝海拓6 小时前
基于深度学习的视觉检测小项目(十六) 用户管理界面的组态
人工智能·python·深度学习·qt·pyqt
弄不死的强仔8 小时前
可被electron等调用的Qt截图-录屏工具【源码开放】
前端·javascript·qt·electron·贴图·qt5
行十万里人生1 天前
Qt事件处理:理解处理器、过滤器与事件系统
开发语言·git·qt·华为od·华为·华为云·harmonyos
黑金IT1 天前
Python3 + Qt5:实现AJAX异步更新UI
qt·ui·ajax
人工智能教学实践1 天前
基于 yolov8_pyqt5 自适应界面设计的火灾检测系统 demo:毕业设计参考
qt·yolo·课程设计
扎量丙不要犟1 天前
跨平台的客户端gui到底是选“原生”还是web
前端·javascript·c++·qt·rust·electron·tauri
笑鸿的学习笔记2 天前
qt-Quick3D笔记之官方例程Runtimeloader Example运行笔记
笔记·qt·3d
菜一头包2 天前
线程池以及在QT中的接口使用
c++·qt
R三哥哥啊2 天前
【Qt5】声明之后快速跳转
开发语言·qt·qt5