重构了代码,将所有功能全部封装在容器类中,使用接口函数初始化标题栏。

  • 使用示例
1
2
3
4
wrapper = new WrapperWidget();

/*接口函数,初始化标题栏*/
wrapper->iniTitleBar(this);

熟悉win32编程的都知道,在注册窗口类时windows系统会提供一个标题栏,在不同的主题下风格都不一样。但是不能修改样式和添加功能。

自定义标题栏只有去掉系统标题栏,自己实现,尽量除了样式之外其他功能都要实现。


步骤

  • 定义两个类,一个用作标题栏,一个用作容器,使用QT的grid栅格布局。将主窗口和标题栏添加到容器类的layout中。

  • 重写鼠标事件,实现鼠标拖动窗口,双击最大化,关闭等。

  • 重写绘图事件,定制背景样式。

  • 容器类通过事件过滤捕获标题栏的事件。



遇到的问题

因为主程序和标题栏都是容器类的子对象,在关闭程序时直接调用容器类的close()函数,不会发生内存泄露。

这个容器类是必须的,如果直接拿主窗口当容器,那么代码都要写在另一个子类里

标题栏的布局,设置标题栏的SizePolicy固定高度。中间的空隙用QSS的border属性清除。

QT自动释放内存机制

引用自知乎

main函数里面main widget/dialog/mainWindow是分配在栈上的,其他new出来的东西都以这个widget作为父亲。 当程序最后结束了,main widget弹栈。父类被销毁。子类跟着被销毁。 所以如果你自己new 出来的。没有父类,不删除就会造成内存泄漏。


源码

main.cpp

1
2
3
4
5
6
7
8
9
10
#include "DesktopTools.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[]){
QApplication a(argc, argv);
DesktopTools w;
w.show();
return a.exec();
}


DesktopTools.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_DesktopTools.h"
#include <QStyle>
#include "CustomBar.h"
#include <QGridLayout>
#include <QPainter>
#include <QPixmap>
#include <QPaintEvent>
#include <QEvent>
#include <QPoint>

class DesktopTools : public QMainWindow{
Q_OBJECT

public:
DesktopTools(QWidget *parent = Q_NULLPTR);
~DesktopTools();

private:
Ui::DesktopToolsClass ui;

WrapperWidget *wrapper;

void iniUi();
void iniConnect();


protected:
/*重写绘图事件*/
void paintEvent(QPaintEvent *event)override;

};



DesktopTools.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "DesktopTools.h"

DesktopTools::DesktopTools(QWidget *parent)
: QMainWindow(parent) {
ui.setupUi(this);
iniUi();
iniConnect();
}

/*析构函数*/
DesktopTools::~DesktopTools() {
//
}

void DesktopTools::iniUi() {

setWindowFlags(Qt::FramelessWindowHint);
//setAttribute(Qt::WA_StyledBackground);
setStyleSheet(QString("\
#DesktopToolsClass{\
border:none;}"));

wrapper = new WrapperWidget();

/*接口函数,初始化标题栏*/
wrapper->iniTitleBar(this);


}

void DesktopTools::iniConnect() {
//
}


void DesktopTools::paintEvent(QPaintEvent *event) {
QPainter mPainter(this);
mPainter.drawPixmap(QRect(0, 0, this->width(), this->height()), QPixmap(":/DesktopTools/Resources/1FR3114501-1.jpg"));
}



CustomBar.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#pragma once
#include <QtWidgets/QWidget>
#include "ui_CustomBar.h"
#include <QString>
#include <QPaintEvent>
#include <QPainter>
#include <QHBoxlayout>
#include <QEvent>
#include <QGridLayout>

/*标题栏*/
class CustomBar :
public QWidget{
Q_OBJECT
public:
CustomBar(QWidget *parent = nullptr);
private:
Ui::CustomBarClass ui;
void iniUi();
void iniConnect();

QHBoxLayout *hLayout;
private slots:
void onBtnCloseClicked();

signals:
void btnCloseClicked();
void doubleClicked();

protected:
void paintEvent(QPaintEvent *event)override;

};



/*容器类*/
class WrapperWidget :
public QWidget {
Q_OBJECT
public:

explicit WrapperWidget(QWidget *parent = nullptr);
~WrapperWidget();

/*接口函数*/
void iniTitleBar(QWidget *widget);

private:
CustomBar *customBar;
QGridLayout *tLayout;
QPoint *currentWindowPos;

void iniUi();
void iniConnect();

private slots:
/*CustomBar的关闭按钮事件*/
void onBtnCloseClicked();

protected:
/*监听CustomBar的事件*/
bool eventFilter(QObject *obj, QEvent *event)override;


};


CustomBar.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include "CustomBar.h"



CustomBar::CustomBar(QWidget *parent) :QWidget(parent) {
ui.setupUi(this);
iniUi();
iniConnect();
}

void CustomBar::iniUi() {
setWindowFlag(Qt::FramelessWindowHint);
setWindowOpacity(0.5);
setStyleSheet(QString("#CustomBarClass{\
background-color:rgba(0,0,0,0.5);\
border:none;}\
QPushButton{\
border:none;\
background-color:pink;\
}\
QPushButton::hover{\
background-color:red;}\
#mTitleLabel{color:white;}\
"));
ui.btnClose->setFixedSize(32, 32);
ui.mTitleLabel->setText(QString(QStringLiteral("自定义标题栏示例 ...btkrc")));

setAttribute(Qt::WA_StyledBackground);

this->setMinimumHeight(40);

hLayout = new QHBoxLayout(this);

hLayout->addWidget(ui.mTitleLabel);
hLayout->addWidget(ui.btnClose);

}

void CustomBar::iniConnect() {
QObject::connect(ui.btnClose, SIGNAL(clicked()), this, SLOT(onBtnCloseClicked()));
}

void CustomBar::paintEvent(QPaintEvent *event) {
return QWidget::paintEvent(event);
}


/*slots*/

/*发射信号,在主类中处理。*/
void CustomBar::onBtnCloseClicked() {
emit btnCloseClicked();
}

/*slots end*/


/*容器类*/
WrapperWidget::WrapperWidget(QWidget *parent) :QWidget(parent) {
iniUi();
iniConnect();
}

WrapperWidget::~WrapperWidget(){
delete currentWindowPos;
}

/*接口,将窗口添加到容器中*/
void WrapperWidget::iniTitleBar(QWidget *widget) {
tLayout->addWidget(widget, 1, 0, 1, 1);
/*将主窗口添加到容器中*/
widget->setParent(this);

this->show();
}


void WrapperWidget::onBtnCloseClicked() {
this->close();
}

/*初始化UI*/
void WrapperWidget::iniUi(){
currentWindowPos = new QPoint();




setWindowFlags(Qt::FramelessWindowHint);

/*initialize size*/
resize(600, 400);
setAttribute(Qt::WA_TranslucentBackground);


customBar = new CustomBar(this);
customBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

/*注册CustomBar的事件监听器到此容器*/
customBar->installEventFilter(this);

customBar->setParent(this);


/*栅格布局,将标题和主窗口添加到容器中*/
tLayout = new QGridLayout(this);
tLayout->setSpacing(0);
tLayout->setContentsMargins(0, 0, 0, 0);
tLayout->addWidget(customBar, 0, 0, 1, 1);
}

void WrapperWidget::iniConnect(){
QObject::connect(customBar, SIGNAL(btnCloseClicked()), this, SLOT(onBtnCloseClicked()));
}

bool WrapperWidget::eventFilter(QObject *obj, QEvent *event) {
if (obj == customBar) {
switch (event->type()) {
/*标题栏鼠标双击*/
case QEvent::MouseButtonDblClick: {

QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
/*鼠标左键双击全屏显示*/
if (mouseEvent->buttons() & Qt::LeftButton) {
if (Qt::WindowMaximized == this->windowState()) {
this->showNormal();
}
else {
this->showMaximized();
}
}
return true;
}
case QEvent::MouseButtonPress: {
/*强制转换类型*/
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
/*保存当前窗口相对坐标*/
*currentWindowPos = mouseEvent->pos();
return true;
}
case QEvent::MouseMove: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->buttons() & Qt::LeftButton) {
this->move(mouseEvent->globalPos() - *currentWindowPos);
return true;
}
else
return false;
}
default: {
return false;
}
}
}
else {//将未处理的事件传给基类
return QWidget::eventFilter(obj, event);
}
}