Qt 窗口阴影

Qt实现窗口阴影网上有多种实现方式,比如:

1.使用qt自带的方法,QGraphicsDropShadowEffect

2.通过在无边框背景透明的窗口中paintevent事件中绘制出来

3.通过dwmapi库的阴影功能具体代码如下:

#pragma once
#include <QWidget>
#include "ui_customwidgetframe.h"

class CustomWidgetFrame : public QWidget {
	Q_OBJECT

public:
	CustomWidgetFrame(QWidget * parent = Q_NULLPTR);
	~CustomWidgetFrame();

	bool nativeEvent(const QByteArray &eventType, void *message, long *result);

private:
	Ui::CustomWidgetFrame ui;
};
#include "customwidgetframe.h"
#include <dwmapi.h>
#include <windowsx.h>
#include <qt_windows.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "user32.lib")

CustomWidgetFrame::CustomWidgetFrame(QWidget * parent) : QWidget(parent) {
	ui.setupUi(this);

	setWindowFlags(windowFlags() | Qt::FramelessWindowHint);

	bool visible = isVisible();

	/*
	此行代码可以带回Aero效果,同时也带回了标题栏和边框,在nativeEvent()会再次去掉标题栏
	主要的关键是WS_THICKFRAME,可以实现窗口停靠边缘自动改变大小功能和Aero效果
	*/
	//this line will get titlebar/thick frame/Aero back, which is exactly what we want
	//we will get rid of titlebar and thick frame again in nativeEvent() later
	HWND hwnd = (HWND)this->winId();
	DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
	::SetWindowLong(hwnd, GWL_STYLE, style | WS_THICKFRAME);

	//保留一个像素的边框宽度,否则系统不会绘制边框阴影
	//
	//we better left 1 piexl width of border untouch, so OS can draw nice shadow around it
	const MARGINS shadow = { 1, 1, 1, 1 };
	DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);

}

CustomWidgetFrame::~CustomWidgetFrame() {
	
}

bool CustomWidgetFrame::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
	MSG *param = static_cast<MSG *>(message);
	static long cnt = 10000;
	printf("######:%x --- %d\n", param->message, ++cnt);
	switch (param->message)
	{
	case WM_NCCALCSIZE: {
		/*
		此消息用于处理非客户区域,比如边框的绘制
		返回false,就是按系统的默认处理,如果返回true,而不做任何绘制,则非客户区域
		就不会被绘制,就相当于没有绘制非客户区域,所以就会看不到非客户区域的效果
		*/
		return true;
	}
	case WM_NCHITTEST:
	{
		const LONG border_width = 3;
		RECT winrect;
		GetWindowRect(HWND(winId()), &winrect);

		long x = GET_X_LPARAM(param->lParam);
		long y = GET_Y_LPARAM(param->lParam);

        /*
        只用这种办法设置动态改变窗口大小比手动通过鼠标事件效果好,可以
        避免闪烁问题
        */
		//left border
		if (x >= winrect.left && x < winrect.left + border_width)
		{
			*result = HTLEFT;
			return true;
		}
		//right border
		if (x < winrect.right && x >= winrect.right - border_width)
		{
			*result = HTRIGHT;
			return true;
		}

		//bottom border
		if (y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOM;
			return true;
		}
		//top border
		if (y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOP;
			return true;
		}
		//bottom left corner
		if (x >= winrect.left && x < winrect.left + border_width &&
			y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOMLEFT;
			return true;
		}
		//bottom right corner
		if (x < winrect.right && x >= winrect.right - border_width &&
			y < winrect.bottom && y >= winrect.bottom - border_width)
		{
			*result = HTBOTTOMRIGHT;
			return true;
		}
		//top left corner
		if (x >= winrect.left && x < winrect.left + border_width &&
			y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOPLEFT;
			return true;
		}
		//top right corner
		if (x < winrect.right && x >= winrect.right - border_width &&
			y >= winrect.top && y < winrect.top + border_width)
		{
			*result = HTTOPRIGHT;
			return true;
		}


		return QWidget::nativeEvent(eventType, message, result);
	}
	}

	return QWidget::nativeEvent(eventType, message, result);
}

第3种方法效果还是不错的,比较推荐使用,效果和系统效果很像

该代码有2个主要功能:

1.阴影功能

2.拉动窗口改变大小功能,这里的拉动窗口改变大小功能比较推荐使用,可以避免闪烁问题

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章