程序窗口的边框,标题栏等是系统管理的,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. printk()函数的总结

    我们在使用printk()函数中使用日志级别为的是使编程人员在编程过程中自定义地进行信息的输出,更加容易地掌握系统当前的状况.对程序的调试起到了很重要的作用.(下文中的日志级别和控制台日志控制级别是一 ...

  2. Android OkHttp网络连接封装工具类

    package com.lidong.demo.utils; import android.os.Handler; import android.os.Looper; import com.googl ...

  3. Seagate-保修验证(za25shrx)

    保修验证 http://support.seagate.com/customer/zh-CN/warranty_validation.jsp   Seagate   保修验证    End User  ...

  4. 【codeforces 742A】Arpa’s hard exam and Mehrdad’s naive cheat

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  5. js进阶正则表达式9量词2(^和&作用:/^HTML5$/g匹配不到aHTML5b中的HTML5,不然是可以匹配到的)(/\d+(?=cm)/g)((?!cm))

    js进阶正则表达式9量词2(^和&作用:/^HTML5$/g匹配不到aHTML5b中的HTML5,不然是可以匹配到的)(/\d+(?=cm)/g)((?!cm)) 一.总结 ^和&作用 ...

  6. 【u012】数字游戏

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 小W发明了一个游戏,他在黑板上写出了一行数字a1,a2,-an,然后给你m个回合的机会,每回合你可以从 ...

  7. [tmux] Enable mouse mode in tmux

    We'll learn how to use mouse mode in tmux, including enable mouse control for resizing, scrolling an ...

  8. 数据序列化之protobuf

    数据序列化之protobuf 很多时候需要将一些数据打包,就是把这些数据搞在一起,方便处理.最常见的情况就是把需要传输的数据,当然数据不止一条,打包成一个消息,然后发送出去,接收端再以一定的规则接收并 ...

  9. 【a601】雇佣计划

    Time Limit: 1 second Memory Limit: 32 MB [问题描述] 一位管理项目的经理想要确定每个月需要的工人,他知道每月所需的最少工人数.当他雇佣或解雇一个工人时,会有一 ...

  10. ionic新手教程第三课-在项目中使用requirejs分离controller文件和server文件

    继上篇教程中提到的,我们新建一个简单的tabs类型的Ionic项目. 依据文件夹文件我们知道,系统自己主动创建了一个controller文件和server文件,而且把全部的控制器和服务都写到这两个文件 ...