效果如上所示, 系统默认的标题栏不太好看,如果要自定义样式的话还是要自己重写无边框的widget,但是自己写的没有缩放功能,这里就介绍如何自己实现拉伸功能
目录
1.重写FramelessWidget,继承于QWidget,我们需要定义一些枚举值来判断当前鼠标位置
FramelessWidget
1.重写FramelessWidget,继承于QWidget,我们需要定义一些枚举值来判断当前鼠标位置
enum EAreaType
{
Area_Invalid, // 窗口范围外
Area_Middle, // 窗口中间区域
Area_Left, // 窗口左边拉伸区域
Area_Right, // 窗口右边拉伸区域
Area_Top, // 窗口上方拉伸区域
Area_TopLeft, // 窗口左上角拉伸区域
Area_TopRight, // 窗口右上角拉伸区域
Area_Bottom, // 窗口底部拉伸区域
Area_BottomLeft, // 窗口左下角拉伸区域
Area_BottomRight, // 窗口右下角拉伸区域
};
头文件
#ifndef FRAMELESSWIDGET_H
#define FRAMELESSWIDGET_H
#include <QWidget>
#include <Windows.h>
#include <QDebug>
class FramelessWidget : public QWidget
{
Q_OBJECT
public:
explicit FramelessWidget(QWidget *parent = nullptr);
~FramelessWidget();
enum EAreaType
{
Area_Invalid, // 窗口范围外
Area_Middle, // 窗口中间区域
Area_Left, // 窗口左边拉伸区域
Area_Right, // 窗口右边拉伸区域
Area_Top, // 窗口上方拉伸区域
Area_TopLeft, // 窗口左上角拉伸区域
Area_TopRight, // 窗口右上角拉伸区域
Area_Bottom, // 窗口底部拉伸区域
Area_BottomLeft, // 窗口左下角拉伸区域
Area_BottomRight, // 窗口右下角拉伸区域
};
void setStrethDelta(int delta);// 设置拉伸区域大小(距离窗口边缘多少个像素)
void setMinAndMaxSize(QSize minSize,QSize maxSize);
protected:
void mousePressEvent(QMouseEvent* e) override; // 重写鼠标按下事件
void mouseReleaseEvent(QMouseEvent* e) override;// 重写鼠释放事件
void mouseMoveEvent(QMouseEvent* e) override; // 重写鼠标移动事件
void mouseMove(QPoint point);
private:
EAreaType getAreaType(QPoint);// 获取位置所在的鼠标区域类型
void printfAreaType(EAreaType);
signals:
void resizeWindow(QRect rect);
void moveWindow(QPoint point);
private:
int _strethArea{};// 在离窗口边缘多少个像素会触发拉伸
QPoint _clickPos;// 鼠标左键点击位置
bool isClicked = false;
Qt::CursorShape _cursorShape;// 记录原始鼠标类型
EAreaType _areaType{Area_Invalid};// 鼠标当前所在区域类型
bool isMaxWindow = true;//记录是否最大窗口
};
#endif // FRAMEWIDGET_H
2.设置鼠标追踪,并且设置距离窗口边缘多少个像素的时候触发
鼠标追踪这个非常重要,每一个子控件都需要设置,不然会出现鼠标移动进来样式没有恢复的情况,我这里设置距离3个像素的时候触发判断
setMouseTracking(true);// 设置鼠标跟踪,不然只会在鼠标按下时才会触发鼠标移动事件
setStrethDelta(3);
3.判断鼠标类型,当前处在什么位置
通过计算鼠标坐标距离边距的位置,设置对应的鼠标类型
FramelessWidget::EAreaType FramelessWidget::getAreaType(QPoint point)
{
bool bTop = (point.y() <= _strethArea && point.y() >= -_strethArea);
bool bBottom = (point.y() >= height()-_strethArea && point.y() <= height()+_strethArea);
bool bLeft = (point.x() <= _strethArea && point.x() >= -_strethArea);
bool bRight = (point.x() >= width()-_strethArea && point.x() <= width()+_strethArea);
bool bVMid = (point.y() > _strethArea && point.y() < height()-_strethArea);
bool bHMid = (point.x() > _strethArea && point.x() < width()-_strethArea);
if (bHMid)
{
if (bVMid)
return Area_Middle;
else if (bTop)
return Area_Top;
else if (bBottom)
return Area_Bottom;
}
else if (bLeft)
{
if (bVMid)
return Area_Left;
else if (bTop)
return Area_TopLeft;
else if (bBottom)
return Area_BottomLeft;
}
else if (bRight)
{
if (bVMid)
return Area_Right;
else if (bTop)
return Area_TopRight;
else if (bBottom)
return Area_BottomRight;
}
return Area_Invalid;
}
4.鼠标按下的时候记录坐标
void FramelessWidget::mousePressEvent(QMouseEvent *e)
{
this->setFocus();
if(e->button()==Qt::LeftButton)
{
_clickPos=e->pos();
}
}
5.根据鼠标类型改变鼠标样式
如果为鼠标左键,则判断为鼠标按下并且拖动的情况,否则则是鼠标移动只改变样式
void FramelessWidget::mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() & Qt::LeftButton)
{
mouseMove(e->pos());
}
else
{
_areaType = getAreaType(e->pos());
// printfAreaType(_areaType);
switch (_areaType)
{
case Area_TopLeft: {
this->setCursor(Qt::SizeFDiagCursor);
}break;
case Area_Left: {
this->setCursor(Qt::SizeHorCursor);
}break;
case Area_BottomLeft: {
this->setCursor(Qt::SizeBDiagCursor);
}break;
case Area_TopRight: {
this->setCursor(Qt::SizeBDiagCursor);
}break;
case Area_Right: {
this->setCursor(Qt::SizeHorCursor);
}break;
case Area_BottomRight: {
this->setCursor(Qt::SizeFDiagCursor);
}break;
case Area_Top: {
this->setCursor(Qt::SizeVerCursor);
}break;
case Area_Middle: {
this->setCursor(Qt::ArrowCursor);
}break;
case Area_Bottom: {
this->setCursor(Qt::SizeVerCursor);
}break;
default: {
this->setCursor(Qt::ArrowCursor);
}break;
}
}
}
6.设置窗口最大最小值
void FramelessWidget::setMinAndMaxSize(QSize minSize,QSize maxSize)
{
setMinimumSize(minSize);
setMaximumSize(maxSize);
}
7.拉伸窗口
这里加上了最大最小值的判断,让窗口不会无限增大和缩小
void FramelessWidget::mouseMove(QPoint point)
{
QPoint globalPoint = mapToGlobal(QPoint(this->geometry().x(),this->geometry().y()));
QRect rect(globalPoint.x(),globalPoint.y(),width(),height());
QPoint delta = point - _clickPos;
switch (_areaType)
{
case Area_Middle: {
// move(pos() + delta);
// emit moveWindow(globalPoint + delta);
} break;
case Area_Left: {
int newWidth = rect.width() - delta.x();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setLeft(rect.left() + delta.x());
rect.setWidth(newWidth);
emit resizeWindow(rect);
}
} break;
case Area_Right: {
int newWidth = rect.width() + delta.x();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setRight(rect.right() + delta.x());
rect.setWidth(newWidth);
_clickPos.setX(_clickPos.x() + delta.x());
emit resizeWindow(rect);
}
} break;
case Area_Top: {
int newHeight = rect.height() - delta.y();
if (newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setTop(rect.top() + delta.y());
rect.setHeight(newHeight);
emit resizeWindow(rect);
}
} break;
case Area_Bottom: {
int newHeight = rect.height() + delta.y();
if (newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setBottom(rect.bottom() + delta.y());
rect.setHeight(newHeight);
_clickPos.setY(_clickPos.y() + delta.y());
emit resizeWindow(rect);
}
} break;
case Area_TopLeft: {
int newWidth = rect.width() - delta.x();
int newHeight = rect.height() - delta.y();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setLeft(rect.left() + delta.x());
rect.setWidth(newWidth);
}
if(newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setTop(rect.top() + delta.y());
rect.setHeight(newHeight);
}
emit resizeWindow(rect);
} break;
case Area_TopRight: {
int newWidth = rect.width() + delta.x();
int newHeight = rect.height() - delta.y();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setRight(rect.right() + delta.x());
rect.setWidth(newWidth);
_clickPos.setX(_clickPos.x() + delta.x());
}
if(newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setTop(rect.top() + delta.y());
rect.setHeight(newHeight);
}
emit resizeWindow(rect);
}break;
case Area_BottomLeft: {
int newWidth = rect.width() - delta.x();
int newHeight = rect.height() + delta.y();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setLeft(rect.left() + delta.x());
rect.setWidth(newWidth);
}
if(newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setBottom(rect.bottom() + delta.y());
rect.setHeight(newHeight);
_clickPos.setY(_clickPos.y() + delta.y());
}
emit resizeWindow(rect);
}break;
case Area_BottomRight: {
int newWidth = rect.width() + delta.x();
int newHeight = rect.height() + delta.y();
if (newWidth >= minimumWidth() && newWidth <= maximumWidth())
{
rect.setRight(rect.right() + delta.x());
rect.setWidth(newWidth);
_clickPos.setX(_clickPos.x() + delta.x());
}
if(newHeight >= minimumHeight() && newHeight <= maximumHeight())
{
rect.setBottom(rect.bottom() + delta.y());
rect.setHeight(newHeight);
_clickPos.setY(_clickPos.y() + delta.y());
}
emit resizeWindow(rect);
}break;
default: {
this->setCursor(Qt::ArrowCursor);
} break;
}
}
8.鼠标离开窗口范围
需要将鼠标恢复默认样式
void FramelessWidget::mouseReleaseEvent(QMouseEvent *e)
{
if(e->button()==Qt::LeftButton)
{
_clickPos = e->pos();
this->setCursor(Qt::ArrowCursor);
_areaType = Area_Middle;
}
else
{
_areaType = getAreaType(e->pos());
// printfAreaType(_areaType);
switch (_areaType)
{
case Area_TopLeft: {
this->setCursor(Qt::SizeFDiagCursor);
}break;
case Area_Left: {
this->setCursor(Qt::SizeHorCursor);
}break;
case Area_BottomLeft: {
this->setCursor(Qt::SizeBDiagCursor);
}break;
case Area_TopRight: {
this->setCursor(Qt::SizeBDiagCursor);
}break;
case Area_Right: {
this->setCursor(Qt::SizeHorCursor);
}break;
case Area_BottomRight: {
this->setCursor(Qt::SizeFDiagCursor);
}break;
case Area_Top: {
this->setCursor(Qt::SizeVerCursor);
}break;
case Area_Middle: {
this->setCursor(Qt::ArrowCursor);
}break;
case Area_Bottom: {
this->setCursor(Qt::SizeVerCursor);
}break;
default: {
this->setCursor(Qt::ArrowCursor);
}break;
}
}
}
MainWindow
1.初始化
将ui中的centralwidget提升为FramelessWidget,并且设置窗口为无边框窗口
void MainWindow::initUi()
{
ui->title_lab->installEventFilter(this);
QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
// top-padding:5 bottom-padding:5
int maxHeight = availableGeometry.height()-(ui->title_widget->height()+10);
int maxWidth = 1920;
QSize maxSize(1920,availableGeometry.height());
QSize minSize(800,(availableGeometry.height()/2));// 1/4屏幕
setMaximumSize(maxSize);
setMinimumSize(minSize);
ui->centralwidget->setMinAndMaxSize(minSize,maxSize);
ui->scrollAreaWidgetContents->setFixedSize(maxWidth,maxHeight);
resize(maximumSize());
setWindowFlags(Qt::FramelessWindowHint);//无边框
connect(ui->centralwidget,&FramelessWidget::resizeWindow,this,[=](QRect rect){setGeometry(rect);});
}
2.实现窗口拖动
初始化的时候给标题安装了事件管理器,这里在事件管理器中实现窗口移动,以及双击最大化或恢复正常窗口
bool MainWindow::eventFilter(QObject *object, QEvent *evt)
{
static QPoint mousePoint;
static bool mousePressed = false;
QMouseEvent *event = static_cast<QMouseEvent *>(evt);
if(object->objectName()=="title_lab" && event->type() == QEvent::MouseButtonPress)
{
if (event->button() == Qt::LeftButton)
{
mousePressed = true;
mousePoint = event->globalPos() - this->pos();
}
}
else if (object->objectName()=="title_lab" && event->type() == QEvent::MouseButtonRelease)
{
mousePressed = false;
}
else if (object->objectName()=="title_lab" && event->type() == QEvent::MouseMove)
{
if (mousePressed && (event->buttons() & Qt::LeftButton))
{
this->move(event->globalPos() - mousePoint);
}
}
else if (object->objectName()=="title_lab" && event->type() == QEvent::MouseButtonDblClick)
{
on_max_bt_clicked();
}
return QWidget::eventFilter(object, event);
}
3.最大化按钮
判断窗口是否是最大化的状态,不是的话就最大化,是的话就恢复默认
void MainWindow::on_max_bt_clicked()
{
if(isMaxWindow)
{
// 获取屏幕的几何信息
QScreen *primaryScreen = QGuiApplication::primaryScreen();
QRect screenGeometry = primaryScreen->geometry();
// 计算主窗口的位置
int x = (screenGeometry.width() - 1280) / 2 ;
int y = (screenGeometry.height() - 720) / 2 ;
setGeometry(x,y,1280,720);
ui->scrollArea->horizontalScrollBar()->setValue(ui->scrollArea->horizontalScrollBar()->maximum()/2);
}
else
{
setGeometry(0,0,maximumWidth(),maximumHeight());
}
}
4.关联窗口大小改变事件
void MainWindow::resizeEvent(QResizeEvent *event)
{
int maxHeight = QGuiApplication::primaryScreen()->availableGeometry().height() - (ui->title_widget->height() + 10) ;
ui->max_bt->setIcon((width() < maximumWidth() || height() < maximumHeight()) ? QIcon(":/pic/max_window_white.png") : QIcon(":/pic/white_window.png"));
isMaxWindow = (size() == maximumSize());
if(isMaxWindow)ui->max_bt->setText("还原");
else ui->max_bt->setText("最大化");
ui->scrollArea->setHorizontalScrollBarPolicy( (width() < 1920 ) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
ui->scrollArea->setVerticalScrollBarPolicy((height() < maxHeight) ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
}
注意事项
- 如果没有设置setMouseTracking(true);的话,鼠标会出现样式不对的情况,所有的子控件都要设置
- 右边拉伸和右下角拉伸窗口不会有抖动,但是左上角的坐标如果改变了就可能会有抖动,这好像是qt自己的原因,不过就算用其他软件拉伸的时候也会有这个抖动现象