赞
踩
参考文章:Qt实战6.万能的无边框窗口(FramelessWindow)
首先创建了一个简单的Qt工程,这里我使用QWidget
作为窗体基类。
#include "QApplication"
#include "frameless/frameless.hxx"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
frameless framelessWindow;
framelessWindow.show();
return QApplication::exec();
}
main.cxx
// // 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
./frameless/frameless.hxx
//
// Created by Marcus on 2023/8/14.
//
#include "frameless.hxx"
frameless::frameless(QWidget *parent) {
}
frameless::~frameless() {
}
./frameless/frameless.cxx
注意:以下代码包含三个window flag,请选择自己需要的window flag
使用。
frameless::frameless(QWidget *parent) {
setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow | Qt::WindowStaysOnTopHint);
}
下面是关于这些window flag
的说明:
window flag
会开启无边框窗体window flag
window flag
注意:演示各个window flag
时仅设置单个window flag
,实际使用时可以设置多个window flag
,使用|
隔开
setWindowFlags(Qt::FramelessWindowHint);
setWindowFlags(Qt::SubWindow);
需要注意的是,设置以后关闭/最大化/最小化按钮也不见了,此时窗口也无法拖拽缩放
setWindowFlags(Qt::WindowStaysOnTopHint);
下面我将加入一个Material Design 2风格的标题栏,由于创建工程时没有创建.ui
文件,所以我修改了frameless.cxx
,若你有一个.ui
文件,可以在QDesginer下修改窗体中的控件。
下面是会用到的头文件:
#include "QWidget"
#include "QVBoxLayout"
#include "QHBoxLayout"
#include "QSpacerItem"
#include "QPushButton"
#include "QLabel"
./frameless/frameless.hxx
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; }" ); }
./frameless/frameless.cxx
setWindowTitle的好处
最后我们得到了一个自定义的窗体:
现在自定义的标题栏已经好了,我们需要实现标题所述功能。
首先新建一些槽备用:
class frameless : public QWidget { Q_OBJECT public: explicit frameless(QWidget *parent = nullptr); ~frameless() override; public slots: void trigMinimize(); void trigMaximize(); void trigExit(); };
./frameless/frameless.hxx
void frameless::trigMinimize() {
this->showMinimized();
}
连接槽:
auto * minimizeApp = new QPushButton; //最小化按钮
minimizeApp->setObjectName("minimizeApp");
connect(minimizeApp, SIGNAL(clicked(bool)), this, SLOT(trigMinimize()));
titleBarContainer->addWidget(minimizeApp);
首先先新建一个bool
变量,方便后续判断程序是否已经最大化,并将maximizeApp
按钮的声明移至头文件中(如果使用Qt Desginer不用移动声明):
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);
需要在头文件中包含QApplication
。
void frameless::trigExit() {
QApplication::quit();
}
auto * closeApp = new QPushButton; //退出按钮
closeApp->setObjectName("closeApp");
connect(closeApp, SIGNAL(clicked(bool)), this, SLOT(trigExit()));
titleBarContainer->addWidget(closeApp);
在头文件中加入MouseEvent
:
#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; //禁止窗体移动 }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。