参考文章:Qt实战6.万能的无边框窗口(FramelessWindow)
准备工作
首先创建了一个简单的Qt工程,这里我使用
#include "QApplication" #include "frameless/frameless.hxx" int main(int argc, char *argv[]) { QApplication a(argc, argv); frameless framelessWindow; framelessWindow.show(); return QApplication::exec(); }
// // Created by Marcus on 2023/8/14. // #include "QWidget" #ifndef FRAMELESSWINDOWTEST_FRAMELESS_HXX #define FRAMELESSWINDOWTEST_FRAMELESS_HXX class frameless : public QWidget { Q_OBJECT public: explicit frameless(QWidget *parent = nullptr); ~frameless() override; }; #endif //FRAMELESSWINDOWTEST_FRAMELESS_HXX
// // Created by Marcus on 2023/8/14. // #include "frameless.hxx" frameless::frameless(QWidget *parent) { } frameless::~frameless() { }
开启无边框窗口
注意:以下代码包含三个window flag,请选择自己需要的
frameless::frameless(QWidget *parent) { setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow | Qt::WindowStaysOnTopHint); }
下面是关于这些
- Qt::FramelessWindowHint:这个是必须要设置的,此
window flag 会开启无边框窗体 - Qt::SubWindow:如果需要运行程序时在任务栏上不显示程序图标(举一个例子,假设需要开发一款桌宠,在软件运行时最好不要在任务栏上显示程序的图标)时需要此
window flag - Qt::WindowStaysOnTopHint:需要程序始终在屏幕顶部时,请设置此
window flag
Window Flag效果
注意:演示各个
Qt::FramelessWindowHint
setWindowFlags(Qt::FramelessWindowHint);
Qt::SubWindow
setWindowFlags(Qt::SubWindow);
需要注意的是,设置以后关闭/最大化/最小化按钮也不见了,此时窗口也无法拖拽缩放
Qt::WindowStaysOnTopHint
setWindowFlags(Qt::WindowStaysOnTopHint);
自定义标题栏
下面我将加入一个Material Design 2风格的标题栏,由于创建工程时没有创建
下面是会用到的头文件:
#include "QWidget" #include "QVBoxLayout" #include "QHBoxLayout" #include "QSpacerItem" #include "QPushButton" #include "QLabel"
frameless::frameless(QWidget *parent) { //设置这个窗体标题不会在标题栏上显示,但是设置的标题会在其他地方显示,拿Windows系统举例,你可以在启用Aero Peek以后在任务栏找到程序图标,鼠标停留几秒就能看到设置的标题 setWindowTitle("无边框窗体测试"); //设置窗体标题 setWindowFlags(Qt::FramelessWindowHint); //设置无边框 this->resize(800, 600); //将窗口大小缩放为800像素×600像素 auto * appLayout = new QVBoxLayout; //新增一个局部appLayout并且设置其边距为0 appLayout->setContentsMargins(0, 0, 0, 0); appLayout->setSpacing(0); auto * titleBar = new QWidget; //这个QWidget将作为标题栏的容器 titleBar->setObjectName("titleBar"); //设置object name,方便后期使用QSS进行美化 auto * titleBarContainer = new QHBoxLayout; //新建一个局部titleBarContainer,它将成为标题栏的局部,这里我们设置其左右边距为16像素,上下边距为0,局部中每个控件间距离为5像素 titleBarContainer->setContentsMargins(16, 0, 16, 0); titleBarContainer->setSpacing(5); //(下面那两行太长了旁边我觉得写不下就写这了)下面这个QSpacerItem是标题栏左侧的空白区域 auto * titleBarSpacer = new QSpacerItem(24, 24, QSizePolicy::Expanding, QSizePolicy::Expanding); titleBarContainer->addSpacerItem(titleBarSpacer); auto * minimizeApp = new QPushButton; //最小化按钮 minimizeApp->setObjectName("minimizeApp"); titleBarContainer->addWidget(minimizeApp); auto * maximizeApp = new QPushButton; //最大化按钮 maximizeApp->setObjectName("maximizeApp"); titleBarContainer->addWidget(maximizeApp); auto * closeApp = new QPushButton; //退出按钮 closeApp->setObjectName("closeApp"); titleBarContainer->addWidget(closeApp); titleBar->setLayout(titleBarContainer); //将之前新建的局部titleBarContainer设置为标题栏的局部 appLayout->addWidget(titleBar); //将标题栏加入局部appLayout //下面不再赘述新建控件的过程 auto * appBar = new QWidget; appBar->setObjectName("appBar"); auto * appBarLayout = new QHBoxLayout; auto * appTitle = new QLabel; appTitle->setObjectName("appTitle"); appTitle->setText("无边框窗体测试"); appBarLayout->addWidget(appTitle); appBar->setLayout(appBarLayout); appLayout->addWidget(appBar); auto * appContent = new QWidget; appContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); auto * appContentLayout = new QVBoxLayout; auto * helloWorld = new QLabel; helloWorld->setText("Hello World!"); helloWorld->setObjectName("helloWorld"); appContentLayout->addWidget(helloWorld); auto * appContentSpacer = new QSpacerItem(16, 16, QSizePolicy::Expanding, QSizePolicy::Expanding); appContentLayout->addSpacerItem(appContentSpacer); appContent->setLayout(appContentLayout); appLayout->addWidget(appContent); this->setLayout(appLayout); //设置QWidget的局部为appLayout //设置窗体样式 this->setStyleSheet("#titleBar{ background: #1976D2; min-height: 32px; max-height: 32px; }" "#minimizeApp{ background: transparent; border-image: url(:/icon/icon/trigMinimize.svg); height: 24px; width: 24px; }" "#maximizeApp{ background: transparent; border-image: url(:/icon/icon/trigMaximize.svg); height: 24px; width: 24px; }" "#closeApp{ background: transparent; border-image: url(:/icon/icon/closeApp.svg); height: 24px; width: 24px; }" "#appBar{ background: #2196F3; min-height: 64px; max-height: 64px; }" "#appTitle{ background: transparent; color: #fff; font-size: 24px; font-weight: bold; font-family: 思源黑体 CN; }" "#helloWorld{ background: transparent; color: #000000; font-size: 16px; font-weight: normal; font-family: 思源黑体 CN; }" ); }
使用
最后我们得到了一个自定义的窗体:
最小化、最大化和退出
现在自定义的标题栏已经好了,我们需要实现标题所述功能。
首先新建一些槽备用:
class frameless : public QWidget { Q_OBJECT public: explicit frameless(QWidget *parent = nullptr); ~frameless() override; public slots: void trigMinimize(); void trigMaximize(); void trigExit(); };
最小化
void frameless::trigMinimize() { this->showMinimized(); }
连接槽:
auto * minimizeApp = new QPushButton; //最小化按钮 minimizeApp->setObjectName("minimizeApp"); connect(minimizeApp, SIGNAL(clicked(bool)), this, SLOT(trigMinimize())); titleBarContainer->addWidget(minimizeApp);
最大化
首先先新建一个
class frameless : public QWidget { Q_OBJECT public: explicit frameless(QWidget *parent = nullptr); ~frameless() override; public slots: void trigMinimize(); void trigMaximize(); void trigExit(); private: bool isAppMaximized = false; QPushButton * maximizeApp = new QPushButton; };
void frameless::trigMaximize() { if(isAppMaximized == false){ this->showMaximized(); //最大化以后给最大化按钮换个图标 maximizeApp->setStyleSheet("#maximizeApp{ background: transparent; border-image: url(:/icon/icon/trigNormal.svg); height: 24px; width: 24px; }"); isAppMaximized = true; } //窗口不是最大化时,最大化窗口 else{ this->showNormal(); maximizeApp->setStyleSheet("#maximizeApp{ background: transparent; border-image: url(:/icon/icon/trigMaximize.svg); height: 24px; width: 24px; }"); isAppMaximized = false; } //窗口最大化时,将窗口恢复正常大小 }
maximizeApp->setObjectName("maximizeApp"); connect(maximizeApp, SIGNAL(clicked(bool)), this, SLOT(trigMaximize())); titleBarContainer->addWidget(maximizeApp);
退出
需要在头文件中包含
void frameless::trigExit() { QApplication::quit(); }
auto * closeApp = new QPushButton; //退出按钮 closeApp->setObjectName("closeApp"); connect(closeApp, SIGNAL(clicked(bool)), this, SLOT(trigExit())); titleBarContainer->addWidget(closeApp);
双击最大化
在头文件中加入
#include "QMouseEvent" ... public slots: void trigMinimize(); void trigMaximize(); void trigExit(); void mouseDoubleClickEvent(QMouseEvent * e);
void frameless::mouseDoubleClickEvent(QMouseEvent * e) { if(e->y() <= 32){ trigMaximize(); } //e的y值就是鼠标点击相对于窗体的y坐标,32为标题栏高度,这个值小于等于32像素即为在标题栏上双击,在其他地方双击鼠标不起作用 }
拖拽事件
在头文件中加入:
public slots: void trigMinimize(); void trigMaximize(); void trigExit(); void mouseDoubleClickEvent(QMouseEvent * e); void mousePressEvent(QMouseEvent * e); void mouseMoveEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent * e); private: bool isAppMaximized = false; bool isMoveAllowed = false; QPoint originalPos; QPushButton * maximizeApp = new QPushButton;
void frameless::mousePressEvent(QMouseEvent * e) { if(e->y() <= 32){ isMoveAllowed = true; originalPos = e->pos(); } //在标题栏上按下鼠标,准备拖拽时执行;isMoveAllowed为真时允许移动窗体,originalPos记录了此时鼠标相对桌面的坐标 } void frameless::mouseMoveEvent(QMouseEvent * e) { if(isMoveAllowed == true){ if(isAppMaximized == true){ trigMaximize(); this->move(e->globalPos()); } //如果程序被最大化,先恢复正常大小的窗口,然后再继续 this->move(e->globalPos() - originalPos); //e->globalPos()为鼠标相对于窗口的位置 } } void frameless::mouseReleaseEvent(QMouseEvent * e) { isMoveAllowed = false; //禁止窗体移动 }