zoukankan      html  css  js  c++  java
  • Qt之自定义界面(窗体缩放-跨平台终极版)

    简述

    通过上一节内容,我们实现了窗体的缩放,功能很不错,但是很遗憾-不支持跨平台!如果对于多平台来说,这是一个硬伤,所以,我们急需要一个能够支持跨平台的实现方案。

    在网上看到过很多不同的实现方式,多多少少会存在一些问题-要么融合度太高、要么不能很好地进行移动、缩放。基于前人的分享与总结,最后,我花了很长时间来完善。独乐乐不如众乐乐,既然纯开源-那就全部分享出来。

    效果

    这里写图片描述

    窗体缩放

    实现

    frameless_helper.h

    #ifndef FRAMELESS_HELPER_H
    #define FRAMELESS_HELPER_H
    
    #include <QObject>
    
    class QWidget;
    class FramelessHelperPrivate;
    
    class FramelessHelper : public QObject
    {
        Q_OBJECT
    
    public:
        explicit FramelessHelper(QObject *parent = 0);
        ~FramelessHelper();
        // 激活窗体
        void activateOn(QWidget *topLevelWidget);
        // 移除窗体
        void removeFrom(QWidget *topLevelWidget);
        // 设置窗体移动
        void setWidgetMovable(bool movable);
        // 设置窗体缩放
        void setWidgetResizable(bool resizable);
        // 设置橡皮筋移动
        void setRubberBandOnMove(bool movable);
        // 设置橡皮筋缩放
        void setRubberBandOnResize(bool resizable);
        // 设置边框的宽度
        void setBorderWidth(uint width);
        // 设置标题栏高度
        void setTitleHeight(uint height);
        bool widgetResizable();
        bool widgetMovable();
        bool rubberBandOnMove();
        bool rubberBandOnResisze();
        uint borderWidth();
        uint titleHeight();
    
    protected:
        // 事件过滤,进行移动、缩放等
        virtual bool eventFilter(QObject *obj, QEvent *event);
    
    private:
        FramelessHelperPrivate *d;
    };
    
    #endif //FRAMELESS_HELPER_H
    

    frameless_helper.cpp

    FramelessHelperPrivate类

    /*****
     * FramelessHelperPrivate
     * 存储界面对应的数据集合,以及是否可移动、可缩放属性
    *****/
    class FramelessHelperPrivate
    {
    public:
        QHash<QWidget*, WidgetData*> m_widgetDataHash;
        bool m_bWidgetMovable        : true;
        bool m_bWidgetResizable      : true;
        bool m_bRubberBandOnResize   : true;
        bool m_bRubberBandOnMove     : true;
    };

    CursorPosCalculator类

    /*****
     * CursorPosCalculator
     * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角
    *****/
    class CursorPosCalculator
    {
    public:
        explicit CursorPosCalculator();
        void reset();
        void recalculate(const QPoint &globalMousePos, const QRect &frameRect);
    
    public:
        bool m_bOnEdges              : true;
        bool m_bOnLeftEdge           : true;
        bool m_bOnRightEdge          : true;
        bool m_bOnTopEdge            : true;
        bool m_bOnBottomEdge         : true;
        bool m_bOnTopLeftEdge        : true;
        bool m_bOnBottomLeftEdge     : true;
        bool m_bOnTopRightEdge       : true;
        bool m_bOnBottomRightEdge    : true;
    
        static int m_nBorderWidth;
        static int m_nTitleHeight;
    };
    int CursorPosCalculator::m_nBorderWidth = 5;
    int CursorPosCalculator::m_nTitleHeight = 30;
    
    /***** CursorPosCalculator *****/
    CursorPosCalculator::CursorPosCalculator()
    {
        reset();
    }
    
    void CursorPosCalculator::reset()
    {
        m_bOnEdges = false;
        m_bOnLeftEdge = false;
        m_bOnRightEdge = false;
        m_bOnTopEdge = false;
        m_bOnBottomEdge = false;
        m_bOnTopLeftEdge = false;
        m_bOnBottomLeftEdge = false;
        m_bOnTopRightEdge  = false;
        m_bOnBottomRightEdge = false;
    }
    
    void CursorPosCalculator::recalculate(const QPoint &gMousePos, const QRect &frameRect)
    {
        int globalMouseX = gMousePos.x();
        int globalMouseY = gMousePos.y();
    
        int frameX = frameRect.x();
        int frameY = frameRect.y();
    
        int frameWidth = frameRect.width();
        int frameHeight = frameRect.height();
    
        m_bOnLeftEdge = (globalMouseX >= frameX &&
                      globalMouseX <= frameX + m_nBorderWidth );
    
    
        m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth &&
                       globalMouseX <= frameX + frameWidth);
    
        m_bOnTopEdge = (globalMouseY >= frameY &&
                     globalMouseY <= frameY + m_nBorderWidth );
    
        m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth &&
                        globalMouseY <= frameY + frameHeight);
    
        m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
        m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
        m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
        m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;
    
        m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
    }

    WidgetData类

    /*****
     * WidgetData
     * 更新鼠标样式、移动窗体、缩放窗体
    *****/
    class WidgetData
    {
    public:
        explicit WidgetData(FramelessHelperPrivate *d, QWidget *pTopLevelWidget);
        ~WidgetData();
        QWidget* widget();
        // 处理鼠标事件-划过、按下、释放、移动
        void handleWidgetEvent(QEvent *event);
        // 更新橡皮筋状态
        void updateRubberBandStatus();
    
    private:
        // 更新鼠标样式
        void updateCursorShape(const QPoint &gMousePos);
        // 重置窗体大小
        void resizeWidget(const QPoint &gMousePos);
        // 移动窗体
        void moveWidget(const QPoint &gMousePos);
        // 处理鼠标按下
        void handleMousePressEvent(QMouseEvent *event);
        // 处理鼠标释放
        void handleMouseReleaseEvent(QMouseEvent *event);
        // 处理鼠标移动
        void handleMouseMoveEvent(QMouseEvent *event);
        // 处理鼠标离开
        void handleLeaveEvent(QEvent *event);
        // 处理鼠标进入
        void handleHoverMoveEvent(QHoverEvent *event);
    
    private:
        FramelessHelperPrivate *d;
        QRubberBand *m_pRubberBand;
        QWidget *m_pWidget;
        QPoint m_ptDragPos;
        CursorPosCalculator m_pressedMousePos;
        CursorPosCalculator m_moveMousePos;
        bool m_bLeftButtonPressed;
        bool m_bCursorShapeChanged;
        bool m_bLeftButtonTitlePressed;
        Qt::WindowFlags m_windowFlags;
    };
    /***** WidgetData *****/
    WidgetData::WidgetData(FramelessHelperPrivate *_d, QWidget *pTopLevelWidget)
    {
        d = _d;
        m_pWidget = pTopLevelWidget;
        m_bLeftButtonPressed = false;
        m_bCursorShapeChanged = false;
        m_bLeftButtonTitlePressed = false;
        m_pRubberBand = NULL;
    
        m_windowFlags = m_pWidget->windowFlags();
        m_pWidget->setMouseTracking(true);
        m_pWidget->setAttribute(Qt::WA_Hover, true);
    
        updateRubberBandStatus();
    }
    
    WidgetData::~WidgetData()
    {
        m_pWidget->setMouseTracking(false);
        m_pWidget->setWindowFlags(m_windowFlags);
        m_pWidget->setAttribute(Qt::WA_Hover, false);
    
        delete m_pRubberBand;
        m_pRubberBand = NULL;
    }
    
    QWidget* WidgetData::widget()
    {
        return m_pWidget;
    }
    
    void WidgetData::handleWidgetEvent(QEvent *event)
    {
        switch (event->type())
        {
        default:
            break;
        case QEvent::MouseButtonPress:
            handleMousePressEvent(static_cast<QMouseEvent*>(event));
            break;
        case QEvent::MouseButtonRelease:
            handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
            break;
        case QEvent::MouseMove:
            handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
            break;
        case QEvent::Leave:
            handleLeaveEvent(static_cast<QMouseEvent*>(event));
            break;
        case QEvent::HoverMove:
            handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
            break;
        }
    }
    
    void WidgetData::updateRubberBandStatus()
    {
        if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize)
        {
            if (NULL == m_pRubberBand)
                m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
        }
        else
        {
            delete m_pRubberBand;
            m_pRubberBand = NULL;
        }
    }
    
    void WidgetData::updateCursorShape(const QPoint &gMousePos)
    {
        if (m_pWidget->isFullScreen() || m_pWidget->isMaximized())
        {
            if (m_bCursorShapeChanged)
            {
                m_pWidget->unsetCursor();
            }
            return;
        }
    
        m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());
    
        if(m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge)
        {
            m_pWidget->setCursor( Qt::SizeFDiagCursor );
            m_bCursorShapeChanged = true;
        }
        else if(m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge)
        {
            m_pWidget->setCursor( Qt::SizeBDiagCursor );
            m_bCursorShapeChanged = true;
        }
        else if(m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge)
        {
            m_pWidget->setCursor( Qt::SizeHorCursor );
            m_bCursorShapeChanged = true;
        }
        else if(m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge)
        {
            m_pWidget->setCursor( Qt::SizeVerCursor );
            m_bCursorShapeChanged = true;
        }
        else
        {
            if (m_bCursorShapeChanged)
            {
                m_pWidget->unsetCursor();
                m_bCursorShapeChanged = false;
            }
        }
    }
    
    void WidgetData::resizeWidget(const QPoint &gMousePos)
    {
        QRect origRect;
    
        if (d->m_bRubberBandOnResize)
            origRect = m_pRubberBand->frameGeometry();
        else
            origRect = m_pWidget->frameGeometry();
    
        int left = origRect.left();
        int top = origRect.top();
        int right = origRect.right();
        int bottom = origRect.bottom();
        origRect.getCoords(&left, &top, &right, &bottom);
    
        int minWidth = m_pWidget->minimumWidth();
        int minHeight = m_pWidget->minimumHeight();
    
        if (m_pressedMousePos.m_bOnTopLeftEdge)
        {
            left = gMousePos.x();
            top = gMousePos.y();
        }
        else if (m_pressedMousePos.m_bOnBottomLeftEdge)
        {
            left = gMousePos.x();
            bottom = gMousePos.y();
        }
        else if (m_pressedMousePos.m_bOnTopRightEdge)
        {
            right = gMousePos.x();
            top = gMousePos.y();
        }
        else if (m_pressedMousePos.m_bOnBottomRightEdge)
        {
            right = gMousePos.x();
            bottom = gMousePos.y();
        }
        else if (m_pressedMousePos.m_bOnLeftEdge)
        {
            left = gMousePos.x();
        }
        else if (m_pressedMousePos.m_bOnRightEdge)
        {
            right = gMousePos.x();
        }
        else if (m_pressedMousePos.m_bOnTopEdge)
        {
            top = gMousePos.y();
        }
        else if (m_pressedMousePos.m_bOnBottomEdge)
        {
            bottom = gMousePos.y();
        }
    
        QRect newRect(QPoint(left, top), QPoint(right, bottom));
    
        if (newRect.isValid())
        {
            if (minWidth > newRect.width())
            {
                if (left != origRect.left())
                    newRect.setLeft(origRect.left());
                else
                    newRect.setRight(origRect.right());
            }
            if (minHeight > newRect.height())
            {
                if (top != origRect.top())
                    newRect.setTop(origRect.top());
                else
                    newRect.setBottom(origRect.bottom());
            }
    
            if (d->m_bRubberBandOnResize)
            {
                m_pRubberBand->setGeometry(newRect);
            }
            else
            {
                m_pWidget->setGeometry(newRect);
            }
        }
    }
    
    void WidgetData::moveWidget(const QPoint& gMousePos)
    {
        if (d->m_bRubberBandOnMove)
        {
            m_pRubberBand->move(gMousePos - m_ptDragPos);
        }
        else
        {
            m_pWidget->move(gMousePos - m_ptDragPos);
        }
    }
    
    void WidgetData::handleMousePressEvent(QMouseEvent *event)
    {
        if (event->button() == Qt::LeftButton)
        {
            m_bLeftButtonPressed = true;
            m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;
    
            QRect frameRect = m_pWidget->frameGeometry();
            m_pressedMousePos.recalculate(event->globalPos(), frameRect);
    
            m_ptDragPos = event->globalPos() - frameRect.topLeft();
    
            if (m_pressedMousePos.m_bOnEdges)
            {
                if (d->m_bRubberBandOnResize)
                {
                    m_pRubberBand->setGeometry(frameRect);
                    m_pRubberBand->show();
                }
            }
            else if (d->m_bRubberBandOnMove)
            {
                m_pRubberBand->setGeometry(frameRect);
                m_pRubberBand->show();
            }
        }
    }
    
    void WidgetData::handleMouseReleaseEvent(QMouseEvent *event)
    {
        if (event->button() == Qt::LeftButton)
        {
            m_bLeftButtonPressed = false;
            m_bLeftButtonTitlePressed = false;
            m_pressedMousePos.reset();
            if (m_pRubberBand && m_pRubberBand->isVisible())
            {
                m_pRubberBand->hide();
                m_pWidget->setGeometry(m_pRubberBand->geometry());
            }
        }
    }
    
    void WidgetData::handleMouseMoveEvent(QMouseEvent *event)
    {
        if (m_bLeftButtonPressed)
        {
            if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges)
            {
                resizeWidget(event->globalPos());
            }
            else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed)
            {
                moveWidget(event->globalPos());
            }
        }
        else if (d->m_bWidgetResizable)
        {
            updateCursorShape(event->globalPos());
        }
    }
    
    void WidgetData::handleLeaveEvent(QEvent *event)
    {
        Q_UNUSED(event)
        if (!m_bLeftButtonPressed)
        {
            m_pWidget->unsetCursor();
        }
    }
    
    void WidgetData::handleHoverMoveEvent(QHoverEvent *event)
    {
        if (d->m_bWidgetResizable)
        {
            updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
        }
    }

    FramelessHelper类

    #include <QRect>
    #include <QRubberBand>
    #include <QMouseEvent>
    #include <QHoverEvent>
    #include <QApplication>
    #include "frameless_helper.h"
    
    class WidgetData;
    
    /*****FramelessHelper*****/
    FramelessHelper::FramelessHelper(QObject *parent)
        : QObject(parent),
          d(new FramelessHelperPrivate())
    {
        d->m_bWidgetMovable = true;
        d->m_bWidgetResizable = true;
        d->m_bRubberBandOnResize = false;
        d->m_bRubberBandOnMove = false;
    }
    
    FramelessHelper::~FramelessHelper()
    {
        QList<QWidget*> keys = d->m_widgetDataHash.keys();
        int size = keys.size();
        for (int i = 0; i < size; ++i)
        {
            delete d->m_widgetDataHash.take(keys[i]);
        }
    
        delete d;
    }
    
    bool FramelessHelper::eventFilter(QObject *obj, QEvent *event)
    {
        switch (event->type())
        {
        case QEvent::MouseMove:
        case QEvent::HoverMove:
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::Leave:
        {
            WidgetData *data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
            if (data)
            {
                data->handleWidgetEvent(event);
                return true;
            }
        }
        }
        return QObject::eventFilter(obj, event);
    }
    
    void FramelessHelper::activateOn(QWidget *topLevelWidget)
    {
        if (!d->m_widgetDataHash.contains(topLevelWidget))
        {
            WidgetData *data = new WidgetData(d, topLevelWidget);
            d->m_widgetDataHash.insert(topLevelWidget, data);
    
            topLevelWidget->installEventFilter(this);
        }
    }
    
    void FramelessHelper::removeFrom(QWidget *topLevelWidget)
    {
        WidgetData *data = d->m_widgetDataHash.take(topLevelWidget);
        if (data)
        {
            topLevelWidget->removeEventFilter(this);
            delete data;
        }
    }
    
    void FramelessHelper::setRubberBandOnMove(bool movable)
    {
        d->m_bRubberBandOnMove = movable;
        QList<WidgetData*> list = d->m_widgetDataHash.values();
        foreach (WidgetData *data, list)
        {
            data->updateRubberBandStatus();
        }
    }
    
    void FramelessHelper::setWidgetMovable(bool movable)
    {
        d->m_bWidgetMovable = movable;
    }
    
    void FramelessHelper::setWidgetResizable(bool resizable)
    {
        d->m_bWidgetResizable = resizable;
    }
    
    void FramelessHelper::setRubberBandOnResize(bool resizable)
    {
        d->m_bRubberBandOnResize = resizable;
        QList<WidgetData*> list = d->m_widgetDataHash.values();
        foreach (WidgetData *data, list)
        {
            data->updateRubberBandStatus();
        }
    }
    
    void FramelessHelper::setBorderWidth(uint width)
    {
        if (width > 0)
        {
            CursorPosCalculator::m_nBorderWidth = width;
        }
    }
    
    void FramelessHelper::setTitleHeight(uint height)
    {
        if (height > 0)
        {
            CursorPosCalculator::m_nTitleHeight = height;
        }
    }
    
    bool FramelessHelper::widgetMovable()
    {
        return d->m_bWidgetMovable;
    }
    
    bool FramelessHelper::widgetResizable()
    {
        return d->m_bWidgetResizable;
    }
    
    bool FramelessHelper::rubberBandOnMove()
    {
        return d->m_bRubberBandOnMove;
    }
    
    bool FramelessHelper::rubberBandOnResisze()
    {
        return d->m_bRubberBandOnResize;
    }
    
    uint FramelessHelper::borderWidth()
    {
        return CursorPosCalculator::m_nBorderWidth;
    }
    
    uint FramelessHelper::titleHeight()
    {
        return CursorPosCalculator::m_nTitleHeight;
    }

    接口说明

    • FramelessHelperPrivate

      存储界面对应的数据集合,以及是否可移动、可缩放属性

    • CursorPosCalculator

      计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角

    • WidgetData

      更新鼠标样式、移动窗体、缩放窗体

    • FramelessHelper

      激活窗体、移除窗体、设置窗体移动、窗体缩放、橡皮筋移动、橡皮筋缩放、边框的宽度、标题栏高度等

    代码很多,我就不详细解答了,里面主要的接口我都添加了注释。其它接口的命名也是比较规范的-见名知义。

    使用方式

    这里的this指的是要处理的窗体。

    FramelessHelper *pHelper = new FramelessHelper(this);
    pHelper->activateOn(this);  //激活当前窗体
    pHelper->setTitleHeight(m_pTitleBar->height());  //设置窗体的标题栏高度
    pHelper->setWidgetMovable(true);  //设置窗体可移动
    pHelper->setWidgetResizable(true);  //设置窗体可缩放
    pHelper->setRubberBandOnMove(true);  //设置橡皮筋效果-可移动
    pHelper->setRubberBandOnResize(true);  //设置橡皮筋效果-可缩放

    平台支持

    因为使用的是纯Qt实现,所以支持跨平台!Win7、Win10、Redhat7.0已测试通过,其它平台尚未测试,有需要的童鞋可自行实验。

  • 相关阅读:
    BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
    BZOJ 2134: 单选错位( 期望 )
    BZOJ 1030: [JSOI2007]文本生成器( AC自动机 + dp )
    BZOJ 2599: [IOI2011]Race( 点分治 )
    BZOJ 3238: [Ahoi2013]差异( 后缀数组 + 单调栈 )
    ZOJ3732 Graph Reconstruction Havel-Hakimi定理
    HDU5653 Bomber Man wants to bomb an Array 简单DP
    HDU 5651 xiaoxin juju needs help 水题一发
    HDU 5652 India and China Origins 并查集
    HDU4725 The Shortest Path in Nya Graph dij
  • 原文地址:https://www.cnblogs.com/itrena/p/5938392.html
Copyright © 2011-2022 走看看