Qt没有真正完美的无边框解决方案吗?
18 个回答
谢... 忘记 哪个大佬一年前邀请我了,看了半天大家都实现的Windows上跟Mac上的无边框的解决方案。
这里我就抛砖引玉,上个Linux的无边框的解决方案吧。
估计Qt写Linux无边框的博客我这是全网第一份。
众所周知,Linux是基于X协议的,所以这里要写x函数,本次使用xlib来实现。你也可以用xcb的函数。
是的我又犯懒了,本来想自己写xlib的函数的,发现deepin的dktwidget里已经有对应的实现了,这里我们把他偷过来
https:// github.com/linuxdeepin/ dtkwidget/tree/master/src/platforms/x11
先上实现,这里上关键代码,重写这三个函数就可以达到效果
void FLWidget_Linux::mouseMoveEvent(QMouseEvent *event)
#ifdef Q_OS_LINUX
const int x = event->x();
const int y = event->y();
if (resizingCornerEdge == XUtils::CornerEdge::kInvalid)
XUtils::UpdateCursorShape(this, x, y, this->layout()->contentsMargins(), ResizeHandleWidth);
#endif
return QWidget::mouseMoveEvent(event);
void FLWidget_Linux::mousePressEvent(QMouseEvent *event)
#ifdef Q_OS_LINUX
const int x = event->x();
const int y = event->y();
if (event->button() == Qt::LeftButton)
const XUtils::CornerEdge ce = XUtils::GetCornerEdge(this, x, y, this->layout()->contentsMargins(), ResizeHandleWidth);
if (ce != XUtils::CornerEdge::kInvalid)
resizingCornerEdge = ce;
//send x11 move event dont send mouserrelease event
XUtils::SendButtonRelease(this, event->pos(), event->globalPos());
XUtils::StartResizing(this, QCursor::pos(), ce);
#endif
return QWidget::mousePressEvent(event);
void FLWidget_Linux::resizeEvent(QResizeEvent *e)
#ifdef Q_OS_LINUX
int resizeHandleWidth = ResizeHandleWidth;
XUtils::SetWindowExtents(this, this->layout()->contentsMargins(), resizeHandleWidth);
#endif
return QWidget::resizeEvent(e);
void FLWidget_Linux::mouseReleaseEvent(QMouseEvent *event)
#ifdef Q_OS_LINUX
resizingCornerEdge = XUtils::CornerEdge::kInvalid;
#endif
return QWidget::mouseReleaseEvent(event);
这里有个小坑,使用xlib的move之后,Qt会收不到鼠标抬起的事件,所以这里我又在deepin的代码里加了个新的函数
//XUtils::SendButtonRelease(this, event->pos(), event->globalPos());
void SendButtonRelease(const QWidget *widget,
const QPoint &pos, const QPoint &globalPos)
const auto display = QX11Info::display();
const auto screen = QX11Info::appScreen();
XEvent xevent;
memset(&xevent, 0, sizeof(XEvent));
xevent.type = ButtonRelease;
xevent.xbutton.button = Button1;
xevent.xbutton.window = widget->effectiveWinId();
xevent.xbutton.x = pos.x();
xevent.xbutton.y = pos.y();
xevent.xbutton.x_root = globalPos.x();
xevent.xbutton.y_root = globalPos.y();
xevent.xbutton.display = display;
XSendEvent(display, widget->effectiveWinId(), False, ButtonReleaseMask,
&xevent);
XFlush(display);
来手动发送下这个事件
效果

Ubuntu
ubuntu的无边框很有意思,你只需要把Qt的flag设置成 Qt::FramelessWindowHint ,不用重写x消息就能够自己无边框可移动。
Qt5.15
据说Qt5.15的QWindow类自带了无边框可移动的flag,可以等等这个feature。
未实现的
至于最大化,最小化,全屏之类的,deepin的代码里都封装好了对应的x函数。我又犯懒了,不想再加上自定义的标题栏,这个就算是抛砖引玉吧
上github代码链接 https:// github.com/CryFeiFei/FL Widget
也顺便实现了下windows的无边框的实现,欢迎star。
也欢迎关注我的公众号:张小飞那些事儿
上个链接
几乎完美!
开源项目Github地址: Bringer-of-Light/Qt-Nice-Frameless-Window
注意:最新实现比下面所写的内容更完善,具体请以Github上的为准。
===============以下为原回答===============
windows系统实现无边框,同时支持Aero效果:
1.边框都没了,标题栏肯定也没了,三个系统按钮也没了,所以需要自己实现最小化、最大化、关闭 三个按钮。
2.需要自己实现鼠标拖动窗口效果。原理很简单,重载 mousePressEvent、mouseReleaseEvent、mouseMoveEvent 这三个事件处理函数,自己处理鼠标拖动逻辑
3.MainWindows构造函数里,使用以下代码:
setWindowFlags(Qt::Window | \
Qt::FramelessWindowHint | \
Qt::WindowSystemMenuHint | \
Qt::WindowMinMaxButtonsHint \
SetWindowLongPtr(HWND(winId()), GWL_STYLE, WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX );
//把client区域扩展到标题栏上和边框上,只留1个像素的余量
const MARGINS shadow = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);
4.重载 nativeEvent函数,其中代码如下
MSG* msg = (MSG *)message;
switch (msg->message)
case WM_NCCALCSIZE:
//this kills the window frame and title bar we added with
//WS_THICKFRAME and WS_CAPTION
*result = 0;
return true;
case WM_NCHITTEST:
*result = 0;
const LONG border_width = 5; //in pixels
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if(resizeWidth)
//left border
if (x >= winrect.left && x < winrect.left + border_width)
*result = HTLEFT;
//right border
if (x < winrect.right && x >= winrect.right - border_width)
*result = HTRIGHT;
if(resizeHeight)
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - border_width)
*result = HTBOTTOM;
//top border
if (y >= winrect.top && y < winrect.top + border_width)
*result = HTTOP;
if(resizeWidth && resizeHeight)
//bottom left corner
if (x >= winrect.left && x < winrect.left + border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width)
*result = HTBOTTOMLEFT;
//bottom right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width)
*result = HTBOTTOMRIGHT;
//top left corner
if (x >= winrect.left && x < winrect.left + border_width &&
y >= winrect.top && y < winrect.top + border_width)
*result = HTTOPLEFT;
//top right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y >= winrect.top && y < winrect.top + border_width)
*result = HTTOPRIGHT;
//*result仍然等于0,说明鼠标位置不在上述的边框范围内,则还有可能处于标题栏范围内
//标题栏效果(win7 及以上):
//1. 双击标题栏,窗口在最大化、正常化之间切换
//2. 拖动标题栏时,可以移动窗口,移动时如果鼠标触碰到桌面顶端,则最大化窗口等类似效果
if(*result==0)
//此处可以定义一个标题栏范围
//也可以认为整个窗口都是标题栏
*result = HTCAPTION ;
//注意:唯一遗留难题(此问题已解决,请到开源项目中查看)
//此处如果返回false,则上述所有操作无效,不能实现标题栏效果
//此处如果返回true,则实现标题栏效果,但窗口内的所有控件,都收不到鼠标点击消息。
//欲解决此问题,需要对Qt Event System 有更深刻的了解
return false;
return true;
} //end case WM_NCHITTEST
default:
return QMainWindow::nativeEvent(eventType, msg, result);
5. 需要的库
#ifdef Q_OS_WIN
#include <windows.h>
#include <WinUser.h>
#include <windowsx.h>
#include <dwmapi.h>
#include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
#include <gdiplus.h>
#include <GdiPlusColor.h>
#pragma comment (lib,"Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved external symbol __imp__DwmExtendFrameIntoClientArea
#pragma comment (lib,"user32.lib")
#endif
OSX 系统实现无边框,同时保留最小化、最大化、关闭按钮和窗口阴影、窗口圆角:
1. 需要使用C++ 和 object-c 混合编程,参考 Mixing Objective-C, C++ and Objective-C++: an Updated Summary
2.需要自己实现鼠标拖动窗口行为,和上述第2条相同。
3.MainWindow构造函数中使用以下代码:
//如果当前osx版本老于10.9,则后续代码不可用。转为使用自定义的最小化等按钮,不支持自由缩放窗口及窗口阴影
if (QSysInfo::MV_None == QSysInfo::macVersion())
if (QSysInfo::MV_None == QSysInfo::MacintoshVersion) {setWindowFlags(Qt::FramelessWindowHint); return;}
if (QSysInfo::MV_10_9 >= QSysInfo::MacintoshVersion) {setWindowFlags(Qt::FramelessWindowHint); return;}
//以下实现隐藏标题栏,但同时仍保留系统按钮、可变大小边框、圆角矩形、窗口阴影等 默认效果
//获取view所在的window
NSView* view = (NSView*)winId();
if (0 == view) {setWindowFlags(Qt::FramelessWindowHint); return;}
NSWindow *window = view.window;
if (0 == window) {setWindowFlags(Qt::FramelessWindowHint); return;}
//设置标题文字和图标为不可见
window.titleVisibility = NSWindowTitleHidden; //MAC_10_10及以上版本支持
//设置标题栏为透明
window.titlebarAppearsTransparent = YES; //MAC_10_10及以上版本支持