![bb1913365db9f3309110870ab315ff72.png](https://i-blog.csdnimg.cn/blog_migrate/195fe63e88385efb44eac566e04a1d99.jpeg)
总第49篇
在我们的项目开发过程中,为了界面的美观,经常会自定义标题栏、自定义无边框窗体等,这些都会面临一个问题,那就是在定义无边框窗体后,要自己处理窗体的移动、拉伸。若在项目中有多处应用无边框窗体,就有必要将其写成一个类并封装成一个库了。
本文梳理了无边框窗体的实现,将其写成一个通用的类,方便在项目的其它地方调用,也方便以后的开发中直接应用,供大家参考和借鉴使用。
此类实现的功能包括:
- 可以指定需要无边框的
widget
- 边框的四周八个方向都可以自由拉伸
- 可设置对应位置的边距,以便扩大识别区域
- 可设置是否允许拉伸
- 可设置是否允许拖动
其实现效果如下图所示:
![7fce13018195015cc2280275bb651e33.gif](https://i-blog.csdnimg.cn/blog_migrate/dce1729a6fed85aaa741e7176cd4dfe3.gif)
不多说,直接上代码。下面是头文件,定义了相关的变量和一些接口函数。
class FramelessWidget : public QObject
{
Q_OBJECT
public:
explicit FramelessWidget(QObject *parent = 0);
protected:
bool eventFilter(QObject *watched, QEvent *event);
private:
int padding; //边距
bool moveEnable; //可移动
bool resizeEnable; //可拉伸
QWidget *widget; //无边框窗体
bool pressed; //鼠标按下
bool pressedLeft; //鼠标按下左侧
bool pressedRight; //鼠标按下右侧
bool pressedTop; //鼠标按下上侧
bool pressedBottom; //鼠标按下下侧
bool pressedLeftTop; //鼠标按下左上侧
bool pressedRightTop; //鼠标按下右上侧
bool pressedLeftBottom; //鼠标按下左下侧
bool pressedRightBottom; //鼠标按下右下侧
int rectX, rectY, rectW, rectH; //窗体坐标+宽高
QPoint lastPos; //鼠标按下处坐标
QRect rectLeft; //左侧区域
QRect rectRight; //右侧区域
QRect rectTop; //上侧区域
QRect rectBottom; //下侧区域
QRect rectLeftTop; //左上侧区域
QRect rectRightTop; //右上侧区域
QRect rectLeftBottom; //左下侧区域
QRect rectRightBottom; //右下侧区域
public Q_SLOTS:
//设置边距
void setPadding(int padding);
//设置是否可拖动+拉伸
void setMoveEnable(bool moveEnable);
void setResizeEnable(bool resizeEnable);
//设置目标无边框的窗体
void setWidget(QWidget *widget);
};
下面是源文件的关键代码,主要函数就是那个事件过滤器的书写。
FramelessWidget::FramelessWidget(QObject *parent) : QObject(parent)
{
padding = 8;
moveEnable = true;
resizeEnable = true;
widget = NULL;
pressed = false;
pressedLeft = false;
pressedRight = false;
pressedTop = false;
pressedBottom = false;
pressedLeftTop = false;
pressedRightTop = false;
pressedLeftBottom = false;
pressedRightBottom = false;
//如果父类是窗体则直接设置
if (parent->isWidgetType()) {
setWidget((QWidget *)parent);
}
}
void FramelessWidget::setWidget(QWidget *widget)
{
if (this->widget == NULL) {
this->widget = widget;
//设置鼠标追踪为真
this->widget->setMouseTracking(true);
//绑定事件过滤器
this->widget->installEventFilter(this);
//设置悬停为真,必须设置这个,
//当父窗体里边还有子窗体全部遮挡了识别不到MouseMove,需要识别HoverMove
this->widget->setAttribute(Qt::WA_Hover, true);
}
}
bool FramelessWidget::eventFilter(QObject *watched, QEvent *event)
{
if (widget != NULL && watched == widget)
{
if (event->type() == QEvent::Resize)
{
//重新计算八个描点的区域
int width = widget->width();
int height = widget->height();
//左侧描点区域
rectLeft = QRect(0, padding, padding, height - padding * 2);
//上侧描点区域
rectTop = QRect(padding, 0, width - padding * 2, padding);
//右侧描点区域
rectRight = QRect(width - padding, padding, padding, height - padding * 2);
//下侧描点区域
rectBottom = QRect(padding, height - padding, width - padding * 2, padding);
//左上角描点区域
rectLeftTop = QRect(0, 0, padding, padding);
//右上角描点区域
rectRightTop = QRect(width - padding, 0, padding, padding);
//左下角描点区域
rectLeftBottom = QRect(0, height - padding, padding, padding);
//右下角描点区域
rectRightBottom = QRect(width - padding, height - padding, padding, padding);
}
else if (event->type() == QEvent::HoverMove)
{
//设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别
QHoverEvent *hoverEvent = (QHoverEvent *)event;
QPoint point = hoverEvent->pos();
if (resizeEnable)
{
if (rectLeft.contains(point)) {
widget->setCursor(Qt::SizeHorCursor);
} else if (rectRight.contains(point)) {
widget->setCursor(Qt::SizeHorCursor);
} else if (rectTop.contains(point)) {
widget->setCursor(Qt::SizeVerCursor);
} else if (rectBottom.contains(point)) {
widget->setCursor(Qt::SizeVerCursor);
} else if (rectLeftTop.contains(point)) {
widget->setCursor(Qt::SizeFDiagCursor);
} else if (rectRightTop.contains(point)) {
widget->setCursor(Qt::SizeBDiagCursor);
} else if (rectLeftBottom.contains(point)) {
widget->setCursor(Qt::SizeBDiagCursor);
} else if (rectRightBottom.contains(point)) {
widget->setCursor(Qt::SizeFDiagCursor);
} else {
widget->setCursor(Qt::ArrowCursor);
}
}
//根据当前鼠标位置,计算XY轴移动了多少
int offsetX = point.x() - lastPos.x();
int offsetY = point.y() - lastPos.y();
//根据按下处的位置判断是否是移动控件还是拉伸控件
if (moveEnable)
{
if (pressed) {
widget->move(widget->x() + offsetX, widget->y() + offsetY);
}
}
if (resizeEnable)
{
if (pressedLeft) {
int resizeW = widget->width() - offsetX;
if (widget->minimumWidth() <= resizeW) {
widget->setGeometry(widget->x() + offsetX, rectY, resizeW, rectH);
}
} else if (pressedRight) {
widget->setGeometry(rectX, rectY, rectW + offsetX, rectH);
} else if (pressedTop) {
int resizeH = widget->height() - offsetY;
if (widget->minimumHeight() <= resizeH) {
widget->setGeometry(rectX, widget->y() + offsetY, rectW, resizeH);
}
} else if (pressedBottom) {
widget->setGeometry(rectX, rectY, rectW, rectH + offsetY);
} else if (pressedLeftTop) {
int resizeW = widget->width() - offsetX;
int resizeH = widget->height() - offsetY;
if (widget->minimumWidth() <= resizeW) {
widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
}
if (widget->minimumHeight() <= resizeH) {
widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
}
} else if (pressedRightTop) {
int resizeW = rectW + offsetX;
int resizeH = widget->height() - offsetY;
if (widget->minimumHeight() <= resizeH) {
widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
}
} else if (pressedLeftBottom) {
int resizeW = widget->width() - offsetX;
int resizeH = rectH + offsetY;
if (widget->minimumWidth() <= resizeW) {
widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
}
if (widget->minimumHeight() <= resizeH) {
widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
}
} else if (pressedRightBottom) {
int resizeW = rectW + offsetX;
int resizeH = rectH + offsetY;
widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
}
}
}
else if (event->type() == QEvent::MouseButtonPress)
{
//记住当前控件坐标和宽高以及鼠标按下的坐标
QMouseEvent *mouseEvent = (QMouseEvent *)event;
rectX = widget->x();
rectY = widget->y();
rectW = widget->width();
rectH = widget->height();
lastPos = mouseEvent->pos();
//判断按下的手柄的区域位置
if (rectLeft.contains(lastPos)) {
pressedLeft = true;
} else if (rectRight.contains(lastPos)) {
pressedRight = true;
} else if (rectTop.contains(lastPos)) {
pressedTop = true;
} else if (rectBottom.contains(lastPos)) {
pressedBottom = true;
} else if (rectLeftTop.contains(lastPos)) {
pressedLeftTop = true;
} else if (rectRightTop.contains(lastPos)) {
pressedRightTop = true;
} else if (rectLeftBottom.contains(lastPos)) {
pressedLeftBottom = true;
} else if (rectRightBottom.contains(lastPos)) {
pressedRightBottom = true;
} else {
pressed = true;
}
}
else if (event->type() == QEvent::MouseButtonRelease)
{
//恢复所有
pressed = false;
pressedLeft = false;
pressedRight = false;
pressedTop = false;
pressedBottom = false;
pressedLeftTop = false;
pressedRightTop = false;
pressedLeftBottom = false;
pressedRightBottom = false;
widget->setCursor(Qt::ArrowCursor);
}
}
return QObject::eventFilter(watched, event);
}
这个无边框的拉伸与移动处理,主要是重新计算窗口的大小和位置,理解了这个,写起来不是太难。
大家可以在这个基础上再加工,根据自己的需要做的更精细、更精美、更通用。
本文到此结束!
如果对你有帮助,请随手 点赞 或 点喜欢!关注本专栏,更多干货与你分享。
=======================================================
欢迎【关注、私信 @武三郎】。我们一起交流一起进步。