Qt 創建圓角、無邊框、有陰影、可拖動的窗口 good


 

程序窗口的邊框,標題欄等是系統管理的,Qt 不能對其進行定制,為了實現定制的邊框、標題欄、關閉按鈕等,需要把系統默認的邊框、標題欄去掉,然后使用 Widget 來模擬它們。這里介紹使用 QSS + QGraphicsDropShadowEffect 來創建圓角、無邊框、有陰影、可拖動的窗口。

核心技術要點:

  • 啟用 QSS: setAttribute(Qt::WA_StyledBackground, true)

    我們繼承 QWidget 實現的 Widget 默認是不啟用 QSS 的,為了啟用 QSS,需要調用 setAttribute(Qt::WA_StyledBackground, true)

  • 使用 border-radius 創建圓角效果

    頂級窗口有些 QSS 不生效,例如 border-radius,所以把要顯示圓角的 Widget 上放在另一個頂級 Widget 中,變為非頂級窗口

  • 頂級窗口需要去掉邊框,背景設置為透明
    • 去掉邊框: setWindowFlags(Qt::FramelessWindowHint);
    • 背景透明: setAttribute(Qt::WA_TranslucentBackground);
  • 使用鼠標事件實現拖動
  • 使用 QGraphicsDropShadowEffect 創建陰影

    很遺憾,QSS 不支持陰影

使用方法:

  • FramelessWindow *window = new FramelessWindow(yourWidget) 即可

效果如圖:

main.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
#include "FramelessWindow.h"
 
#include <QDebug>
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
 
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
 
// 創建包含主要控件的 Widget
QPushButton *quitButton = new QPushButton("退出");
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget( new QLabel("按住我拖動也可以拖動窗口的哦"));
layout->addWidget( new QTextEdit());
layout->addWidget(quitButton);
 
QWidget *contentWidget = new QWidget();
contentWidget->setLayout(layout);
contentWidget->setObjectName( "contentWidget");
contentWidget->setStyleSheet( "#contentWidget{background: lightgray; border-radius: 4px;}" // 定制圓角
".QLabel{background: gray;}.QTextEdit{background: white;}");
 
QObject::connect(quitButton, &QPushButton::clicked, [&app] {
app.quit();
});
 
// 創建無邊框、有陰影、可拖動的窗口
FramelessWindow *window = new FramelessWindow(contentWidget);
window->resize( 300, 400);
window->show();
 
return app.exec();
}

FramelessWindow.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef FRAMELESSWINDOW_H
#define FRAMELESSWINDOW_H
 
#include <QWidget>
 
struct FramelessWindowPrivate;
 
class FramelessWindow : public QWidget {
Q_OBJECT
public:
explicit FramelessWindow(QWidget *contentWidget, QWidget *parent = 0);
~FramelessWindow();
 
protected:
void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
 
private:
FramelessWindowPrivate *d;
};
 
#endif // FRAMELESSWINDOW_H

FramelessWindow.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
#include "FramelessWindow.h"
 
#include <QMouseEvent>
#include <QGridLayout>
#include <QGraphicsDropShadowEffect>
 
struct FramelessWindowPrivate {
FramelessWindowPrivate(QWidget *contentWidget) : contentWidget(contentWidget) {}
 
QWidget *contentWidget;
QPoint mousePressedPosition; // 鼠標按下時的坐標
QPoint windowPositionAsDrag; // 鼠標按小時窗口左上角的坐標
};
 
FramelessWindow::FramelessWindow(QWidget *contentWidget, QWidget *parent) : QWidget(parent) {
setWindowFlags(Qt::FramelessWindowHint); // 去掉邊框
setAttribute(Qt::WA_TranslucentBackground); // 背景透明
 
d = new FramelessWindowPrivate(contentWidget);
 
// 添加陰影
QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(contentWidget);
shadowEffect->setColor(Qt::lightGray);
shadowEffect->setBlurRadius( 4); // 陰影的大小
shadowEffect->setOffset( 0, 0);
contentWidget->setGraphicsEffect(shadowEffect);
 
// 添加到窗口中
QGridLayout *lo = new QGridLayout();
lo->addWidget(contentWidget, 0, 0);
lo->setContentsMargins( 4, 4, 4, 4); // 注意和陰影大小的協調
setLayout(lo);
}
 
FramelessWindow::~FramelessWindow() {
delete d;
}
 
void FramelessWindow::mousePressEvent(QMouseEvent *e) {
// 記錄鼠標按下時全局的位置和窗口左上角的位置
d->mousePressedPosition = e->globalPos();
d->windowPositionAsDrag = pos();
}
 
void FramelessWindow::mouseReleaseEvent(QMouseEvent *e) {
Q_UNUSED(e)
// 鼠標放開始設置鼠標按下的位置為 null,表示鼠標沒有被按下
d->mousePressedPosition = QPoint();
}
 
void FramelessWindow::mouseMoveEvent(QMouseEvent *e) {
if (!d->mousePressedPosition.isNull()) {
// 鼠標按下並且移動時,移動窗口, 相對於鼠標按下時的位置計算,是為了防止誤差累積
QPoint delta = e->globalPos() - d->mousePressedPosition;
move(d->windowPositionAsDrag + delta);
}
}

思考

還可以使用其他方式實現上面的功能,並且功能也不夠豐富,思考下面的問題:

    • 使用其他方式實現圓角、陰影,例如:
      • 繪圖
        • 繪制圓角矩形並且實現陰影的算法
        • 使用一個圓角帶陰影圖片,利用九宮格技術繪制(border-image 的原理)
      • QSS 的 border-image
    • 拖動調整無邊框窗口的大小
    • 添加標題欄
    • 添加最小化、最大化、關閉按鈕

 

http://www.qtdebug.com/qt-frameless-window/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM