本次分享,是基于Qt实现了无边框的窗口,并支持拖动缩放、最大化、最小化、关闭、双击全屏等。
实现无边框其实很简单,一行代码搞定。
setWindowFlag(Qt::FramelessWindowHint);
但是,隐藏了窗口的默认边框,标题栏没了,窗口无法进行拖动缩放了,最小化、最大化、关闭按钮也没有了,因此都需要自己实现。
为了实现无边框窗口到窗口背景的清晰过度,还需要给边框加border线或过度阴影。(本文不提供过度阴影,感兴趣的可以自己去研究,Qt有相关的类可实现)
如果将这个无边框的窗口应用到所有窗口,即如何将无边框窗口作为一个容器使用,也是本文需要解决的问题
首先给大家展示一下效果图。
Qt实战 无边框窗口的实现
无边框窗口类实现
SuperWindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SuperWindow</class>
<widget class="QWidget" name="SuperWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>SuperWindow</string>
</property>
<property name="styleSheet">
<string notr="true">QWidget#titleBar{
background-color: rgb(150, 150, 150);
}
QPushButton[type="btn_win_ctrl"]
{
max-width: 20px;
max-height: 20px;
border: 1px solid rgba(100, 100, 100, 50);
}
QPushButton[type="btn_win_ctrl"]:hover
{
border: 1px solid blue;
}
QPushButton[type="btn_win_ctrl"]:pressed
{
border: 2px solid blue;
}
QPushButton[type="btn_win_ctrl"]#btnMinimize
{
background: center url(:/icons/minium.png) no-repeat;
}
QPushButton[type="btn_win_ctrl"]#btnClose
{
background: center url(:/icons/close.png) no-repeat;
}
QLabel#lbWinIcon
{
max-width: 20px;
max-height: 20px;
min-width: 20px;
min-height: 20px;
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QWidget" name="titleBar" native="true">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="lbWinIcon">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbTitle">
<property name="text">
<string>标题</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnMinimize">
<property name="text">
<string/>
</property>
<property name="type" stdset="0">
<string>btn_win_ctrl</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnMaxmize">
<property name="text">
<string/>
</property>
<property name="type" stdset="0">
<string>btn_win_ctrl</string>
</property>
<property name="bMax" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClose">
<property name="text">
<string/>
</property>
<property name="type" stdset="0">
<string>btn_win_ctrl</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="wgtContent" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridContent"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
SuperWindow.h
#pragma once
#include "ui_SuperWindow.h"
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class SuperWindow; }
QT_END_NAMESPACE
class SuperWindow : public QWidget
{
Q_OBJECT
public:
SuperWindow(QWidget *parent = nullptr);
~SuperWindow();
template<typename T>
T *CentralWidget()
{
if (!m_centralWgt) return nullptr;
return qobject_cast<T*>(m_centralWgt);
}
void SetTitle(QString title);
template<typename T>
static SuperWindow* Create(QWidget* parent = nullptr)
{
SuperWindow* fw = new SuperWindow(parent);
T* centralWgt = new T(fw);
fw->SetCentralWidget(centralWgt);
return fw;
}
protected:
void mouseDoubleClickEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void paintEvent(QPaintEvent* e);
void leaveEvent(QEvent* e);
private:
void SetCentralWidget(QWidget *widget);
//计算九宫格行
int row(QPointF pos);
//计算九宫格列
int col(QPointF pos);
//点击区域 相对于九宫格
int moveArea(QPointF pos);
void setMouseStyle(int moveArea);
private slots:
void OnMaxmized();
private:
Ui::SuperWindow *ui;
QWidget* m_centralWgt{ nullptr };
bool m_bPressed{ false };
bool m_bResizing{false};
int m_flag;
QPoint curPos;
int m_nBorder{ 3 };
};
SuperWindow.cpp
#include "SuperWindow.h"
#include <QMouseEvent>
#include <QPainter>
SuperWindow::SuperWindow(QWidget* parent)
: QWidget(parent)
, ui(new Ui::SuperWindow)
{
ui->setupUi(this);
setWindowFlag(Qt::FramelessWindowHint);
setMouseTracking(true);
ui->btnMaxmize->setIcon(QIcon(":/icons/maxsize.png"));
for (auto obj : children()) {
auto wgt = qobject_cast<QWidget*>(obj);
if (wgt) wgt->setMouseTracking(true);
}
connect(ui->btnMinimize, &QPushButton::clicked, this, &SuperWindow::showMinimized);
connect(ui->btnMaxmize, &QPushButton::clicked, this, &SuperWindow::OnMaxmized);
connect(ui->btnClose, &QPushButton::clicked, this, &SuperWindow::close);
}
SuperWindow::~SuperWindow()
{
delete ui;
}
void SuperWindow::SetTitle(QString title)
{
ui->lbTitle->setText(title);
setWindowTitle(title);
}
void SuperWindow::SetCentralWidget(QWidget *widget)
{
if (m_centralWgt) return;
m_centralWgt = widget;
ui->gridContent->addWidget(widget);
}
void SuperWindow::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
if (ui->titleBar->geometry().contains(event->pos()))
{
OnMaxmized();
}
}
}
void SuperWindow::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton)
{
if (ui->titleBar->geometry().contains(event->pos()) &&
this->cursor() == Qt::ArrowCursor)
{
m_bPressed = true;
curPos = event->globalPosition().toPoint();
}
else if (m_flag != 22)
{
m_bResizing = true;
curPos = event->globalPosition().toPoint();
}
}
}
void SuperWindow::mouseMoveEvent(QMouseEvent* event)
{
if (m_bPressed) //如果鼠标左键按下
{
QPoint tmpPos = event->globalPosition().toPoint();
QPoint delta = tmpPos - curPos;
move(pos() + delta);
curPos = tmpPos;
}
else
{
if (!m_bResizing)
{
m_flag = moveArea(event->pos());
setMouseStyle(m_flag);
return;
}
qDebug() << "resizing " << event->pos();
QPoint tmpPos = event->globalPosition().toPoint();
QPoint delta = tmpPos - curPos;
curPos = tmpPos;
//记录窗体当前位置
QRect rect = geometry();
//鼠标左键处于拖拽拉伸区域
//m_flag为鼠标点击左键时 鼠标样式状态
switch (m_flag)
{
case 11: rect.setTopLeft(rect.topLeft() + delta); break; //左上角
case 13: rect.setTopRight(rect.topRight() + delta); break; //右上角
case 31: rect.setBottomLeft(rect.bottomLeft() + delta); break; //左下角
case 33: rect.setBottomRight(rect.bottomRight() + delta); break; //右下角
case 12: rect.setTop(rect.top() + delta.y()); break; //上
case 21: rect.setLeft(rect.left() + delta.x()); break; //左
case 23: rect.setRight(rect.right() + delta.x()); break; //右
case 32: rect.setBottom(rect.bottom() + delta.y()); break; //下
default: break;
}
this->setGeometry(rect);
}
}
void SuperWindow::mouseReleaseEvent(QMouseEvent* event)
{
Q_UNUSED(event);
if (event->button() == Qt::LeftButton)
{
if (m_bPressed) m_bPressed = false;
if (m_bResizing)
{
m_bResizing = false;
m_flag = 22;
update();
}
}
}
void SuperWindow::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
{
painter.save();
QRect rect(m_nBorder/2, m_nBorder/2, width()-m_nBorder/2, height()-m_nBorder/2);
QPen pen;
pen.setColor(Qt::lightGray);
pen.setWidth(m_nBorder);
painter.setPen(pen);
painter.drawRect(rect);
painter.restore();
}
if (m_flag == 22) return;
else
{
QBrush brush;
brush.setColor(QColor(73, 95, 22, 150));//设置颜色
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
switch (m_flag)
{
case 12:
{
QRect rect(0, 0, width(), m_nBorder);
painter.drawRect(rect);
break;
}
case 21:
{
QRect rect(0, 0, m_nBorder, height());
painter.drawRect(rect);
break;
}
case 23:
{
QRect rect(width()-m_nBorder, 0, m_nBorder, height());
painter.drawRect(rect);
break;
}
case 32:
{
QRect rect(0, height()-m_nBorder, width(), m_nBorder);
painter.drawRect(rect);
break;
}
case 11:
{
QRect rect(0, 0, width(), m_nBorder);
painter.drawRect(rect);
rect = QRect(0, 0, m_nBorder, height());
painter.drawRect(rect);
break;
}
case 13:
{
QRect rect(0, 0, width(), m_nBorder);
painter.drawRect(rect);
rect = QRect(width()-m_nBorder, 0, m_nBorder, height());
painter.drawRect(rect);
break;
}
case 31:
{
QRect rect(0, 0, m_nBorder, height());
painter.drawRect(rect);
rect = QRect(0, height()-m_nBorder, width(), m_nBorder);
painter.drawRect(rect);
break;
}
case 33:
{
QRect rect(0, height()-m_nBorder, width(), m_nBorder);
painter.drawRect(rect);
rect = QRect(width()-m_nBorder, 0, m_nBorder, height());
painter.drawRect(rect);
break;
}
default:
break;
}
}
}
void SuperWindow::leaveEvent(QEvent *e)
{
Q_UNUSED(e);
m_flag = 22;
}
int SuperWindow::row(QPointF pos)
{
if (pos.y() < m_nBorder)
return 10;
else if (pos.y() > height() - m_nBorder)
return 30;
else
return 20;
}
int SuperWindow::col(QPointF pos)
{
if (pos.x() < m_nBorder)
return 1;
else if (pos.x() > width() - m_nBorder)
return 3;
else
return 2;
}
int SuperWindow::moveArea(QPointF pos)
{
return row(pos) + col(pos);
}
void SuperWindow::setMouseStyle(int moveArea)
{
switch (moveArea)
{
case 11: setCursor(Qt::SizeFDiagCursor); break;
case 12: setCursor(Qt::SizeVerCursor); break;
case 13: setCursor(Qt::SizeBDiagCursor); break;
case 21: setCursor(Qt::SizeHorCursor); break;
case 22:
{
if (!m_bPressed && !m_bResizing)
setCursor(Qt::ArrowCursor);
break;
}
case 23: setCursor(Qt::SizeHorCursor); break;
case 31: setCursor(Qt::SizeBDiagCursor); break;
case 32: setCursor(Qt::SizeVerCursor); break;
case 33: setCursor(Qt::SizeFDiagCursor); break;
default: setCursor(Qt::ArrowCursor); break;
}
}
void SuperWindow::OnMaxmized()
{
if (this->isMaximized())
{
this->showNormal();
ui->btnMaxmize->setIcon(QIcon(":/icons/maxsize.png"));
}
else
{
this->showMaximized();
ui->btnMaxmize->setIcon(QIcon(":/icons/normal.png"));
}
}
main.cpp
#include "MainWin.h"
#include "SuperWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
SuperWindow* win = SuperWindow::Create<MainWin>(nullptr);
win->show();
MainWin* mw = win->CentralWidget<MainWin>();
if (!mw) qDebug() << "not nullptr";
else mw->setVisible(false);
return a.exec();
}
对于MainWin的实现,这里就不在放代码了,只要是集成QWidget的窗口即可。
如果我的分享,帮助了您,不要忘记点赞、评论和收藏。