Qt之无边框窗口创建

为什么要去边框?


美观

个人感觉系统自带的边框美观上稍微欠缺一点,这也是好多软件去掉边框的原因吧。

自定义

去掉边框后,就能自由在任何位置添加一个自己的边框、标题栏之类的,甚至可以在上面添加一些工具菜单。


如何去掉边框?


Qt自带的函数

使用下面这个函数就能去掉边框:

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);

//第一个参数是设置无边框。第二个参数是允许任务栏按钮右键菜单,第三个参数是允许最小化与还原。

使用第一个参数即可。

遇到问题

去掉边框后,发现鼠标也无法移动窗口,也无法自由调整大小。要说最小最大化、关闭的话,这个可以添加两个按钮来实现。
于是,我网上开始查找一些资料、自己也思考了一下,大概如下解决:

关于移动窗口

这个自然是重写鼠标点击、移动事件了。

步骤一:鼠标点击,记录鼠标点击时的位置。
步骤二:鼠标移动,根据鼠标当前的位置和起始位置,计算出鼠标的位移,然后设置窗口的新位置。

关于自由调整大小

这个是比较麻烦的,网上找了好多资料,都没有完美的实现方法。

网上比较多的是下面两种做法:

一种是调用Windows API来实现,但是这样就失去了跨平台的意义。

一种是通过重写鼠标的region和move事件来实现,但是有些小BUG。

不完美的实现

通过第二种方法和我的调试以及一些思考,提出以下做法:

条件一:首先,鼠标region窗体时,而且距离边框进入某个阈值PADDING时,此时被认为可以自动调整窗体大小。而且此效果优先于移动窗口的效果。

条件二:满足第一步的情况下,按下鼠标左键,并且移动鼠标,将改变窗体的大小。不过根据鼠标的位置会朝不同的方向改变。比如在窗体左边界附近,那么只会左侧的窗体扩展或者收缩。

在前面两个条件下,确实能满足自由调整大小,但是会出现一个问题。在主窗体中,某些子组件在鼠标进入时,主窗体会失去对鼠标的检测。可是在鼠标点击的时候,主窗体又会再次获得鼠标的检测。所以会出现,当鼠标进入主窗体,激活region效果,然后鼠标进入子组件后,鼠标位移了好长一段距离,主窗体的region效果仍然存在,此时如果鼠标左击,同时触发第二个条件,然后窗体一下子大小就变了。以上是我进过反复调试得到的结论。

然后考虑到出现此BUG的最主要缘故是,子组件获得鼠标移动的检测时,主窗体会丢失鼠标的检测。但是网上找了好多方法都没有解决这个问题的,网上有些说设置mouseTracking可以解决,但是有些组件可以解决,有些组件无效。

于是我想了一个办法:既然无法解决鼠标的事情,就考虑重置region那个效果。当子组件获得鼠标检测的时候,也就是mouseenter的时候,会发出一个mouseenter的信号,然后父组件接收这个信号的时候,触发losemouse事件,重置region效果。这样只需要对每个边界处的子组件,编写mouseenter事件就可以解决上面的BUG了。

具体代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>

//PADDING用于设置改变窗口时鼠标侦测的边距
#define PADDING 4
enum Direction
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
    LEFTTOP,
    LEFTBOTTOM,
    RIGHTBOTTOM,
    RIGHTTOP,
    NONE
};

class MainWindow : public QWidget
{
        Q_OBJECT
    public:
        explicit MainWindow(QWidget *parent = 0);

    public slots:
        void loseMouse();//当鼠标焦点被子组件获取时,需要还原鼠标形状和changeDir。
        void setMaxSize();

    private:
        void region(const QPoint &cursorGlobalPoint);
        void mouseReleaseEvent(QMouseEvent *event);
        void mouseMoveEvent(QMouseEvent *event);
        void mousePressEvent(QMouseEvent *event);



    protected:
        bool isMaxSize;
        bool isLeftPressDown;//判断左键是否按下
        QPoint dragPosition;//窗口移动拖动时需要记住的点
        Direction changeDir;//窗口大小改变时,记录改变方向
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QMouseEvent>
#include <QApplication>
#include <QDesktopWidget>

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    resize(970, 700);
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
    //第一个参数是设置无边框。第二个参数是允许任务栏按钮右键菜单,第三个参数是允许最小化与还原。

    isLeftPressDown = false;
    isMaxSize = false;
    changeDir = NONE;
    setMaximumWidth(QApplication::desktop()->width());
    setMaximumHeight(QApplication::desktop()->height());
    setMinimumHeight(570);
    setMinimumWidth(985);
    setMouseTracking(true);//追踪鼠标
    //this->setStyleSheet("QDialog{background:url(:/bg_main.png)}");//设置样式背景色,可有可无

    this->setFocusPolicy(Qt::ClickFocus);//主窗口设置鼠标点击焦点,新建歌单时有用
}

void MainWindow::setMaxSize()
{
    if (this->isMaxSize)
    {
        this->isMaxSize = false;
        this->showNormal();
    }
    else
    {
        this->isMaxSize = true;
        this->showMaximized();
    }
}

void MainWindow::region(const QPoint &cursorGlobalPoint)
{
    if (isMaxSize)
        return;
    //获取窗体在屏幕上的位置区域,tl为topleft点,rb为rightbottom点
    QRect rect = this->rect();
    QPoint topleft = mapToGlobal(rect.topLeft());
    QPoint rightbottom = mapToGlobal(rect.bottomRight());

    int x = cursorGlobalPoint.x();
    int y = cursorGlobalPoint.y();

    if(topleft.x()+PADDING >= x &&
        topleft.x() <= x &&
        topleft.y()+PADDING >= y &&
        topleft.y() <= y)
    {
        // 左上角
        changeDir = LEFTTOP;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));//设置鼠标形状
    }
    else if(x >= rightbottom.x()-PADDING &&
        x <= rightbottom.x() &&
        y >= rightbottom.y()-PADDING &&
        y <= rightbottom.y())
    {
        // 右下角
        changeDir = RIGHTBOTTOM;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    }
    else if(x <= topleft.x()+PADDING &&
        x >= topleft.x() &&
        y >= rightbottom.y()-PADDING &&
        y <= rightbottom.y())
    {
        //左下角
        changeDir = LEFTBOTTOM;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    }
    else if(x <= rightbottom.x() &&
        x >= rightbottom.x()-PADDING &&
        y >= topleft.y() &&
        y <= topleft.y()+PADDING)
    {
        // 右上角
        changeDir = RIGHTTOP;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    }
    else if(x <= topleft.x()+PADDING &&
        x >= topleft.x())
    {
        // 左边
        changeDir = LEFT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    }
    else if(x <= rightbottom.x() &&
        x >= rightbottom.x()-PADDING)
    {
        // 右边
        changeDir = RIGHT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    }
    else if(y >= topleft.y() &&
        y <= topleft.y()+PADDING)
    {
        // 上边
        changeDir = UP;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    }
    else if(y <= rightbottom.y() &&
        y >= rightbottom.y()-PADDING)
    {
        // 下边
        changeDir = DOWN;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    }
    else
    {
        // 默认
        changeDir = NONE;
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void MainWindow::loseMouse()
{
    changeDir = NONE;
    this->setCursor(QCursor(Qt::ArrowCursor));
}

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if (isMaxSize)
        return;
    if(event->button() == Qt::LeftButton)
    {
        isLeftPressDown = false;
        if(changeDir != NONE)
        {
            this->releaseMouse();
            this->setCursor(QCursor(Qt::ArrowCursor));
        }
    }
}

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if (isMaxSize)
        return;
    if (event->button() == Qt::LeftButton)
    {
        isLeftPressDown = true;
        if(changeDir != NONE)
            this->mouseGrabber();
        else
            dragPosition = event->globalPos() - this->frameGeometry().topLeft();
    }
    else
        QWidget::mousePressEvent(event);
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if (isMaxSize)
        return;
    QPoint globalPoint = event->globalPos();
    QRect rect = this->rect();
    QPoint topleft = mapToGlobal(rect.topLeft());
    QPoint bottomright = mapToGlobal(rect.bottomRight());

    if(!isLeftPressDown)
    {
        this->region(globalPoint);
    }
    else if(changeDir != NONE)
    {
        QRect rMove(topleft, bottomright);
        switch(changeDir)
        {
            case LEFT:
                if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
                    rMove.setX(topleft.x());
                else
                    rMove.setX(globalPoint.x());
                break;
            case RIGHT:
                rMove.setWidth(globalPoint.x() - topleft.x());
                break;
            case UP:
                if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
                    rMove.setY(topleft.y());
                else
                    rMove.setY(globalPoint.y());
                break;
            case DOWN:
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            case LEFTTOP:
                if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
                    rMove.setX(topleft.x());
                else
                    rMove.setX(globalPoint.x());
                if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
                    rMove.setY(topleft.y());
                else
                    rMove.setY(globalPoint.y());
                break;
            case RIGHTTOP:
                rMove.setWidth(globalPoint.x() - topleft.x());
                rMove.setY(globalPoint.y());
                break;
            case LEFTBOTTOM:
                rMove.setX(globalPoint.x());
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            case RIGHTBOTTOM:
                rMove.setWidth(globalPoint.x() - topleft.x());
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            default:
                break;
        }
        this->setGeometry(rMove);
    }
    else
    {
        move(event->globalPos() - dragPosition);
        //event->accept();
    }
    QWidget::mouseMoveEvent(event);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值