目录
1.问题的提出
一般情况下,当去掉窗体标题栏时,按住鼠标左键就无法实现拖动窗体;当去掉窗体标题栏时,窗体四周的边框也去掉了,此时按住鼠标左键拖动窗体四周边沿,无法实现鼠标拖动改变窗体大小。如下为Qt实现去掉窗体标题栏的代码:
setWindowFlag(Qt::FramelessWindowHint); // 去掉标题栏
现在的问题是:
- 在去掉窗体标题栏的情况下,如何按住鼠标左键,实现鼠标拖动改变窗体大小?
- 在去掉窗体标题栏的情况下,如何在窗体上按住鼠标左键,实现移动窗体?
2.实现方法
Qt中有个QSizeGrip类,可以很好解决1节提到的问题,关于该类的具体用法,参见Qt Assistant。
下面直接上代码:
.h文件如下:
#pragma once
#include <QtWidgets/QWidget>
#include "ui_QtGuiApplication2.h"
class QtGuiApplication2 : public QWidget
{
Q_OBJECT
public:
QtGuiApplication2(QWidget *parent = Q_NULLPTR);
private: // virtual
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private:
Ui::QtGuiApplication2Class ui;
bool m_bLeftMousePressed{ false }; // 鼠标左键是否被按下
QPoint m_lastPoint; // 鼠标上次按下的点
};
.cpp文件如下:
#include "QtGuiApplication2.h"
#include<QSizeGrip>
#include<QVBoxLayout>
#include<QFrame>
#include<QStatusBar>
#include<QMouseEvent>
QtGuiApplication2::QtGuiApplication2(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
setWindowFlag(Qt::FramelessWindowHint); // 去掉标题栏
QVBoxLayout* pMainWnd = new QVBoxLayout(this);
setLayout(pMainWnd);
auto pFrame = new QFrame(this);
pFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
pMainWnd->addWidget(pFrame);
QHBoxLayout* pBottomLayout = new QHBoxLayout(this);
pMainWnd->addLayout(pBottomLayout);
// 加一个水平弹簧,为了把后面的QSizeGrip挤到最右边
auto horizontalSpacer = new QSpacerItem(193, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
pBottomLayout->addItem(horizontalSpacer);
// 创建QSizeGrip对象,该对象就是用来调整窗体大小的
auto pSizeGrip = new QSizeGrip(this);
pSizeGrip->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
pBottomLayout->addWidget(pSizeGrip); // 将QSizeGrip对象加入窗体右下角
// 也可以加入一个状态栏来实现拖动窗体右下角改变窗体大小
//auto pStatusBar = new QStatusBar(this);
//pStatusBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
//pBottomLayout->addWidget(pStatusBar);
}
// 鼠标左键按下,准备移动窗体
void QtGuiApplication2::mousePressEvent(QMouseEvent* event)
{
if (Qt::LeftButton != event->button())
{
return;
}
m_lastPoint = event->globalPos();
m_bLeftMousePressed = true;
}
// 移动窗体结束
void QtGuiApplication2::mouseReleaseEvent(QMouseEvent* event)
{
if (Qt::LeftButton != event->button())
{
return;
}
m_bLeftMousePressed = false;
}
// 开始移动窗体
void QtGuiApplication2::mouseMoveEvent(QMouseEvent* event)
{
auto curMousePt = event->globalPos(); // 注意:这里记录是窗体在屏幕上的坐标位置
auto offsetPt = curMousePt - m_lastPoint; // 计算和上次移动点的坐标差值
move(pos() + offsetPt);
m_lastPoint = curMousePt; // 记录本次窗体所在位置,以便下次计算位置
}
效果如下:

图1
QSizeGrip对象在窗体中的位置如下:

图2
说明:
- Qt中的QDialog和QStatusBar类封装了 QSizeGrip对象,故上面代码也可以插入QStatusBar类到右下角来实现 QSizeGrip对象调整大小功能,即注释掉29~31行代码,取消34~36注释,实现同样功能。
- Qt中的QDialog和QStatusBar类提供了setSizeGripEnabled(bool)方法用来设置是否启用QSizeGrip对象,默认是启用的,即右下角QSizeGrip对象是可见的。
3.存在的问题
从图1可以看到,当按住鼠标左键拖动右下角的QSizeGrip对象以改变窗体大小,此时窗体大小确实改变了,但窗体也随之被移动了,也就是既改变了窗体大小也移动了窗体。原因是QSizeGrip对象必须响应鼠标移动事件才能改变窗体大小,与此同时窗体响应鼠标事件被移动。如何只保证改变窗体大小,而不移动窗体呢?此时必须从QSizeGrip类派生出自己的子类,代码如下:
mySizeGrip.h:
#pragma once
#include <QSizeGrip>
namespace Ui { class CMySizeGrip; };
class CMySizeGrip : public QSizeGrip
{
Q_OBJECT
public:
CMySizeGrip(QWidget *parent = Q_NULLPTR);
~CMySizeGrip();
private: // virtual
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private:
Ui::CMySizeGrip *ui;
QPoint m_lastPoint; // 鼠标上次按下的点
QWidget* m_pParent; // 父窗体,也就是要调节大小的窗体
};
mySizeGrip.cpp:
#include "mySizeGrip.h"
#include "ui_mySizeGrip.h"
#include<QMouseEvent>
#include<QApplication>
CMySizeGrip::CMySizeGrip(QWidget *parent)
: QSizeGrip(parent)
{
m_pParent = parent;
ui = new Ui::CMySizeGrip();
ui->setupUi(this);
}
CMySizeGrip::~CMySizeGrip()
{
delete ui;
}
void CMySizeGrip::mousePressEvent(QMouseEvent* event)
{
if (Qt::LeftButton != event->button())
{
return;
}
m_lastPoint = event->globalPos();
}
void CMySizeGrip::mouseReleaseEvent(QMouseEvent* event)
{
}
void CMySizeGrip::mouseMoveEvent(QMouseEvent* event)
{
auto curMousePt = event->globalPos();
auto offset = curMousePt - m_lastPoint;
m_lastPoint = curMousePt;
// 调整父窗体大小
m_pParent->resize(m_pParent->size().width() + offset.x(), m_pParent->size().height() + offset.y());
}
将前面第29行的:
auto pSizeGrip = new QSizeGrip(this);
改为:
auto pSizeGrip = new CMySizeGrip(this);
在Qt中,通过设置Qt::FramelessWindowHint标志可以去掉窗体标题栏,但这会导致无法拖动窗体和改变大小。解决方案是使用QSizeGrip类实现拖动和调整大小功能。当使用QSizeGrip时,会同时触发窗体移动和大小改变。为了解决这个问题,需要从QSizeGrip派生新的类,重写鼠标事件处理,以确保仅能改变窗体大小而不会移动它。

1159

被折叠的 条评论
为什么被折叠?



