Qt4下创建自定义窗体

时间:2023-01-27 09:43:43
Qt4下创建自定义窗体 Qt下想创建一个可以自定义标题栏的窗体,一般大家都会使用FramelessWindowHint枚举通过QWidget的setWindowFlags方法来把窗体的边框和标题栏去掉,虽然这么做可以实现一个大家想要打窗体,但是它带来的负作用是失去了很多原生窗体的一些自定义的一些事件响应(比如win+方向键无法响应,无法自动布局很不爽)。还好鄙人是写C#出生,在winform下有过自定义窗体的绘制经验,就想想能不能在Qt中直接调用windows api来给QWidget套个外壳。 废话不多说直接来实践下吧! Qt重要捕获windows的事件的话需要重载winEvent方法:
protected:
virtual bool winEvent(MSG *message, long *result);
Qt帮助下对这个函数做了介绍:
This special event handler can be reimplemented in a subclass to receive native Windows events which are passed in themessage parameter. In your reimplementation of this function, if you want to stop the event being handled by Qt, return true and setresult to the value that the window procedure should return. If you return false, this native event is passed back to Qt, which translates the event into a Qt event and sends it to the widget.(英语不太好久不在这边显摆翻译了)。实现了这个方法,就能捕获windows的他的一些事件啦~ 1.首先,我是想去掉windows自带的标题栏,这样我就可以绘制自己要的最大化,最小化,关闭按钮等等。 直接在窗体构造函数里加入代码
SetWindowLong(this->winId(), GWL_STYLE, GetWindowLong(this->winId(), GWL_STYLE) & ~WS_CAPTION);
Qt4下创建自定义窗体由于我的电脑是win10的,win10的效果就这这样的!是不是感觉成功大半了!接下来的就交给我们上面提到的那个方法吧。
1.要自己实现WM_NCPAINT函数的处理,来自己绘制自己想要的窗体边框:
<span style="white-space:pre"></span>HWND hwnd = message->hwnd;
WPARAM wParam = message->wParam;
WPARAM lParam = message->lParam;
if (message->message == WM_NCPAINT)
{
RECT rc;
GetClientRect(hwnd,&rc);
RECT rcWnd;//窗口坐标
GetWindowRect(hwnd,&rcWnd);
ScreenToClient(hwnd,(LPPOINT)&rcWnd.left);// 将窗口坐标转换为客户坐标
ScreenToClient(hwnd,(LPPOINT)&rcWnd.right);
rc.left -= rcWnd.left; //计算得到窗口内新的客户区域坐标
rc.top -= rcWnd.top;
rc.right += rc.left;
rc.bottom += rc.top;
HDC hdc = GetWindowDC(hwnd);
//现在rc是一个新的窗口内的客户区坐标
ExcludeClipRect(hdc,rc.left,rc.top,rc.right,rc.bottom); //不刷新客户区域
InflateRect(&rc,m_nThickness,m_nThickness); // 将客户区域坐标扩大WIDTH
//在客户区域的外围画一个红色边框
HBRUSH hBr;
int r,g,b;
if (!m_ncActived)
{
m_ncUnActivedColor.getRgb(&r,&g,&b);
}
else
{
m_ncActivedColor.getRgb(&r,&g,&b);
}
hBr = CreateSolidBrush(RGB(r,g,b));
FillRect(hdc,&rc, hBr);
ReleaseDC(hwnd,hdc);
return 1;
}
2.接着实现WM_NCCALCSIZE当窗体移动或者改变大小时来重新规定他的客户区大小:
<span style="white-space:pre"></span>else if (message->message == WM_NCCALCSIZE)
{
NCCALCSIZE_PARAMS* ncParams = (NCCALCSIZE_PARAMS* )lParam;

RECT rect0 = ncParams->rgrc[0];
rect0.left+=m_nThickness;
rect0.top+=m_nThickness;
rect0.right -= m_nThickness;
rect0.bottom -= m_nThickness;

ncParams->rgrc[0] = rect0;

*result =0;
return 1;
}
3.最后来实现下WM_NCACTIVATE,当窗体非客户区失去焦点和得到焦点对边框进行换色处理
<span style="white-space:pre"></span>else if (message->message == WM_NCACTIVATE)
{
if (m_ncActived!=(bool)wParam)
{
m_ncActived = wParam;
SendMessage(hwnd,WM_NCPAINT,1,0);
}
*result =1;
return 1;
}
4.当然最后对另外的事件还是要让Qt自己去处理咯:
return QWidget::winEvent(message,result);
来看看效果吧,窗体在激活状态下(红色区域是客户区):
Qt4下创建自定义窗体
然后是非激活状体下 Qt4下创建自定义窗体窗体的拉伸完全是系统自带的哦,而且能够响应win的自带的事件:Qt4下创建自定义窗体

PS:当然这样的窗体还是不完美的还存在一些问题:1、去掉了标题栏,就会失去标题栏的一些事件响应,比如鼠标按住标题栏拖到桌面边缘也能进行自动布局这样也很方便,当然也是有办法可以让他实现的,通过重写WM_NCHITTEST来把客户区一块区域模拟成标题栏(这边就不实现了,代码也很简单的)。2、不同系统的所需边框宽度是不同win7下m_nThickness定义为3就是像素为3的边框,win10下会有误差。3、求大神指教如何把win10顶部的白条去掉!!!!