實現qt 窗口無邊框拖拽
無邊框拖拽是參考Qt實戰6.萬能的無邊框窗口(FramelessWindow) - Qt小羅 - 博客園的文章,對其代碼進行修改而來。
使用的是qt6 所以有可能里面一些關于坐標的類需要修改一下類型
代碼使用的話,我是直接讓widget繼承于framlessWidget,下圖是效果圖

相比較,我將m_movePoint變成是m_pressPoint距離鼠標的相對坐標;然后讓m_bIsResizing的值由m_direction來判斷是否要拉伸窗口,同時添加了一個透明的帶邊框的窗體border來實現預覽移動而拉伸的狀態,因為我將qt小羅的及時修改邊框的位置和大小改成延時,所以需要有個能預覽的邊框來觀看。
只要由鼠標按下和松開來調用其他函數,例如鼠標按下要對一些變量進行重新設置避免上次操作的影響、判斷是否要拉伸窗口和讓border綁定父窗口顯示出border。其他函數需要自己查看
對于移動窗口的話,需要對派生類進行多幾步的操作
列子:topWidget和mainWidgetLeft都是頁面的邊緣是我想在這些地方點擊后能觸發移動條件
void MainWidget::mousePressEvent(QMouseEvent* event)
{
int topWidgetHeight = this->topWidget->height();
int leftWidgetWidth = this->mainWidgetLeft->width();
int x = event->x();
int y = event->y();
// 以上獲取的數據是分別是符合條件的區域和鼠標的相對坐標
// 判斷是否符合窗口移動條件
if (x <= leftWidgetWidth || y <= topWidgetHeight) {
this->setMoveStatus(true);//true表示能夠移動
}
FramelessWidget::mousePressEvent(event);
}
//framelessWidget.h
#pragma once
#include <QWidget>
class TransparentBorder;
class FramelessWidget : public QWidget
{
Q_OBJECT
public:
enum Direction {//鼠標處于哪個邊界
BOTTOMRIGHT,
TOPRIGHT,
TOPLEFT,
BOTTOMLEFT,
RIGHT,
DOWN,
LEFT,
UP,
NONE
};
enum {//距離邊界多少時改變鼠標樣式
MARGIN_MIN_SIZE = 0,
MARGIN_MAX_SIZE = 4
};
public:
FramelessWidget(QWidget* parent = nullptr);
~FramelessWidget();
void setBorderColor(const QColor& color);
protected:
bool event(QEvent* event) override;
void leaveEvent(QEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
//修改鼠標樣式,且是否處于邊界
void updateRegion(QMouseEvent* event);
//修改大小和位置,即geometry
void resizeRegion(int marginTop, int marginBottom, int marginLeft, int marginRight);
void createShadow();
void maximizeWidget();
void minimizeWidget();
void restoreWidget();
void setMoveStatus(bool status);
void paintEvent(QPaintEvent* event) override;
private:
bool m_bIsPressed; //是否鼠標按下
bool m_bIsResizing; //是否要拉伸
bool m_bIsDoublePressed;//沒用到
bool m_bIsMove;
QPoint m_pressPoint; //鼠標按下時的坐標
QPoint m_pressPoint_initial;//沒用到
QPoint m_movePoint; //鼠標移動了的相對坐標
Direction m_direction; //鼠標的狀態即在哪個邊界
QRect rect; //用于存放geometry
TransparentBorder* border;
};
class TransparentBorder :public QWidget {
public:
TransparentBorder();
~TransparentBorder();
void resizeBorder(const QPoint& movePoint,FramelessWidget::Direction direction);
void moveBorder(const QPoint& movePoint);
void setParentRect(const QRect& rect);
void setBorderColor(const QColor& color);
protected:
void paintEvent(QPaintEvent* event) override;
private:
QPoint marginOrigin;
QRect parentRect;
QColor borderColor;
};
cpp文件
#include "framelesswidget.h"
#include <QEvent>
#include <QMouseEvent>
#include <QRect>
#include <QApplication>
#include <QGraphicsDropShadowEffect>
#include <QtMath>
#include <QPen>
#include <QPainter>
#include <QPainterPath>
#include "model/data.h"
FramelessWidget::FramelessWidget(QWidget* parent)
: QWidget(parent), m_bIsPressed(false), m_bIsResizing(false), m_bIsDoublePressed(false),m_bIsMove(false),
m_direction(NONE)
{
setWindowFlags(Qt::FramelessWindowHint); //隱藏標題欄(無邊框)
setAttribute(Qt::WA_StyledBackground); //啟用樣式背景繪制
//setAttribute(Qt::WA_TranslucentBackground); //背景透明
setAttribute(Qt::WA_Hover);
setAttribute(Qt::WA_StaticContents);
this->setMinimumSize(50, 50);
border = new TransparentBorder();//并沒有讓border掛在this下面,所以得析構時得delete
border->hide();
}
FramelessWidget::~FramelessWidget()
{
delete border;
}
bool FramelessWidget::event(QEvent* event)
{
///
// 使得移除窗口仍能進行鼠標移動的事件
///
if (event->type() == QEvent::HoverMove) {
QHoverEvent* hoverEvent = static_cast<QHoverEvent*>(event);
QMouseEvent mouseEvent(QEvent::MouseMove, hoverEvent->pos(),
Qt::NoButton, Qt::NoButton, Qt::NoModifier);
mouseMoveEvent(&mouseEvent);
//LOG() << "hover move";
}
return QWidget::event(event);
}
void FramelessWidget::mousePressEvent(QMouseEvent* event)
{
QWidget::mousePressEvent(event);
if (event->button() == Qt::LeftButton) {
m_bIsPressed = true;
m_pressPoint = event->globalPos();//鼠標按下的絕對坐標
m_movePoint = QPoint(0, 0);//使得上次移動的相對坐標清零
}
//*
//如果m_direction不為NoNE 即 鼠標在窗口邊界 那么就是要進行窗口拉伸
//*
if (m_direction != NONE) {
m_bIsResizing = true;
}
//由于使用的是 額外創建一個boder邊框使得能夠預覽窗口的位置
// 所以得讓boder知道要綁定誰,且知道他的geometry
if (m_bIsMove || m_bIsResizing) {
border->setParentRect(geometry());
border->show();//顯示邊框
}
}
void FramelessWidget::mouseMoveEvent(QMouseEvent* event)
{
QWidget::mouseMoveEvent(event);
m_movePoint = event->globalPos() - m_pressPoint;
//LOG() <<"m_bIsResizing"<< m_bIsResizing;
//LOG() <<"m_bIsPressed"<< m_bIsPressed;
//*
// 鼠標沒按下 且 不處于拉伸狀態才來判斷是不是在邊界
//*
if (windowState() != Qt::WindowMaximized && !m_bIsPressed && !m_bIsResizing) {
updateRegion(event);
}
// LOG() << "width" << minimumWidth();
// LOG() << "height" << minimumHeight();
//*
// 鼠標按下 但不處于拉伸狀態
//*
if (m_bIsPressed && !m_bIsResizing) {
border->moveBorder(m_movePoint);
}
//拉伸狀態
else if (m_bIsResizing) {
border->resizeBorder(m_movePoint, m_direction);
}
}
// 用于識別是否是拉伸動作
void FramelessWidget::updateRegion(QMouseEvent* event)
{
QRect mainRect = geometry();
int marginTop = event->globalY() - mainRect.y();
int marginBottom = mainRect.y() + mainRect.height() - event->globalY();
int marginLeft = event->globalX() - mainRect.x();
int marginRight = mainRect.x() + mainRect.width() - event->globalX();
//LOG() << marginTop << "|" << marginBottom << "|" << marginLeft << "|" << marginRight;
if (!m_bIsResizing && !m_bIsPressed) {
if ((marginRight >= MARGIN_MIN_SIZE && marginRight <= MARGIN_MAX_SIZE)
&& ((marginBottom <= MARGIN_MAX_SIZE) && marginBottom >= MARGIN_MIN_SIZE)) {
m_direction = BOTTOMRIGHT;
setCursor(Qt::SizeFDiagCursor);
}
else if ((marginTop >= MARGIN_MIN_SIZE && marginTop <= MARGIN_MAX_SIZE)
&& (marginRight >= MARGIN_MIN_SIZE && marginRight <= MARGIN_MAX_SIZE)) {
m_direction = TOPRIGHT;
setCursor(Qt::SizeBDiagCursor);
}
else if ((marginLeft >= MARGIN_MIN_SIZE && marginLeft <= MARGIN_MAX_SIZE)
&& (marginTop >= MARGIN_MIN_SIZE && marginTop <= MARGIN_MAX_SIZE)) {
m_direction = TOPLEFT;
setCursor(Qt::SizeFDiagCursor);
}
else if ((marginLeft >= MARGIN_MIN_SIZE && marginLeft <= MARGIN_MAX_SIZE)
&& (marginBottom >= MARGIN_MIN_SIZE && marginBottom <= MARGIN_MAX_SIZE)) {
m_direction = BOTTOMLEFT;
setCursor(Qt::SizeBDiagCursor);
}
else if (marginBottom <= MARGIN_MAX_SIZE && marginBottom >= MARGIN_MIN_SIZE) {
m_direction = DOWN;
setCursor(Qt::SizeVerCursor);
}
else if (marginLeft <= MARGIN_MAX_SIZE - 1 && marginLeft >= MARGIN_MIN_SIZE - 1) {
m_direction = LEFT;
setCursor(Qt::SizeHorCursor);
}
else if (marginRight <= MARGIN_MAX_SIZE && marginRight >= MARGIN_MIN_SIZE) {
m_direction = RIGHT;
setCursor(Qt::SizeHorCursor);
}
else if (marginTop <= MARGIN_MAX_SIZE && marginTop >= MARGIN_MIN_SIZE) {
m_direction = UP;
setCursor(Qt::SizeVerCursor);
}
else {
m_direction = NONE;
setCursor(Qt::ArrowCursor);
}
}
//LOG() << m_direction;
}
//對窗口進行大小和位置進行設置
void FramelessWidget::resizeRegion(int marginTop, int marginBottom,
int marginLeft, int marginRight)
{
if (m_bIsPressed && m_bIsResizing) {
//LOG() << "resize" << m_direction;
switch (m_direction) {
case BOTTOMRIGHT:
{
rect = geometry();
rect.setBottomRight(rect.bottomRight() + m_movePoint);
this->setGeometry(rect);
}
break;
case TOPRIGHT:
{
rect = geometry();
// 設置的寬度 小于 最小寬度 高度 小于 最小高度
if (geometry().width() + m_movePoint.x() <= minimumWidth() && geometry().height() - m_movePoint.y() <= minimumHeight()) {
rect.setRect(rect.x() ,
rect.y() + rect.height() - minimumHeight(),
minimumWidth(),
minimumHeight());
//LOG() << "1";
}
// 設置的寬度 小于 最小寬度 高度 大于 最小高度
else if (geometry().width() + m_movePoint.x() <= minimumWidth() && geometry().height() - m_movePoint.y() > minimumHeight()) {
rect.setRect(rect.x() ,
rect.y() + m_movePoint.y(),
minimumWidth(),
rect.height() - m_movePoint.y());
//LOG() << "2";
}
// 設置的寬度 大于 最小寬度 高度 小于 最小高度
else if (geometry().height() - m_movePoint.y() <= minimumHeight() && geometry().width() + m_movePoint.x() > minimumWidth()) {
rect.setRect(rect.x() ,
rect.y() + rect.height() - minimumHeight(),
rect.width() + m_movePoint.x(),
minimumHeight());
//LOG() << "3"<<rect;
}
// 設置的寬度 大于 最小寬度 高度 大于 最小高度
else {
rect.setTopRight(rect.topRight() + m_movePoint);
//LOG() << "4";
}
this->setGeometry(rect);
}
break;
case TOPLEFT:
{
rect = geometry();
// 設置的寬度 小于 最小寬度 高度 小于 最小高度
if (geometry().width() - m_movePoint.x() <= minimumWidth() && geometry().height() - m_movePoint.y() <= minimumHeight()) {
rect.setRect(rect.x() +rect.width() -minimumWidth(),
rect.y() + rect.height() -minimumHeight(),
minimumWidth(),
minimumHeight());
//LOG() << "1";
}
// 設置的寬度 小于 最小寬度 高度 大于 最小高度
else if (geometry().width() - m_movePoint.x() <= minimumWidth() && geometry().height() - m_movePoint.y() > minimumHeight()) {
rect.setRect(rect.x() +rect.width() -minimumWidth() ,
rect.y() + m_movePoint.y(),
minimumWidth(),
rect.height() - m_movePoint.y());
//LOG() << "2";
}
// 設置的寬度 大于 最小寬度 高度 小于 最小高度
else if (geometry().height() - m_movePoint.y() <= minimumHeight() && geometry().width() - m_movePoint.x() > minimumWidth()) {
rect.setRect(rect.x() + m_movePoint.x(),
rect.y() + rect.height() - minimumHeight(),
rect.width() - m_movePoint.x(),
minimumHeight());
//LOG() << "3"<<rect;
}
// 設置的寬度 大于 最小寬度 高度 大于 最小高度
else {
rect.setTopLeft(rect.topLeft() + m_movePoint);
//LOG() << "4";
}
this->setGeometry(rect);
}
break;
case BOTTOMLEFT:
{
rect = geometry();
// 設置的寬度 小于 最小寬度 高度 小于 最小高度
if (geometry().width() - m_movePoint.x() <= minimumWidth() && geometry().height() + m_movePoint.y() <= minimumHeight()) {
rect.setRect(rect.x() +rect.width() -minimumWidth(),
rect.y(),
minimumWidth(),
minimumHeight());
//LOG() << "1";
}
// 設置的寬度 小于 最小寬度 高度 大于 最小高度
else if (geometry().width() - m_movePoint.x() <= minimumWidth() && geometry().height() + m_movePoint.y() > minimumHeight()) {
rect.setRect(rect.x() +rect.width() -minimumWidth() ,
rect.y(),
minimumWidth(),
rect.height() + m_movePoint.y());
//LOG() << "2";
}
// 設置的寬度 大于 最小寬度 高度 小于 最小高度
else if (geometry().height() + m_movePoint.y() <= minimumHeight() && geometry().width() - m_movePoint.x() > minimumWidth()) {
rect.setRect(rect.x() + m_movePoint.x(),
rect.y(),
rect.width() - m_movePoint.x(),
minimumHeight());
//LOG() << "3"<<rect;
}
// 設置的寬度 大于 最小寬度 高度 大于 最小高度
else {
rect.setBottomLeft(rect.bottomLeft() + m_movePoint);
//LOG() << "4";
}
this->setGeometry(rect);
}
break;
case RIGHT:
{
rect = geometry();
rect.setRight(rect.right() + m_movePoint.x());
this->setGeometry(rect);
//setFixedSize(rect.width(), rect.height());
}
break;
case DOWN:
{
rect = geometry();
rect.setBottom(rect.bottom() + m_movePoint.y());
//rect.setHeight(rect.height() + m_movePoint.y());
this->setGeometry(rect);
//setFixedSize(rect.width(), rect.height());
//LOG() << "down";
}
break;
case LEFT:
{
if (geometry().width() - m_movePoint.x() < minimumWidth()) {
rect = geometry();
rect.setRect(rect.x() + rect.width() - minimumWidth(),
rect.y() ,
minimumWidth(),
rect.height());
this->setGeometry(rect);
}
else {
rect = geometry();
rect.setLeft(rect.left() + m_movePoint.x());
//rect.setX(rect.width() - m_movePoint.x());
this->setGeometry(rect);
//setFixedSize(rect.width(), rect.height());
//this->move(rect.x() + m_movePoint.x(), rect.y());
}
}
break;
case UP:
{
if (geometry().height() - m_movePoint.y() < minimumHeight()) {
rect = geometry();
rect.setRect(rect.x(),
rect.y() + rect.height() - minimumHeight(),
rect.width(),
minimumHeight());
this->setGeometry(rect);
}
else {
rect = geometry();
rect.setTop(rect.top() + m_movePoint.y());
this->setGeometry(rect);
//LOG() << "UP";
}
}
break;
default:
{
}
break;
}
}
else {
m_bIsResizing = false;
//當不在邊界一定得設置NONE,不然會導致在邊界后,下次不在邊界會被判斷成拉伸狀態
m_direction = NONE;
}
}
void FramelessWidget::mouseReleaseEvent(QMouseEvent* event)
{
///
// 鼠標松開 需要判斷 是否處于拉伸狀態需要修改窗口
// 是否是窗口需要移動
///
QWidget::mouseReleaseEvent(event);
//LOG() << m_direction;
if (NONE != m_direction) {
//LOG() << "resize";
resizeRegion(0, 0, 0, 0);
}
// 鼠標松開,當鼠標按下的狀態還沒修改
// 處于移動窗口的狀態
if (!m_bIsResizing && m_bIsPressed && m_bIsMove) {
this->move(geometry().x() + m_movePoint.x(), geometry().y() + m_movePoint.y());
}
this->setMoveStatus(false);
//LOG() << "1:" << geometry();
// 修改鼠標的樣式
if (windowState() != Qt::WindowMaximized) {
updateRegion(event);
}
//重置值,防止影響下次判斷
if (event->button() == Qt::LeftButton) {
m_bIsPressed = false;
m_bIsResizing = false;
m_bIsDoublePressed = false;
m_direction = NONE;
}
//完成 操作 取消顯示邊框
border->hide();
//LOG() << "move_point" << m_movePoint;
//LOG()<<"2:" << geometry();
}
void FramelessWidget::leaveEvent(QEvent *event)
{
// m_bIsPressed = false;
// m_bIsDoublePressed = false;
// m_bIsResizing = false;
QWidget::leaveEvent(event);
}
void FramelessWidget::createShadow()
{
QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(this);
shadowEffect->setColor(Qt::black);
shadowEffect->setOffset(0, 0);
shadowEffect->setBlurRadius(13);
this->setGraphicsEffect(shadowEffect);
}
void FramelessWidget::maximizeWidget()
{
showMaximized();
}
void FramelessWidget::minimizeWidget()
{
showMinimized();
}
void FramelessWidget::restoreWidget()
{
showNormal();
}
void FramelessWidget::setMoveStatus(bool status)
{
this->m_bIsMove = status;
}
void FramelessWidget::setBorderColor(const QColor& color)
{
this->border->setBorderColor(color);
}
void FramelessWidget::paintEvent(QPaintEvent* event)
{
QWidget::paintEvent(event);
}
TransparentBorder::TransparentBorder():
QWidget(),marginOrigin(0,0),parentRect(0,0,0,0),borderColor(Qt::white)
{
setWindowOpacity(1);
this->setAttribute(Qt::WA_TranslucentBackground, true);//透明
this->setWindowFlags(Qt::FramelessWindowHint);//無邊框
}
TransparentBorder::~TransparentBorder()
{
}
//邊框的大小設置
void TransparentBorder::resizeBorder(const QPoint& m_movePoint, FramelessWidget::Direction direction)
{
switch (direction) {
case FramelessWidget::Direction::BOTTOMRIGHT:
{
LOG() << "BottomRight";
QRect rect(parentRect);
rect.setBottomRight(rect.bottomRight() + m_movePoint);
this->setGeometry(rect);
}
break;
case FramelessWidget::Direction::TOPRIGHT:
{
QRect rect(parentRect);
rect.setTopRight(rect.topRight() + m_movePoint);
this->setGeometry(rect);
}
break;
case FramelessWidget::Direction::TOPLEFT:
{
QRect rect(parentRect);
rect.setTopLeft(rect.topLeft() + m_movePoint);
this->setGeometry(rect);
}
break;
case FramelessWidget::Direction::BOTTOMLEFT:
{
QRect rect(parentRect);
rect.setBottomLeft(rect.bottomLeft() + m_movePoint);
this->setGeometry(rect);
}
break;
case FramelessWidget::Direction::RIGHT:
{
QRect rect(parentRect);
rect.setRight(rect.right() + m_movePoint.x());
setGeometry(rect);
}
break;
case FramelessWidget::Direction::DOWN:
{
//LOG() << "down";
//LOG() << "parentRect:" << parentRect;
QRect rect(parentRect);
rect.setBottom(rect.bottom() + m_movePoint.y());
//rect.setHeight(rect.height() + m_movePoint.y());
setGeometry(rect);
}
break;
case FramelessWidget::Direction::LEFT:
{
QRect rect(parentRect);
rect.setLeft(rect.left() + m_movePoint.x());
setGeometry(rect);
}
break;
case FramelessWidget::Direction::UP:
{
QRect rect(parentRect);
//rect.setHeight(rect.height() - m_movePoint.y());
rect.setTop(rect.top() + m_movePoint.y());
setGeometry(rect);
//setFixedSize(rect.width(), rect.height());
//LOG() << "UP";
}
break;
default:
{
}
break;
}
}
//移動邊框
void TransparentBorder::moveBorder(const QPoint& movePoint)
{
this->move(parentRect.x()+movePoint.x(), parentRect.y()+movePoint.y());
}
//設置要綁定哪個窗口
void TransparentBorder::setParentRect(const QRect& rect)
{
parentRect.setRect(rect.x(),rect.y(),rect.width(),rect.height());
this->setGeometry(parentRect);
}
void TransparentBorder::setBorderColor(const QColor& color)
{
borderColor = color;
}
// 渲染時畫出出邊框
void TransparentBorder::paintEvent(QPaintEvent *event)
{
QRect rect = geometry();
QPainter painter(this);
painter.setBrush(QColor(9,151,247,1));//painter區域全部的背景色
painter.setPen(QPen(borderColor,3,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin));
painter.setCompositionMode(QPainter::CompositionMode_Difference);
painter.drawRect(0,0,rect.width(),rect.height());
//this->resize(parentRect.width(),parentRect.height());
}

浙公網安備 33010602011771號