程序窗口的边框,标题栏等是系统管理的,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/

Qt 创建圆角、无边框、有阴影、可拖动的窗口 good的更多相关文章

  1. 【C#】使用DWM实现无边框窗体阴影或全透窗体

    1.无边框窗体阴影,win7(需要开启Aero效果)及以上系统 public class LdwmForm : Form { public LdwmForm() { Initialize(); } / ...

  2. 初学c# -- 学习笔记(五) winfrom无边框四周阴影

    刚用到这个功能,网上扯淡的东西太多了,都是2边阴影,还什么窗口叠加.ps作图啥的,什么玩意.还是老外实在,google找的,无边框窗体,四边透明阴影. public partial class For ...

  3. Qt中实现无边框的窗体

    1 自定义窗体类继承自QWidget 2 在构造函数中设置无边框效果 setWindowFlags(Qt::FramelessWindowHint);//无边框 setAttribute(Qt::WA ...

  4. Qt:移动无边框窗体(使用Windows的SendMessage)

    移动无边框窗体的代码网上很多,其原理都是一样的,但是是有问题的,我这里只是对其修正一下 网上的代码仅仅实现了两个事件 void EditDialog::mousePressEvent(QMouseEv ...

  5. WINFORM 无边框窗体 阴影与移动

    //窗体移动API[DllImport("user32.dll")]public static extern bool ReleaseCapture();[DllImport(&q ...

  6. C# WPF 建立无边框(标题栏)的登录窗口

    前言:笔者最近用c#写WPF做了一个项目,此前未曾做过完整的WPF项目,算是一边学一边用,网上搜了不少资料,效率当然是不敢恭维的,有时会在一些很简单的问题上纠结很长时间,血与泪的教训可不少. 不过,正 ...

  7. 无边框WPF窗体——允许拖动

    https://blog.csdn.net/zjcxhswill/article/details/38646525

  8. Qt无边框窗体-模拟模态窗体抖动效果

    目录 一.概述 二.效果展示 三.功能实现 四.相关文章 原文链接:Qt无边框窗体-模拟模态窗体抖动效果 一.概述 用Qt开发windows客户端界面确实是一大利器,兼顾性能的同时,速度相对来说也不错 ...

  9. 如何在pyqt中给无边框窗口添加DWM环绕阴影

    前言 在之前的博客<如何在pyqt中通过调用SetWindowCompositionAttribute实现Win10亚克力效果>中,我们实现了窗口的亚克力效果,同时也用SetWindowC ...

随机推荐

  1. Java 学习(22):Java MySQL 连接

    Java MySQL 连接 本章节我们为大家介绍 Java 如何使用 使用 JDBC 连接 MySQL 数据库. Java 连接 MySQL 需要驱动包,最新版下载地址为:http://dev.mys ...

  2. 《iOS Human Interface Guidelines》——Edit Menu

    编辑菜单 用户能够显示一个编辑菜单来在文本视图.网页视图和图像视图运行诸如剪切.粘贴和选择的操作. 你能够调整一些菜单的行为来在你的app中给用户给多的内容控制.比方你能够: 指定哪一个标准菜单命令对 ...

  3. js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g)

    js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g) 一.总结 1.w d s,word digital space 2.特 ...

  4. Java8学习之旅2---基于Lambda的JDBC编程

    Java8的Lambda表达式确实是一个很好的特性.可是在哪些场合下使用.事实上还是须要细致考虑的.我们当然不能为了使用而使用,而是须要找到切实实用的场合.在JDBC编程中,比如查询语句,首先须要进行 ...

  5. html常用样式margin、border怎么使用

    html常用样式margin.border怎么使用 一.总结 一句话总结:1.margin:auto配合width才能居中:2.border的三个属性依次是边框宽度,边框样式,边框颜色 1.html中 ...

  6. INT_MIN与溢出

    隔了好久没更新了,由于我在学习PL和编译器/解释器的知识. 挺好奇这方面的,由于没有学过相关的课程.所以学起来有点吃力,进展缓慢.所以导致没啥可写的. 今天看到这么一段话: 32位的int型的取值是2 ...

  7. 树莓派——root用户和sudo

    Linux操作系统是一个多用户操作系统,它同意多个用户登录和使用一台计算机. 为了保护计算机(和其它用户的隐私).用户都被限制了能做的事情. 大多数用户都同意执行计算机上大部分程序,而且编辑和保存存放 ...

  8. erlang app 文件

    http://hje.iteye.com/blog/1211734 应用的概念¶ 当我们写了实现特定功能的代码之后,我们可能想将代码转成一个 应用 (application),这是可以作为一个单元启动 ...

  9. 【t006】三角形分形描绘问题

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 分形是以多种概念和方法相互冲击融合为特征的图形.分形所呈现的无穷玄机和美感引发人们去探索.分形使人们觉悟 ...

  10. 怎样获取android手机联系人并按字母展示(三)

    假设获取contact的头像信息并展示: 怎样依据photoId来获取bitmap: public static Bitmap getContactPhoto(Context context, lon ...