• [QT] 无边框窗体改变大小 完美实现


    近期,做项目用到无边框窗体,令人蛋疼的是无边框窗体大小的改变要像右边框那样,上下左右四周,而且要流畅。

    网上也找了些代码,发现居然还要连接到windows事件,这显然不合常理,后来自己新建了demo,写了一个小时,问题太多了,扔一边先不管了。

    今天需要改进UI界面,没办法了,重新整理了下思路,没想到做出来了。下面来分享下实现的过程,也许是菜鸟专栏,高手勿喷~

    1.首先大家要了解各九宫格的概念

    一个窗体可以被划分为上、下、左、右、左上、左下、右上、右下、中间,除了中间部分,其他都需要写程序处理。

    在程序中定义Padding 为2,并同时定义枚举类型。

    #define PADDING 2
    enum Direction { UP=0, DOWN=1, LEFT, RIGHT, LEFTTOP, LEFTBOTTOM, RIGHTBOTTOM, RIGHTTOP, NONE };

    2.定义一个方法,参数为鼠标目前的全局位置。

    void Dialog::region(const QPoint &cursorGlobalPoint)
    {
      // 获取窗体在屏幕上的位置区域,tl为topleft点,rb为rightbottom点 QRect rect
    = this->rect(); QPoint tl = mapToGlobal(rect.topLeft()); QPoint rb = mapToGlobal(rect.bottomRight());
    int x = cursorGlobalPoint.x(); int y = cursorGlobalPoint.y(); if(tl.x() + PADDING >= x && tl.x() <= x && tl.y() + PADDING >= y && tl.y() <= y) { // 左上角 dir = LEFTTOP; this->setCursor(QCursor(Qt::SizeFDiagCursor)); // 设置鼠标形状 } else if(x >= rb.x() - PADDING && x <= rb.x() && y >= rb.y() - PADDING && y <= rb.y()) { // 右下角 dir = RIGHTBOTTOM; this->setCursor(QCursor(Qt::SizeFDiagCursor)); } else if(x <= tl.x() + PADDING && x >= tl.x() && y >= rb.y() - PADDING && y <= rb.y()) { //左下角 dir = LEFTBOTTOM; this->setCursor(QCursor(Qt::SizeBDiagCursor)); } else if(x <= rb.x() && x >= rb.x() - PADDING && y >= tl.y() && y <= tl.y() + PADDING) { // 右上角 dir = RIGHTTOP; this->setCursor(QCursor(Qt::SizeBDiagCursor)); } else if(x <= tl.x() + PADDING && x >= tl.x()) { // 左边 dir = LEFT; this->setCursor(QCursor(Qt::SizeHorCursor)); } else if( x <= rb.x() && x >= rb.x() - PADDING) { // 右边 dir = RIGHT; this->setCursor(QCursor(Qt::SizeHorCursor)); }else if(y >= tl.y() && y <= tl.y() + PADDING){ // 上边 dir = UP; this->setCursor(QCursor(Qt::SizeVerCursor)); } else if(y <= rb.y() && y >= rb.y() - PADDING) { // 下边 dir = DOWN; this->setCursor(QCursor(Qt::SizeVerCursor)); }else { // 默认 dir = NONE; this->setCursor(QCursor(Qt::ArrowCursor)); } }

    3.在对话框类中定义几个私有成员变量

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

    编写对话框构造函数,初始化几个变量并做一些其他工作。

    isLeftPressDown = false;
    this->dir = NONE;
    this->setMinimumHeight(100);
    this->setMinimumWidth(150);
    this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowSystemMenuHint); // 设置成无边框对话框
    this->setMouseTracking(true);                    // 追踪鼠标
    this->setStyleSheet("QDialog{background:url(:/bg_main.png)}"); // 设置样式背景色,可有可无

    4.接着就要实现几个重要的重载事件了

    void mouseReleaseEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);

    实现过程如下:

    void Dialog::mouseReleaseEvent(QMouseEvent *event)
    {
        if(event->button() == Qt::LeftButton) {
            isLeftPressDown = false;
            if(dir != NONE) {
                this->releaseMouse();
                this->setCursor(QCursor(Qt::ArrowCursor));
            }
        }
    }
    
    void Dialog::mousePressEvent(QMouseEvent *event)
    {
        switch(event->button()) {
        case Qt::LeftButton:
            isLeftPressDown = true;
            if(dir != NONE) {
                this->mouseGrabber();
            } else {
                dragPosition = event->globalPos() - this->frameGeometry().topLeft();
            }
            break;
        case Qt::RightButton:
            this->close();
            break;
        default:
            QDialog::mousePressEvent(event);
        }
    
    }
    
    void Dialog::mouseMoveEvent(QMouseEvent *event)
    {
        QPoint gloPoint = event->globalPos();
        QRect rect = this->rect();
        QPoint tl = mapToGlobal(rect.topLeft());
        QPoint rb = mapToGlobal(rect.bottomRight());
    
        if(!isLeftPressDown) {
            this->region(gloPoint);
        } else {
    
            if(dir != NONE) {
                QRect rMove(tl, rb);
    
                switch(dir) {
                case LEFT:
                    if(rb.x() - gloPoint.x() <= this->minimumWidth())
                        rMove.setX(tl.x());
                    else
                        rMove.setX(gloPoint.x());
                    break;
                case RIGHT:
                    rMove.setWidth(gloPoint.x() - tl.x());
                    break;
                case UP:
                    if(rb.y() - gloPoint.y() <= this->minimumHeight())
                        rMove.setY(tl.y());
                    else
                        rMove.setY(gloPoint.y());
                    break;
                case DOWN:
                    rMove.setHeight(gloPoint.y() - tl.y());
                    break;
                case LEFTTOP:
                    if(rb.x() - gloPoint.x() <= this->minimumWidth())
                        rMove.setX(tl.x());
                    else
                        rMove.setX(gloPoint.x());
                    if(rb.y() - gloPoint.y() <= this->minimumHeight())
                        rMove.setY(tl.y());
                    else
                        rMove.setY(gloPoint.y());
                    break;
                case RIGHTTOP:
                    rMove.setWidth(gloPoint.x() - tl.x());
                    rMove.setY(gloPoint.y());
                    break;
                case LEFTBOTTOM:
                    rMove.setX(gloPoint.x());
                    rMove.setHeight(gloPoint.y() - tl.y());
                    break;
                case RIGHTBOTTOM:
                    rMove.setWidth(gloPoint.x() - tl.x());
                    rMove.setHeight(gloPoint.y() - tl.y());
                    break;
                default:
                    break;
                }
                this->setGeometry(rMove);
            } else {
                move(event->globalPos() - dragPosition);
                event->accept();
            }
        }
        QDialog::mouseMoveEvent(event);
    }

    到此为止,一个无边框窗体拖动并且改变大小的功能就实现了,Run and debug it。

    总结起来,这个算法其实并不复杂,就看有几个点能不能想到:

    1)窗体矩形区域要转换成在屏幕上的区域,我采取的方式就是取TopLeft和RightBottom两个点来确定这个区域。

    2)鼠标移动要去全局的坐标。

    3)region函数中判断坐标区间,然后改变鼠标形状,这块很容易出错,如果你一下子就写出来,那我真的很佩服。

    这个纠结的网上都没有现成代码的问题,终于在今天圆满解决了。如需运行,请下载实例代码:SizableNoFrame.rar

    转载请注明出处哦:http://www.cnblogs.com/xufeiyang/p/3313104.html

  • 相关阅读:
    YTU 2543: 数字整除
    YTU 2542: 弟弟的作业
    YTU 2541: 汽水瓶
    YTU 2535: C++复数运算符重载(+与<<)
    YTU 2530: 小勇玩lol
    YTU 2520: 小慧唱卡拉OK
    YTU 2517: 打倒魔王↖(^ω^)↗
    YTU 2516: 剪刀石头布
    reload、replace、href、assign、window.history.go(0)的区别
    js 数组排序sort方法
  • 原文地址:https://www.cnblogs.com/xufeiyang/p/3313104.html
Copyright © 2020-2023  润新知