描述
在很多时候都需要自定义标题头的样式。在Qt里面。如果去掉了系统自带的标题头。那么很多属性也就会消失。例如没法拖拽窗体。没法通过鼠标拉伸窗体。
大致实现思路
添加一个水平布局。里面分别有两个QLabel以及三个QPushButton。两个QLabel用来显示图标和窗体标题。三个QPushButton分别对应着隐藏。最大化以及关闭按钮。
然后鼠标在靠近窗体的边缘的时候需要更改鼠标光标的形状。且可以拉伸窗体。同时鼠标按在标题上可以进行拖拽。此标题头内部维护了QWidget* _ownerWidget.其实就是这个标题头的拥有窗体。然后通过事件过滤器eventFilter
来监测鼠标事件等事件。
实现代码
void FCustomTitleWidget::setUpConnection()
{
connect(_buttonHide, &QPushButton::clicked, this, &FCustomTitleWidget::onTargetWidgetHide);//隐藏按钮
connect(_buttonMinOrMax, &QPushButton::clicked, this, &FCustomTitleWidget::onTargetWidgetMinOrMax);//最大化最小化按钮
connect(_buttonClose, &QPushButton::clicked, this, &FCustomTitleWidget::onTargetWidgetClose);//关闭按钮
}
void FCustomTitleWidget::initUi()
{
_layoutMain = new QHBoxLayout;
_layoutMain->setContentsMargins(0, 0, 0, 0);
_layoutMain->setSpacing(5);
_labelIcon = new QLabel(this);
_labelIcon->setObjectName("labelIcon");
_labelTitle = new QLabel(this);
_labelTitle->setObjectName("labelTitle");
_buttonHide = new QPushButton(this);
_buttonHide->setObjectName("buttonHide");
_buttonMinOrMax = new QPushButton(this);
_buttonMinOrMax->setObjectName("buttonMinOrMax");
_buttonClose = new QPushButton(this);
_buttonClose->setObjectName("buttonClose");
_layoutMain->addWidget(_labelIcon);
_layoutMain->addWidget(_labelTitle);
_layoutMain->addStretch();
_layoutMain->addWidget(_buttonHide);
_layoutMain->addWidget(_buttonMinOrMax);
_layoutMain->addWidget(_buttonClose);
this->setLayout(_layoutMain);
}
void FCustomTitleWidget::setOwnerWidget(QWidget * target)//设置拥有窗体
{
assert(target);
if (_ownerWidget != target)
{
if (_ownerWidget)
{
_ownerWidget->removeEventFilter(this);
}
_ownerWidget = target;
_ownerWidget->setAttribute(Qt::WA_Hover);//hover属性
_ownerWidget->installEventFilter(this);//安装事件过滤器
}
}
拖拽的实现
在鼠标按下的时候。记录下鼠标按下的位置。然后在鼠标拖拽移动的时候记录下和上一次位置的差值。然后更改窗体的位置即可。
实现代码
case QEvent::MouseButtonPress:
{
if (mouseEvent)
{
_mousePressedPoint = mouseEvent->pos();//记录下开始的位置
_dragDir = getDragDirByMousePos(_mousePressedPoint);
_lastMousePoint = mouseEvent->globalPos();
}
}
break;
case QEvent::MouseMove:
{
if (mouseEvent)
{
updateWidgetSizeByDragDir(_dragDir, mouseEvent->globalPos());//更改窗体大小或者位置
_lastMousePoint = mouseEvent->globalPos();
}
}
break;
更改鼠标光标
设置**_ownerWidget的属性_ownerWidget->setAttribute(Qt::WA_Hover);
可以检测到鼠标悬浮移动事件。然后判断鼠标在_ownerWidget**的位置。若是在四周就根据所在的方向更改鼠标光标形状。
实现代码
Qt::CursorShape FCustomTitleWidget::getCursorShapeByDragDir(DragDir dir)
{
Qt::CursorShape shape = Qt::ArrowCursor;
switch (dir)
{
case DragDirMove:
{
shape = Qt::ArrowCursor;
}
break;
case DragDirLeftUp:
case DragDirRightBottom:
{
shape = Qt::SizeFDiagCursor;
}
break;
case DragDirUp:
case DragDirBottom:
{
shape = Qt::SizeVerCursor;
}
break;
case DragDirRightUp:
case DragDirLeftBottom:
{
shape = Qt::SizeBDiagCursor;
}
break;
case DragDirLeft:
case DragDirRight:
{
shape = Qt::SizeHorCursor;
}
break;
default:
break;
}
return shape;
}
鼠标拉伸窗体
首先根据鼠标拖拽的方向来确定如何更改窗体的位置或者大小。如果鼠标没有在四周,但是是在此标题头中。那么就是移动窗体的位置。如果在四周。就需要更改窗体的位置以及大小。由于笔者没有GIF制作工具。就不贴效果图了。具体的效果可以看看visual studio。将鼠标放在visual studio的四周。可以发现光标发生改变。若是按在上面的标题头,拖拽可以改变位置。若是在四周拖拽。可以拉伸改变窗体的位置。
实现代码
void FCustomTitleWidget::updateWidgetSizeByDragDir(DragDir dragDir, const QPoint &mousePos)
{
if (_ownerWidget == nullptr)
{
return;
}
auto widgetGeometry = _ownerWidget->geometry();
switch (dragDir)
{
case DragDirMove:
{
_ownerWidget->move(mousePos.x() - _lastMousePoint.x()+_ownerWidget->pos().x(), mousePos.y() - _lastMousePoint.y() + _ownerWidget->pos().y());
}
break;
case DragDirLeftUp:
{
widgetGeometry.setTopLeft(mousePos);
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirRightBottom:
{
widgetGeometry.setBottomRight(mousePos);
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirUp:
{
widgetGeometry.setTop(mousePos.y());
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirBottom:
{
widgetGeometry.setBottom(mousePos.y());
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirRightUp:
{
widgetGeometry.setTopRight(mousePos);
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirLeftBottom:
{
widgetGeometry.setBottomLeft(mousePos);
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirLeft:
{
widgetGeometry.setLeft(mousePos.x());
_ownerWidget->setGeometry(widgetGeometry);
}
break;
case DragDirRight:
{
widgetGeometry.setRight(mousePos.x());
_ownerWidget->setGeometry(widgetGeometry);
}
break;
default:
break;
}
}
使用示例
#include "FCustomTitleWidget.h"//自定义标题头
#include <QVBoxLayout>
#include <QTableWidget>
class TestTitleWidget : public QWidget
{
public:
TestTitleWidget()
{
_layout = new QVBoxLayout;
this->setLayout(_layout);
_titleWidget = new FCustomTitleWidget;
_titleWidget->setOwnerWidget(this);//设置标题头的拥有窗体
_layout->addWidget(_titleWidget);//标题头在最上面
_tableWidget = new QTableWidget;
_layout->addWidget(_tableWidget);
this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);//去掉系统标题头
}
~TestTitleWidget()
{
}
private:
QVBoxLayout *_layout;
QTableWidget *_tableWidget;
FCustomTitleWidget* _titleWidget;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestTitleWidget loginWidget;
loginWidget.show();
return a.exec();
}
代码链接
因为很多人上GitHub的网速都很慢。所以就将代码托管在了Gitee上面。
注意
代码不是一个完整的工程。只有.h文件以及.cpp文件。文件名是FCustomTitleWidget.h和FCustomTitleWidget.cpp
git地址
https://gitee.com/zmf199785/csdncode.git
代码在FCustomTitleWidget 目录中