将qt的子程序窗口嵌入qt的主进程主窗口中(第二种方式)

上一篇文章,存在失去焦点问题,分析可能原因是直接将子进程的窗口句柄封装到QWindow对象中之后,窗口的键盘事件可能就qt自己处理,但是目前没有搞清楚怎么处理的.

下面这种方式也类似第一种方式,不同点在于处理窗口句柄的方式.

通过自定义一个QWindow的一个子类,在子类中通过window平台api将子进程窗口的句柄与子类对象形成父子关系.然后在将子类封装为QWidget供qt主进程的其他控件使用.这种方式的优点是不存在失去焦点问题.但是要处理win7下的其他问题.

笔者在win7下出现的问题:

1.当子进程非全屏模式时,当通过QProcess启动这个子进程窗口时,出现卡住的问题,并且不会显示该窗口. 只有将子进程的设置为 showFullScreen()后,就能正常启动了.分析可能原因时,window平台api设置窗口为无边框及工具窗口对于qt窗口无效,只能通过子进程内部设置好.

2.当主进程窗口是showFullScreen()状态时,子进程存在QDailog窗口,或者一些其他控件的操作时,会出现window桌面任务栏弹出的现象.win10下正常. 笔者通过判断当前操作系统版本,然后通过将任务栏的自动隐藏功能开启后,可通软件层面技术性解决该问题.将任务栏自动隐藏功能开启后,及时存在弹出现象,也因为隐藏后,不会显示. 当软件退出时,再取消该功能.

代码1: 自定义一个QWindow子类

#include <windows.h>
#include <QWindow>


//创建子进程用的QWindow子类
class ChildWindowWrapper : public QWindow {
public:
    /// <summary>
    /// 构造一个Qwindow子类对象
    /// </summary>
    /// <param name="hwnd">子进程创建句柄</param>
    /// <param name="parent">Qwindow对象的父对象</param>
    ChildWindowWrapper(HWND hwnd, QWindow* parent = nullptr)
        : QWindow(parent), m_hwnd(hwnd) {
        embedChildWindow();
    }
    ~ChildWindowWrapper()
    {
        if (m_hwnd) {
            SetParent(m_hwnd, NULL);
        }
    }

    void embedChildWindow() {
        // 修改子窗口样式,去掉标题栏和边框  win7下,qt窗口好像无效了,所以通过子进程内部将窗口设置为全屏
        LONG style = GetWindowLong(m_hwnd, GWL_STYLE);
        style &= ~(WS_CAPTION | WS_THICKFRAME);
        style|= WS_EX_TOOLWINDOW;
        SetWindowLong(m_hwnd, GWL_STYLE, style);
        //关键点,通过window api 将当前QWindow对象设置为子进程窗口的父对象窗口
        SetParent(m_hwnd, (HWND)winId());
        //这个也很重要,将子进程窗口设置为无边框及标题栏的窗口 ,在win7下才不会弹出底下的状态栏.,win10没有这个也正常
        //SWP_FRAMECHANGED刷新一次上述更改的状态.SWP_NOZORDER保存窗口为层级不变
        SetWindowPos(m_hwnd, nullptr, 0, 0, width(), height(),  SWP_NOACTIVATE | SWP_FRAMECHANGED);
        // 显示子进程窗口
        //ShowWindow(m_hwnd, SW_SHOW);
        //SetForegroundWindow(m_hwnd);
        //SetFocus(m_hwnd);
    }

protected:
    bool event(QEvent* event) override {
        if (event->type() == QEvent::Resize) {
            if (m_hwnd) {
                //SetWindowPos(m_hwnd, nullptr, 0, 0, width(), height(), SWP_NOZORDER | SWP_NOACTIVATE);
                SetWindowPos(m_hwnd, nullptr, 0, 0, width(), height(), SWP_NOACTIVATE);
            }
        }
        return QWindow::event(event);
    }

private:
    //子进程窗口的句柄
    HWND m_hwnd;
};
 //头文件中定义子类对象指针
ChildWindowWrapper* m_ChildVMWin{ nullptr };

 具体代码实现

//window 底层操作,隐藏window桌面的任务栏功能,在win7下

/// <summary>
/// 获取window版本
/// </summary>
/// <returns>0为win7,1为win10,其他为-1</returns>
int CncOpWindows::GetWindowsVerson()
{
	int iType{ 0 };
    QString osName = QSysInfo::productType();
    //qDebug() << "Operating System:" << osName;

    if (osName == "windows")
	{ 
		if (QSysInfo::windowsVersion() == QSysInfo::WV_WINDOWS7)
		{
			//qDebug() << "Windows 7";
			iType = 0;
		}
		else if (QSysInfo::windowsVersion() == QSysInfo::WV_WINDOWS10) 
		{
			//qDebug() << "Windows 10";
			iType = 1;
		}
		else
		{
			//其他windows系统
			iType = 2;
		}
    }
    else {
        //qDebug() << "Other OS";
		iType = -1;
    }
	return iType;
}


void CncOpWindows::FindWindowsTaskBarHandle()
{
//windows任务栏句柄
     hwndWindowsTaskBar = FindWindow("Shell_TrayWnd", NULL);
}


void CncOpWindows::HideWindowsTaskBar()
{
	if(hwndWindowsTaskBar)
		SetAutoHideWindowsTaskbar(true);

}
void CncOpWindows::ShowWindowsTaskBar()
{
	if (hwndWindowsTaskBar)
		SetAutoHideWindowsTaskbar(false);
}

void CncOpWindows::SetAutoHideWindowsTaskbar(bool enable) 
{
//开启/关闭任务栏自动隐藏功能
    APPBARDATA appBarData = { 0 };
    appBarData.cbSize = sizeof(APPBARDATA);
    appBarData.hWnd = hwndWindowsTaskBar;
    if (enable) {
        appBarData.lParam = ABS_AUTOHIDE ;
    }
    else {
        appBarData.lParam = 0;
    }
    SHAppBarMessage(ABM_SETSTATE, &appBarData);
}


void CncOpWindows::CreateShareMem()
{
//创建共享内存,与子进程通信用, 通过变量控制子进程的退出与显示
	m_pShareMemObj = new qcShareMemory(nullptr);
	m_pThreadShareMem = new QThread(this);
	m_pThreadShareMem->setObjectName(tr("ShareMenThread"));
	m_pShareMemObj->moveToThread(m_pThreadShareMem);
	connect(m_pThreadShareMem, &QThread::started, m_pShareMemObj, &qcShareMemory::slotInit);
	connect(m_pThreadShareMem, &QThread::finished, m_pShareMemObj, &qcShareMemory::slotFinish);
	connect(this, &CncOpWindows::signalReadWriteShareMem, m_pShareMemObj, &qcShareMemory::slotReadWriteShareMem);
	connect(m_pShareMemObj, &qcShareMemory::signalShowLastWnd, this, &CncOpWindows::slotShowLastWnd);

	m_pThreadShareMem->start();
	m_pShareMemObj->m_bStart = true;
	emit signalReadWriteShareMem();
}

bool CncOpWindows::LoadVisionMeasureApp()
{
    QString cmd= QCoreApplication::applicationDirPath();
	int iCamAppType = 0;
    switch (iCamAppType)
    {
    case 0:
        cmd += "/ImageGrab.exe";//子程序执行文件地址
        break;
    case 5:
        cmd += "/MeasureVM.exe";//子程序执行文件地址
        break;
    }
    QStringList argList;
	argList << QString::number(ui->stackedWidget_sub->winId());//把父窗口的id给子进程传递过去

	m_pProcessVM = new QProcess(this);//使用进程运行子进程窗口
    connect(m_pProcessVM, &QProcess::readyReadStandardError, this, &CncOpWindows::slotCreateWaitingVM);//等待子进程窗口把自身的winId传递过来
    connect(m_pProcessVM, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), 
		this, qOverload<int , QProcess::ExitStatus>(&CncOpWindows::slotFinishedProcessVM));
    //connect(m_pProcessVM, &QProcess::readyRead, this, &CncOpWindows::slotReadProcessVM);
	
	m_pProcessVM->start(cmd, argList);

	return m_pProcessVM->waitForStarted(2000);
}

void CncOpWindows::slotCreateWaitingVM()
{
    quint64 winId = m_pProcessVM->readAllStandardError().toLongLong();
	m_ChildVMWin = new ChildWindowWrapper((HWND)winId, ui->stackedWidget_sub->windowHandle());
	//m_ChildVMWin = QWindow::fromWinId(winId);
    if (m_ChildVMWin)
    {
		//m_ChildVMWin->setParent(ui->stackedWidget_sub->windowHandle());
        m_WidgetVMProcess = QWidget::createWindowContainer(m_ChildVMWin);//获取一个子进程窗口的widget
        // 调整窗口大小以适应控件大小
        ui->stackedWidget_sub->addWidget(m_WidgetVMProcess);

        //这里是可以使用布局器管理子进程窗口的,不管理的话就在坐标0,0处
    }
   
}

void CncOpWindows::slotFinishedProcessVM(int exitCode, QProcess::ExitStatus exitStatus)
{
	qInfo() << "ImageGrab.exe quit"<<"[exitCode="<< exitCode << "ExitStatus=" << exitStatus << "]";
	//if (exitStatus== QProcess::NormalExit)
	//{
	//	//正常退出
	//}
	//else
	//{
	//	//非正常退出
	//}
	
}

void CncOpWindows::CloseVisionMeasureApp()
{
	//关闭当前视觉测量进程
	if (m_pProcessVM)
	{
		//
		//关键点,必须将嵌入的QWindow设置为没有父对象,在调用退出指令就正常了
      /*  if (m_ChildVMWin)
        {
            m_ChildVMWin->setParent(nullptr);
        }*/
		
		//正常退出程序terminate 发送一个close消息到顶层窗口.
		//m_pProcessVM->terminate();
		//结束app信号 共享内存与子进程通信
        if (PointGlobalParams()->m_pDataApp)
        {
            PointGlobalParams()->m_pDataApp->bC2V[1] = true;
        }
		//等待结束不能省,
		bool bFinish=m_pProcessVM->waitForFinished(5000);
		if (!bFinish)
		{
			//非正常退出
			qInfo() << "ImageGrab.exe crash quit";
		}
	}
	
}

子进程实现

//mian函数中的实现,只需要传递子进程窗口的winid,也即window下的窗口句柄
 if (argc >1)
    {
        //关键代码,触发错误之后,主进程能够响应
        fprintf(stderr, "%lld", (quint64)w.winId());//写入标准错误输出,stderr能立即输出,stdout则不行
        w.hide();
    }
    else
    {
        w.show();
    }
//定义一个定时器,时刻监控共享内存的状态,来执行下面这两个函数
//显示当前app
void ImageGrab::slotShowApp()
{
      //将当前窗口处于激活状态
      m_pGlobalPa->m_pDataApp->bV2C[0] = true;
      if (this->isHidden())
      {
          this->show();
          this->activateWindow();
      }
}

void ImageGrab::slotCloseApp()
{
    //将当前app关闭
    this->close();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/714511.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SNAT和DNAT策略

1、SNAT策略及应用 SNAT应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff08;私有不能在Internet中被正常路由&#xff09; SNAT原理&#xff1a; 修改数据包的源地址。 SNAT转换前提条件&#xff1a; 局域网各主机已正确设置IP地址、子网掩码、默认…

库的制作 与 使用 (Linux下)

目录 动静态库的制作 前置知识 库的基本构造 问题 分析 要给什么文件 如何更好的让别人使用 库的生成 静态库的生成 makefile参考 动态库的生成 makefile参考&#xff08;包含动态库和静态库生成&#xff09; 库的使用 法一&#xff1a;放入系统路径 弊端 法二…

Android开发系列:高性能视图组件Surfaceview

一、Surfaceview概述 在Android应用开发领域&#xff0c;面对视频播放、游戏构建及相机实时预览等高性能需求场景&#xff0c;直接操控图像数据并即时展示于屏幕成为必要条件。传统View组件在此类情境下显现局限性&#xff1a; 性能瓶颈&#xff1a;传统View的绘制任务由UI主…

如何充分利用 Postgres 的内存设置

为了充分利用 PostgreSQL 的内存设置&#xff0c;你需要调整多个参数以优化数据库性能。这些参数包括共享缓冲区&#xff08;shared_buffers&#xff09;、工作内存&#xff08;work_mem&#xff09;、维护工作内存&#xff08;maintenance_work_mem&#xff09;、有效缓存大小…

命令词:引导行动的语言工具

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

《全职猎人》

《全职猎人》 [1-2]是日本漫画家富坚义博的作品。 1999年版改编电视动画由日本动画公司负责动画制作&#xff0c;于1999年10月16日&#xff0d;2001年3月30日在富士电视台播出&#xff0c;该动画的故事至贪婪之岛篇章结束&#xff0c;全92话。 该作在富坚义博老师天马行空的想…

markupsafe,一个神奇的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个神奇的 Python 库 - markupsafe。 Github地址&#xff1a;https://github.com/pallets/markupsafe 在 Web 开发和模版渲染中&#xff0c;处理用户输入的数据时&#xff0c;防止 HTML 注入是至…

【Java】Object、Objects、包装类、StringBuilder、StringJoiner

目录 1.API2.Object类3.Objects类4.包装类4.1包装类概述4.2包装类的其他常见操作 5.StringBuilder 可变字符串5.1概述5.2StringBuilder案例 6.StringJoiner 1.API API&#xff1a;应用程序编程接口&#xff0c;全称application programing interface&#xff0c;即Java已经写好…

3分钟带手把手带你了解 FL Studio v21.2.3.4004 中文免费版(附中文设置教程)安装指南

3分钟带手把手带你了解 FL Studio v21.2.3.4004 中文免费版(附中文设置教程)安装指南&#xff0c;大家我是兔八哥爱分享&#xff0c;今天你带来的安装FL Studio 21破解版&#xff0c;纯正简体中文支持&#xff01; FL Studio 21 简称FL21&#xff0c;全称Fruity Loops Studio&a…

消息队列-Rabbit运行机制

Producer(生产者) 和 Consumer(消费者) Producer(生产者) :生产消息的一方&#xff08;邮件投递者&#xff09;Consumer(消费者) :消费消息的一方&#xff08;邮件收件人&#xff09; 消息一般由 2 部分组成&#xff1a;消息头&#xff08;或者说是标签 Label&#xff09;和 …

keystone认证服务

keystone认证服务 1、keystone管理用户 1-1、简介&#xff1a; 在OpenStack云计算平台中&#xff0c;Keystone是一个核心组件&#xff0c;主要用于提供统一的认证服务。其功能包括&#xff1a; 身份验证&#xff1a;Keystone负责验证用户的身份&#xff0c;通常通过用户名和…

记录一个flink跑kafka connector遇到的问题

【报错】 D:\Java\jdk1.8.0_231\bin\java.exe "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.3\lib\idea_rt.jar56647:D:\Program Files\JetBrains\IntelliJ IDEA 2022.2.3\bin" -Dfile.encodingUTF-8 -classpath D:\Java\jdk1.8.0_231\jre\lib\cha…

本学期嵌入式期末考试的综合项目,我是这么出题的

时间过得真快&#xff0c;临近期末&#xff0c;又到了老师出卷的时候。作为《嵌入式开发及应用》这门课的主讲教师&#xff0c;今年给学生出的题目有一点点难度&#xff0c;最后的综合项目要求如下所示&#xff0c;各位学生朋友和教师同行可以评论一下难度如何&#xff0c;单片…

DataWhale - 吃瓜教程学习笔记(一)

学习视频&#xff1a;第1章-绪论_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 第一章 机器学习三观 What&#xff1a;什么是机器学习&#xff1f; 关键词&#xff1a;“学习算法” Why: 为什么要学机器学习&#xff1f; #### 1. 机器学习理论研究#### 2. 机器学习系统开…

[240615] X-CMD 发布 v0.3.11,增加对 elvish 的支持

目录 X-CMD 发布 v0.3.11&#xff0c;增加对 elvish 的支持&#xff0c;并优化对 nushell&#xff0c;fish&#xff0c;xonsh&#xff0c;tcsh 的支持✨ co 模块 - copilot✨ elv 模块✨ hub X-CMD 发布 v0.3.11&#xff0c;增加对 elvish 的支持&#xff0c;并优化对 nushell&…

Python合并文件(dat、mdf、mf4)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

LabVIEW与C#的区别及重新开发自动测试程序的可行性分析

LabVIEW和C#是两种广泛使用的编程语言&#xff0c;各自有不同的应用领域和特点。本文将详细比较LabVIEW与C#在自动测试程序开发中的区别&#xff0c;并分析将已完成的LabVIEW自动测试程序重新用C#开发的合理性。本文帮助评估这种转换的必要性和潜在影响。 LabVIEW与C#的区别 开…

怎么把三列数据相同的号码一起求和?

可以使用excel的合并计算功能。 一、合并计算 将三列求和的数字列标题改成相同的&#xff0c;示例中全改成B1&#xff0c;这个是使用合并计算的关键一步&#xff0c;不改列标题&#xff0c;计算结果会是分开的。 2. 然后选中任意空白单元格作为输入结果的起始位置&#xff0c;…

Python学习笔记11:入门终结篇

前言 入门知识到这里基本结束了&#xff0c;这里主要讲一下input和range。这两个讲完&#xff0c;讲讲后面进阶学些啥。 range函数 之前将循环的时候讲过一点&#xff0c;这个函数是Python内置的函数&#xff0c;主要用来生成一系列数字&#xff0c;简单方便。 这里重新&…

Java17 --- SpringSecurity之前后端分离处理

目录 一、实现前后端分离 1.1、导入pom依赖 1.2、认证成功处理 1.3、认证失败处理 1.4、用户注销处理 1.5、请求未认证处理 1.6、跨域处理 1.7、用户认证信息处理 1.8、会话并发处理 一、实现前后端分离 1.1、导入pom依赖 <dependency><groupId>co…