欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt 杂谈 —— Qt中捕获某个窗口的停用和**的消息处理不同情况 —— Activate & Deactivate

程序员文章站 2022-06-01 19:46:51
...

Qt中捕获某个窗口的停用和**的消息处理不同情况 —— Activate & Deactivate

背景&原理说明

背景

     本人由于最近接到一个需求,很奇特,就是让一个窗口在MainWindow**的时候,设置成最顶层窗口,但是在非**又不是最小化的状态下,处于非顶层,不然就会出现一种神奇的bug,就是当前主窗口为非**状态时,某些窗口依旧悬浮在顶层盖住了其他应用的显示区域,这就非常的奇怪。

     因此正在网上搜集资料的时候,看到了一个很有意思的文章(具体见参考文章),解决了我一部分工作中遇到的问题,主要是给我提供了一个崭新的思路去解决一些问题,虽然最终还是没有完全解决我工作中遇到的这个bug,但是确实这里值得我来记录一下,作为一个学习和思考的笔记。不过最后还是要说,自己组长厉害就是厉害,经验就是到位,这个问题被我组长用另外一个方案,利用QApplication的Activate和InActive的状态来完美解决了。

     当然,如果有什么新的思路,或者是觉得我写的有什么错误,也欢迎大家批评指导。

原理

void QWidget::focusOutEvent ( QFocusEvent * event )   [virtual protected] 

     同样一开始,我跟参考文献中的作者思考的是一样的,重写FocusOutEvent可以解决这个问题,同样,我试了试,发现了自己的天真可爱。直到我找到了我参考的文章,我才明白为什么他没用,因为这是个跨线程的操作,所以我根本就不在你那个线程里面了你再怎么Focus也没用啊。

     任何一个时候,我们的Windows桌面上总有一个最前台的窗口,其实说简单的,就是标题栏变成深蓝色的那个窗口,仅此一个,这个窗口就是前景窗口(Foreground Window),其他窗口就是后台窗口(Background Window)。那创建前景窗口的线程就是前景线程(Foreground Thread),这个线程并不一定就是应用程序的主线程。
     线程内部会维护当前自己的活动窗口(Active Window)和焦点窗口(Focus Window), 焦点窗口其实只是窗口的一个属性,其实就是“焦点状态”是窗口的一个属性,而焦点窗口的顶层窗口就是活动窗口,举个例子:一个对话框中有一个按钮,当按钮 获得焦点的时候,那此按钮就是焦点窗口,则包含此按钮的对话框就是活动窗口,若出现窗口嵌套的情况,则最根的那个窗口才是活动窗口。(以上文字引自参考的文章 )

代码案例

     那么从实践的角度来讲,我在参考文章的示例代码基础上,写了个小demo调试了一下。具体代码如下:

首先 , main.cpp:

///
// Copyright (c) 2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include "activate_window.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	ActivateWindow w;
	w.show();
	return a.exec();
}

接下来, activate_window.h:

///
// Copyright (c) 2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#ifndef _ACTIVATE_WINDOW_H_H_
#define _ACTIVATE_WINDOW_H_H_



#include <QtWidgets/QMainWindow>
#include "ui_activate_window.h"

class ActivateWindow : public QMainWindow
{
	Q_OBJECT

public:
	ActivateWindow(QWidget *parent = Q_NULLPTR);

protected:
	bool eventFilter(QObject *watched, QEvent *event) override;

private:
	Ui::ActivateWindowClass ui;
};

#endif // !_ACTIVATE_WINDOW_H_H_

最后, activate_window.cpp:

///
// Copyright (c) 2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include <QtCore/QEvent>
#include "activate_window.h"

ActivateWindow::ActivateWindow(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	installEventFilter(this);
}

bool ActivateWindow::eventFilter(QObject * watched, QEvent * event)
{
	if (watched == this)
	{
		//窗口停用,变为不活动的窗口  
		if (QEvent::WindowDeactivate == event->type())
		{
			//  
			//DEBUGP( "eventFilter" ) ;  
			// hide();  //或者关闭窗口,加入自己的操作.  
			setStyleSheet("QWidget { background-color : red; }");
			return true;
		}
		else
		{
			// setStyleSheet("QWidget { background-color : blue; }");
			return false;
		}
	}
	return false;
}

项目中的实际应用举例

     在本人项目中,找到了另外一种利用QApplication的状态监听来改变对应的状态的解决办法,该办法合理的解决了窗口顶层的问题, 具体代码示意如下,主要理解想法,代码上下文不方便提供:

     在初始化函数中:

connect(qApp, &QApplication::applicationStateChanged, [this](Qt::QpplicationState state) 
{
	if(!windowHandle()) 
	{
		return;
	}
	if (state & Qt::ApplicationActive) 
	{
		windowHandle()->setFlag(Qt::WindowStaysOnTopHit);
	}
	else if (state & Qt::ApplicationInactive) 
	{
		windowHandle()->setFlag(Qt::WindowStaysOnTopHit, false);
	}
});

     通过上面的逻辑我们不难发现,捕获App的状态修改,我们可以在主窗口**和非**状态下,完成一些不同的操作。

参考文章

Qt 中如何捕获窗口停用和**的消息 Activate&Deactivate

个人格言

     用心去感受你自己需要坚持的生活,未来慢慢会给你答案的。