-
2020-05-30 15:56:17
Qt虽然提供了诸如
Qt::FramelessWindowHint
之类的属性可以移除窗体的边框,但是移除边框之后,窗体的一些默认行为也被移除了,如鼠标拖动改变大小、双击标题栏最大化等,这些行为需要开发者自己来实现。本文主要介绍实现无边框窗体的几种方案。
笔者认为,一个完美的无边框窗体解决方案需要支持如下功能:
- 支持通过使用鼠标拖拽来改变窗体位置和大小;
- 支持双击标题栏最大化窗体和还原窗体;
- 支持
Windows Areo Snap
特性; - 支持系统阴影;
- 支持跨不同DPI的屏幕拖拽;
- 适应分辨率和DPI改变;
在Qt中实现无边框窗体有2种方案:
一、Hook方案
通过重载
nativeEvent
函数拦截Windows消息(如WM_NCHITTEST
)来实现,大致步骤如下:- 给窗体设置
WS_THICKFRAME | WS_CAPTION
属性从而还原窗体边框和标题栏,这样窗体就可以接收到WM_NCHITTEST
消息。 - 在
WM_NCCALCSIZE
消息处理中再移除边框和标题栏。 - 在
WM_NCHITTEST
消息处理中通过判断鼠标位置来设置鼠标行为(HTLEFT
,HTRIGHT
等)。
这种方案的优点是可以支持
Windows Areo Snap
和系统阴影
的特性,但是针对Windows消息处理起来很复杂而且需要兼容Qt的各个版本,目前我还没有找到一个通过这种方案来完美实现无边框的解决方案。据我所知,有如下的开源项目是通过这种方式来实现的,但都有些许问题,如不支持跨不同DPI屏幕拖拽、不能适应分辨率和DPI改变、
WM_NCHITTEST
有时无响应等。另外,在设置了背景透明属性之后(如Qt::WA_TranslucentBackground
),系统阴影特性也将消失。- qtdevs/FramelessHelper: https://github.com/qtdevs/FramelessHelper
- wangwenx190/framelesshelper: https://github.com/wangwenx190/framelesshelper
二、 纯Qt方案
这种方案不Hook windows的
WM_NCHITTEST
、WM_NCCALCSIZE
消息,也不改变窗体样式,通过纯Qt方式实现。通过对每个Widget设置MouseTracking
,来使每个Widget都可以响应鼠标事件(mouseMoveEvent、mousePressEvent、mouseReleaseEvent等),然后这些事件中判断鼠标位置来设置鼠标的形状和行为。这种方式虽然对鼠标位置的判断逻辑比较繁琐,但兼容性较好,较纯粹,不需要处理Windows的各个消息。
👉 笔者根倾向于使用这种方案,简单、稳定、兼容性好,不用关注那些令人头疼的Windows消息。针对该方案的实现,可以参考笔者的开源项目【Qt-FramelessWindow】,目前可以支持上述除
“Windows Areo Snap”和“系统阴影”
特性之外的所有无边框窗体的特性。- Qt-FramelessWindow: https://github.com/winsoft666/Qt-FramelessWindow
更多相关内容 -
Qt无边框窗口最大化时拖拽还原代码
2021-03-07 00:00:48# Qt无边框窗口最大化时拖拽还原代码 通过重定义eventFilter()函数和changeEvent()函数,实现Qt无边框窗口最大化时拖动标题栏还原窗口的效果。 -
Qt无边框对话框实现
2019-10-18 21:00:14Qt无边框界面实现, 支持拖拽移动、四边拖动大小,双击放大等系统边框支持的功能,模态对话框支持抖动效果。 -
Qt无边框、阴影、圆角、可拖动、有最大化最小化关闭按钮美观UI窗口
2020-12-08 11:40:00#Qt美观UI窗口 1. 去除系统自带边框; 2. 设置窗口圆角、窗口阴影; 3. 设置窗口可任意拖动; 4. 添加最大化、最小化、关闭按钮; 5.窗口功能完善,无bug。 -
Qt无边框窗口移动、拉伸、缩放
2016-10-18 16:58:09Qt无边框窗口的移动、拉伸边框、鼠标滚轮缩放大小 -
Qt无边框可拉伸改变大小
2019-01-15 11:12:11Qt无边框源码可拉伸改变大小,可根据自己需求任意改变窗口大小 -
Qt无边框窗口体拖拽边框改变窗体大小终极方法(橡皮筋窗体)
2021-02-05 13:37:46Qt无边框窗口体拖拽边框改变窗体大小终极方法(橡皮筋窗体) -
Qt 无边框界面 自绘图标 透明图标按钮 可设置背景状态颜色,图标颜色 最小化最大化关闭按钮等等
2020-07-25 14:56:30最近发现酷狗的新版exe软件挺好的,里面使用的全是自定义的按钮,所以自己写了个,大家有兴趣可以下载来看看。按钮类型目前有:最小化,最大化,关闭,更换皮肤,更多信息,搜索,下载,向左,向右,向上,向下,... -
Qt无边框窗口
2022-04-20 22:39:51无边框窗口 /** * 自定义无边框窗体、对话框和提示框 * * MainWindow.h * 测试用例主要窗口 * */ #include <QLabel> #include <QDebug> #include "MainWindow.h" #include "ui_MainWindow.h" #.../** * 自定义无边框窗体、对话框和提示框 * * MainWindow.h * 测试用例主要窗口 * */ #include <QLabel> #include <QDebug> #include "MainWindow.h" #include "ui_MainWindow.h" #include "ui_AeroClientWidget.h" #include "MuWinWindow.h" MainWindow::MainWindow(QWidget *parent) : MuCustomWindow(parent) , ui(new Ui::MainWindow) , aeroUI(new Ui::AeroCLientWidget) { setWindowTitle("Test Custom Window"); resize(800, 600); QWidget *pClientWidget = new QWidget(this); ui->setupUi(pClientWidget); this->titleBar()->setTitleHeight(50); this->titleBar()->setObjectName("titleBar"); this->titleBar()->titleLabel()->setObjectName("titleLabel"); this->titleBar()->minimizeButton()->setObjectName("minimizeButton"); this->titleBar()->maximizeButton()->setObjectName("maximizeButton"); this->titleBar()->closeButton()->setObjectName("closeButton"); // 设置中心客户区域 setClientWidget(pClientWidget); // 设置messagebox的按钮样式表和标题样式表 const QString buttonStyle = "QPushButton { \ border: none; \ background-color: #52baff; \ width: 80px; \ height: 30px; \ } \ QPushButton::pressed { \ background-color: gray; \ }"; MuCustomMessageBox::setTitleStyleSheet(QStringLiteral("QLabel { color: black }")); MuCustomMessageBox::setButtonStyleSheet(QDialogButtonBox::Ok, buttonStyle); connect(ui->dialogBtn, &QPushButton::clicked, this, &MainWindow::onDialogBtnClicked); connect(ui->informationBtn, &QPushButton::clicked, this, &MainWindow::onInformationBtnClicked); connect(ui->errorBtn, &QPushButton::clicked, this, &MainWindow::onErrorBtnClicked); connect(ui->successBtn, &QPushButton::clicked, this, &MainWindow::onSuccessBtnClicked); connect(ui->warningBtn, &QPushButton::clicked, this, &MainWindow::onWarningBtnClicked); #ifdef Q_OS_WIN32 initAreoWindow(); connect(ui->aeroBtn, &QPushButton::clicked, m_AeroWindow, &MuWinAeroShadowWindow::show); connect(ui->winWindowBtn, &QPushButton::clicked, this, &MainWindow::onWinWindow); #else ui->aeroBtn->hide(); #endif } MainWindow::~MainWindow() { delete ui; #ifdef Q_OS_WIN32 delete m_AeroWindow; #endif } void MainWindow::onDialogBtnClicked() { MuCustomDialog dialog; QLabel label("This is a Custom Dialog!"); label.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); dialog.titleBar()->titleLabel()->setObjectName("dialogTitleLabel"); dialog.setModal(true); dialog.setWindowTitle("dialog"); dialog.setClientWidget(&label); dialog.exec(); } void MainWindow::onInformationBtnClicked() { MuCustomMessageBox::showInformation(nullptr, QStringLiteral("Information!"), QStringLiteral("This is a Information MessageBox!")); } void MainWindow::onErrorBtnClicked() { MuCustomMessageBox::showError(nullptr, QStringLiteral("Error!"), QStringLiteral("This is a Error MessageBox!")); } void MainWindow::onSuccessBtnClicked() { MuCustomMessageBox::showSuccess(nullptr, QStringLiteral("Success!"), QStringLiteral("This is a Success MessageBox!")); } void MainWindow::onWarningBtnClicked() { MuCustomMessageBox::showWarning(nullptr, QStringLiteral("Warning!"), QStringLiteral("This is a Warning MessageBox!")); } #ifdef Q_OS_WIN32 void MainWindow::initAreoWindow() { m_AeroWindow = new MuWinAeroShadowWindow; m_AeroWindow->setRubberBandOnMove(true); m_AeroWindow->setRubberBandOnResize(true); m_AeroWindow->setWindowTitle(QStringLiteral("Test Aero Window")); m_AeroWindow->titleBar()->setObjectName("aeroTitleBar"); QWidget *pClientWidget = new QWidget(m_AeroWindow); aeroUI->setupUi(pClientWidget); m_AeroWindow->setClientWidget(pClientWidget); } #endif void MainWindow::onWinWindow() { MuWinWindow *window = new MuWinWindow; window->resize(800, 600); window->show(); }
-
Qt无边框窗口模仿windows原生窗口桌面边缘移动动画
2018-10-19 16:31:18无边框窗口在移动到桌面边缘时不会自动全屏或者半屏,网上有些资料能实现全屏或者半屏,但是窗口变化过程中没有动画。所以我想实践下能不能实现windows原生的效果。 -
QT 无边框窗体设计
2018-03-13 09:47:03本demo 介绍了如何设计QT的无边框窗体 同时 解决了 窗体大小改变 以及移动的问题 同时也介绍了如果美化按钮等等 -
Qt无边框窗口实现拖动
2021-08-27 21:30:071、设置无边框 w.setWindowFlags(Qt::FramelessWindowHint); 2、拖动窗口 mousePressEvent(QMouseEvent *e) //重写鼠标点击事件,为了获取开始点击的坐标 mouseMoveEvent(QMouseEvent *e) //重写移动事件, ...目标:
1、去除边框
2、窗口的上面部分实现拖动实现:
1、设置无边框
w.setWindowFlags(Qt::FramelessWindowHint);
2、拖动窗口
mousePressEvent(QMouseEvent *e) //重写鼠标点击事件,为了获取开始点击的坐标
mouseMoveEvent(QMouseEvent *e) //重写移动事件#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMouseEvent> #include <windows.h> //注意头文件 #include <windowsx.h> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; protected: void mousePressEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e); private: int boundaryWidth; QPoint clickPos; }; #endif // MAINWINDOW_H
void MainWindow::mousePressEvent(QMouseEvent *e) { if(e->button()==Qt::LeftButton) clickPos=e->pos(); } void MainWindow::mouseMoveEvent(QMouseEvent *e) { if(e->buttons()&Qt::LeftButton //左键点击并且移动 && e->pos().x()>=0 //范围在窗口的上面部分 && e->pos().y()>=0 && e->pos().x()<= geometry().width() && e->pos().y() <= geometry().height()/10) { move(e->pos()+pos()-clickPos); //移动窗口 } }
-
Qt无边框窗口实现拖动和改变大小
2021-11-05 14:58:32fps=1,但原文章在某些情况下,鼠标形状不刷新,改进了一版。 MyResizeWidget.h #ifndef MYRESIZEWIDGET_H #define MYRESIZEWIDGET_H ...QtGui> #define EDGE_MARGIN 8 namespace Ui { class MyRe主要参考了https://blog.csdn.net/qq_16952303/article/details/51974502?locationNum=8&fps=1,但原文章在某些情况下,鼠标形状不刷新,改进了一版。
MyResizeWidget.h
#ifndef MYRESIZEWIDGET_H #define MYRESIZEWIDGET_H #include <QtGui> #define EDGE_MARGIN 8 namespace Ui { class MyResizeWidget; } class MyResizeWidget : public QWidget { Q_OBJECT public: explicit MyResizeWidget(QWidget *parent = 0); protected: virtual void mouseMoveEvent(QMouseEvent * event); virtual void mousePressEvent(QMouseEvent * event); virtual void mouseReleaseEvent(QMouseEvent * event); virtual void enterEvent(QEvent * event); virtual void leaveEvent(QEvent * event); private: QPoint dragPosition; //鼠标拖动的位置 enum {nodir, top = 0x01, bottom = 0x02, left = 0x04, right = 0x08, topLeft = 0x01 | 0x04, topRight = 0x01 | 0x08, bottomLeft = 0x02 | 0x04, bottomRight = 0x02 | 0x08 } resizeDir; //更改尺寸的方向 private: void testEdge(QMouseEvent *event); //检测鼠标是否接近窗口边缘 }; #endif // SEARCHWIDGET_H
MyResizeWidget.cpp
#include "MyResizeWidget.h" #define min(a,b) ((a)<(b)? (a) :(b)) #define max(a,b) ((a)>(b)? (a) :(b)) MyResizeWidget::MyResizeWidget(QWidget *parent) : QWidget(parent) { resizeDir = nodir; //初始化检测方向为无 setWindowFlags(Qt::FramelessWindowHint); //设置无边框 setMouseTracking(true); //开启鼠标追踪 setMinimumSize(90, 200); } void MyResizeWidget::mousePressEvent(QMouseEvent * event) { if (event->button() == Qt::LeftButton) //每当按下鼠标左键就记录一下位置 { dragPosition = event->globalPos() - frameGeometry().topLeft(); //获得鼠标按键位置相对窗口左上面的位置 } } void MyResizeWidget::testEdge(QMouseEvent *event) { int diffLeft = event->globalPos().x() - frameGeometry().left(); //计算鼠标距离窗口上下左右有多少距离 int diffRight = event->globalPos().x() - frameGeometry().right(); int diffTop = event->globalPos().y() - frameGeometry().top(); int diffBottom = event->globalPos().y() - frameGeometry().bottom(); Qt::CursorShape cursorShape; if(diffTop < EDGE_MARGIN && diffTop>=0){ //根据 边缘距离 分类改变尺寸的方向 if(diffLeft < EDGE_MARGIN && diffLeft>=0){ resizeDir = topLeft; cursorShape = Qt::SizeFDiagCursor; } else if(diffRight > -EDGE_MARGIN && diffRight<=0){ resizeDir = topRight; cursorShape = Qt::SizeBDiagCursor; } else{ resizeDir = top; cursorShape = Qt::SizeVerCursor; } } else if(abs(diffBottom) < EDGE_MARGIN && diffBottom<=0){ if(diffLeft < EDGE_MARGIN && diffLeft>=0){ resizeDir = bottomLeft; cursorShape = Qt::SizeBDiagCursor; } else if(diffRight > -EDGE_MARGIN && diffRight<=0){ resizeDir = bottomRight; cursorShape = Qt::SizeFDiagCursor; } else{ resizeDir = bottom; cursorShape = Qt::SizeVerCursor; } } else if(abs(diffLeft) < EDGE_MARGIN){ resizeDir = left; cursorShape = Qt::SizeHorCursor; } else if(abs(diffRight) < EDGE_MARGIN){ resizeDir = right; cursorShape = Qt::SizeHorCursor; } else{ resizeDir = nodir; cursorShape = Qt::ArrowCursor; } QApplication::setOverrideCursor(cursorShape); } void MyResizeWidget::mouseMoveEvent(QMouseEvent * event) { if (event->buttons() & Qt::LeftButton){ //如果左键是按下的 if(resizeDir == nodir){ //如果鼠标不是放在边缘那么说明这是在拖动窗口 move(event->globalPos() - dragPosition); } else{ int ptop,pbottom,pleft,pright; //窗口上下左右的值 ptop = frameGeometry().top(); pbottom = frameGeometry().bottom(); pleft = frameGeometry().left(); pright = frameGeometry().right(); if(resizeDir & top){ //检测更改尺寸方向中包含的上下左右分量 if(height() == minimumHeight()){ ptop = min(event->globalY(),ptop); } else if(height() == maximumHeight()){ ptop = max(event->globalY(),ptop); } else{ ptop = event->globalY(); } } else if(resizeDir & bottom){ if(height() == minimumHeight()){ pbottom = max(event->globalY(),ptop); } else if(height() == maximumHeight()){ pbottom = min(event->globalY(),ptop); } else{ pbottom = event->globalY(); } } if(resizeDir & left){ //检测左右分量 if(width() == minimumWidth()){ pleft = min(event->globalX(),pleft); } else if(width() == maximumWidth()){ pleft = max(event->globalX(),pleft); } else{ pleft = event->globalX(); } } else if(resizeDir & right){ if(width() == minimumWidth()){ pright = max(event->globalX(),pright); } else if(width() == maximumWidth()){ pright = min(event->globalX(),pright); } else{ pright = event->globalX(); } } setGeometry(QRect(QPoint(pleft,ptop),QPoint(pright,pbottom))); } } else testEdge(event); //当不拖动窗口、不改变窗口大小尺寸的时候 检测鼠标边缘 } void MyResizeWidget::mouseReleaseEvent(QMouseEvent *event) { if(resizeDir != nodir){ //还原鼠标样式 testEdge(event); } } void MyResizeWidget::enterEvent(QEvent * event) { QWidget::enterEvent(event); } void MyResizeWidget::leaveEvent(QEvent * event) { QApplication::setOverrideCursor(Qt::IBeamCursor); QWidget::leaveEvent(event); }
-
qt 无边框窗口拉伸,可能是全网最精简的代码
2022-02-22 10:33:15王婆卖瓜自卖自夸!废话少说,上代码,基于QMainWindow: H: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow>...QT_BEGIN_NAMESPACE ...#define DEFAULT_CURSOR Qt::ArrowCursor class -
Qt 无边框设置的正确姿势
2021-08-31 11:23:51windowFlags() | Qt::FramelessWindowHint) 关键是带上原来的window flags,缺少了会导致奇怪的行为,例如对于一个QDialog MyDialog::MyDialog(QWidget *parent) : QDialog(parent) { ... this->... -
Qt无边框时鼠标拖动界面
2022-01-26 14:45:38// 设置为SplashScreen this->setWindowFlags(Qt::SplashScreen); // 与上边不一致处为:在任务栏上显示对话框 this->setWindowFlags(Qt::FramelessWindowHint); 1.创建继承自QWidget的派生类QXXX,并重载QWidget类... -
Qt无边框窗口拖拽和阴影
2021-07-02 16:32:46这一次是来用Qt实现一个无边框的可拖拽窗口,效果如下: 1.2实现步骤 步骤一: 实现窗口的去边框和窗口阴影 (1)、新建一个QWidget为基类的窗口,命名为widget; (2)、在widget.cpp文件中的构造函数中添加如下... -
QT无边框窗体类(Win/Linux/Mac、阴影边框、靠边半屏)
2021-09-07 23:00:07之前做过一版无边框窗体,可以参考前面博文,但存在两个缺点,一个不支持阴影边框,二不支持windows系统的贴边半屏,最近下定决心要攻克这个难题,很显然已经解决了。 此处感谢2位大神,代码中参考了2位前辈的部分... -
QT无边框窗口,无边框弹出框,无边框MessageBox
2022-04-20 22:48:37QT无边框窗口,无边框弹出框,无边框MessageBox -
Qt 实现无边框窗口,支持缩放窗口大小
2017-09-26 02:25:36Qt 实现无边框窗口,可以自由缩放窗口尺寸,QT5.8+MinGW编译通过,运行效果见博客分类“Qt实用技术”中关于无边框窗口的实现文章。 -
Qt无边框实现阴影拖动效果
2021-03-04 09:18:18Qt无边框实现阴影拖动效果 Qt-Nice-Frameless-Window githup上的开源项目,效果很漂亮的 效果图: -
Qt无边框窗体(Windows)
2019-10-31 09:13:44Qt无边框窗体Windows篇去掉标题栏和边框实现拖拽功能还原窗体功能注意点我们可以做的更好添加阴影亚克力面板效果结语 去掉标题栏和边框 首先第一步我们要通过设置系统绘制的边框消失 setWindowFlags(Qt::... -
Qt无边框窗体-最大化时支持拖拽还原
2019-09-29 10:02:32目录 一、概述 二、效果展示 三、demo制作 1、设计窗体 2、双击放大 四、拖拽 五、相关文章 ...原文链接:Qt无边框窗体-最大化时支持拖拽还原 一、概述 用Qt进行开发界面时,既想要实现友... -
qt无边框无标题栏自定义
2022-05-06 15:58:09自定义窗体实现无边框无标题栏拖动改变窗体大小功能 -
qt 无边框窗体拖动、拉伸
2020-08-31 21:29:32使用setAttribute( Qt::WA_Hover,true)也可以实现对鼠标的监控,相对于setMouseTracking(true)来说,它可以弥补鼠标事件被子窗体获取的问题: setAttribute(Qt::WA_Hover,true); 主要代码: bool Widget::... -
Qt 5.11 无边框可拖动 可改变大小窗口
2018-11-19 13:51:50QWidget 无边框,可拖动,可改变大小窗口。代码简单可复用。主窗口,子窗口都可以使用。 -
QT无边框窗口鼠标拖动事件
2020-12-15 11:00:22} void Dialogin::mouseMoveEvent(QMouseEvent *event) { if(m_moving && (event->buttons() && Qt::LeftButton) && (event->globalPos() - m_lastPos).manhattanLength() > QApplication::startDragDistance()) { ... -
Qt无边框之重新实现窗口拉伸缩放
2022-05-20 09:16:36在实际开发当中,很少会直接用Qt自带的边框,经常是去掉自带的边框,然后重新使用部件+底图来构建带有企业产品特征的边框。 然而去掉了Qt自带的边框后,随之也去掉了自带的窗口拉伸缩放功能,这就导致了在产品使用... -
qt 无边框窗口,可拖动,可拉伸
2019-08-30 10:47:13qt的无边框窗口可以很方便的设置样式,但是窗口设置成无边框后就无法拖动和拉伸了,之前参考过一些晚上的教程,大体方向是对的,但是细节上有很多问题,这里分享我写的无边框窗口。 新建一个QT工程,主界面是...