流行的圆角阴影带动画的窗口总结

现在的应用例如QQ,通常有一个美观的轮廓,有阴影特效,边上可能还带点圆角,然后可以自由移动、变化大小、带开启关闭动画等等。如果用电脑自带的那种风格可能大家都不太喜欢。前面有2篇博文写了一下实现的过程,但是可能写的有点乱而且还有点问题,也没示意图。现在翻开博客,打开Qt又重新总结了下,希望自己忘记了也可以看看吧。
窗口模型示意
首先这是一张窗口模型示意图,黑框是一个透明的衬底,这是给主窗体提供阴影特效、圆角特效空间。蓝框就是主窗体,里面可以分为标题烂和中心组件,当然 可以随便你怎么安排。蓝框和黑框之间是阴影特效。我自己写的模板就是按照这个图写的,先看一下测试的效果吧。
这里写图片描述

当然窗体是可以自由移动、缩放的,并且还带了个关闭动画。主要的代码都是在写移动和缩放。移动和缩放总的来说就是确定2个点,左上角和右下角。确定这2个点,窗体就确定下来了。所以关键就是如何计算这2个点,下面请看示意图:这里写图片描述
我们所做的移动是平行的移动,也就是像上图所示的那样:C移动到D,那么A要平行于CD移动到B,并且CD= AB,那用矢量表示的话就是AB矢量= CD矢量。
展开化简可以得到,B坐标 = D+A-C。也就是你先要确定一下你鼠标按下的那个点,然后由上式算出 移动后窗口的左上角顶点,右下角的点也是这么确定的。阴影和动画前面文章里写到了,代码就一点点就不说了。
下面我把完成的类贴出来:(不包含测试程序,只是模板)

#ifndef STYLEDWINDOW_H
#define STYLEDWINDOW_H

#include <QWidget>
class QGridLayout;
class QFrame;
class QLabel;
class StyledWindow : public QWidget
{
    Q_OBJECT

public:
    StyledWindow(QWidget *parent = 0);
    void setEdgeMargin(int margin);   //设置检测的边缘距离
    void setTitleBar(QWidget* titleBar);  //设置窗口顶部部件
    void setCentralWidget(QWidget* centralWidget);
    int  EdgeMargin() const;
    void setIsPlayCloseAnimation(bool play);

    QGridLayout* Layout();


private:
    QPoint diffTLpoint;   //鼠标单击的位置和左上角相差的坐标矢量
    QPoint diffBRpoint;   //鼠标单击的位置和右下角相差的坐标矢量
    int    edgeMargin;     //鼠标检测的边缘距离
    enum {nodir,
          top = 0x01,
          bottom = 0x02,
          left = 0x04,
          right = 0x08,
          topLeft = 0x01 | 0x04,
          topRight = 0x01 | 0x08,
          bottomLeft = 0x02 | 0x04,
          bottomRight = 0x02 | 0x08} resizeDir; //更改尺寸的方向
    enum {notplay,hasplayed}closeAnimationState;//关闭动画的播放状态  
    bool  isPlayCloseAnimation;
    QFrame* mainWidget;
    QWidget* titleBar;
    QWidget* centralWidget;
    QGridLayout* layout;
    bool hasTitleBar() const;
    bool hasCentralWidget() const;
private:
    void testEdge();
protected:
    void mousePressEvent(QMouseEvent*event);
    void mouseMoveEvent(QMouseEvent*event);
    void mouseReleaseEvent(QMouseEvent*event);
    void virtual playCloseAnimation();
    void closeEvent(QCloseEvent*event);
};

#endif // STYLEDWINDOW_H
#include "styledwindow.h"
#include <QMouseEvent>
#include <QCursor>
#include <QCloseEvent>
#include <QPropertyAnimation>
#include <QDebug>
#include <QGridLayout>
#include<QFrame>
#include <QGraphicsDropShadowEffect>
#include <QLabel>
#define min(a,b) ((a)<(b)? (a) :(b))
#define max(a,b) ((a)>(b)? (a) :(b))
StyledWindow::StyledWindow(QWidget *parent) :
    QWidget(parent)
{
    edgeMargin = 4;        //设置检测边缘为4
    resizeDir = nodir;   //初始化检测方向为无
    setWindowFlags(Qt::FramelessWindowHint);  //设置无边框
    setAttribute(Qt::WA_TranslucentBackground);
    setMouseTracking(true); //开启鼠标追踪
    closeAnimationState = notplay;
    QGridLayout* marginLayout = new QGridLayout(this);
    mainWidget = new QFrame(this);
    mainWidget ->setMouseTracking(true);
    mainWidget->setObjectName("mainWidget");
    mainWidget->setStyleSheet("QFrame#mainWidget{margin:4px}");
    QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect;
    effect->setBlurRadius(10);
    effect->setColor(Qt::black);
    effect->setOffset(0,0);
    mainWidget->setGraphicsEffect(effect);

    marginLayout->addWidget(mainWidget);
    marginLayout->setMargin(0);
    setLayout(marginLayout);
    setContentsMargins(0,0,0,0);

    layout = new QGridLayout(mainWidget);
//    layout->setRowStretch(0,0);
//    layout->setRowStretch(1,1);
    layout->setMargin(2);
    mainWidget->setLayout(layout);
    titleBar = NULL;
    centralWidget =NULL;



}
void StyledWindow::mousePressEvent(QMouseEvent * event)
{
    if (event->button() == Qt::LeftButton){
   
   //每当按下鼠标左键就记录一下位置
        diffTLpoint = event->globalPos() - frameGeometry().topLeft();  //获得鼠标按键位置相对窗口左上面的坐标矢量
        diffBRpoint = event->globalPos() - frameGeometry().bottomRight(); //右下角
        event->accept();
        }

}
void StyledWindow::testEdge()
{
    int diffLeft = abs(cursor().pos().x() - frameGeometry().left());      //计算鼠标距离窗口上下左右有多少距离
    int diffRight = abs(cursor().pos().x() - frameGeometry().right());
    int diffTop = abs(cursor().pos().y() - frameGeometry().top());
    int diffBottom = abs(cursor().pos().y() - frameGeometry().bottom());
  //  qDebug()<<diffLeft<<diffRight<<diffTop<<diffBottom;
    QCursor tempCursor;                                    //获得当前鼠标样式,注意:只能获得当前鼠标样式然后再重新设置鼠标样式
    tempCursor = cursor();                                 //因为获得的不是鼠标指针,所以不能这样用:cursor().setXXXXX

    if(diffTop < edgeMargin){                              //根据 边缘距离 分类改变尺寸的方向
        if(diffLeft < edgeMargin){
            resizeDir = topLeft;
            tempCursor.setShape(Qt::SizeFDiagCursor);
        }
        else if(diffRight < edgeMargin){
            resizeDir = topRight;
            tempCursor.setShape(Qt::SizeBDiagCursor);
        }
        else{
            resizeDir = top;
            tempCursor.setShape(Qt::SizeVerCursor);
        }
    }
    else if(diffBottom < edgeMargin){
        if(diffLeft < edgeMargin){
            resizeDir = bottomLeft;
            tempCursor.setShape(Qt::SizeBDiagCursor);
        }
        else if(diffRight < edgeMargin){
            resizeDir = bottomRight;
            tempCursor.setShape(Qt::SizeFDiagCursor);
        }
        else{
            resizeDir = bottom;
            tempCursor.setShape(Qt::SizeVerCursor);
        }
    }
    else if(diffLeft < edgeMargin){
        resizeDir = left;
        tempCursor.setShape(Qt::SizeHorCursor);
    }
    else if(diffRight < edgeMargin){
        resizeDir = right;
        tempCursor.setShape(Qt::SizeHorCursor);
    }
    else{
        resizeDir = nodir;
        tempCursor.setShape(Qt::ArrowCursor);
    }

    setCursor(tempCursor);                    //重新设置鼠标,主要是改样式




}

void StyledWindow::mouseMoveEvent(QMouseEvent * event)
{
    if (event->buttons() & Qt::LeftButton){                 //如果左键是按下的
      if(resizeDir == nodir){                             //如果鼠标不是放在边缘那么说明这是在拖动窗口
         move(event->globalPos() - diffTLpoint);
      }
      else{
          int ptop,pbottom,pleft,pright;                   //窗口上下左右的值
          ptop = geometry().top();
          pbottom = geometry().bottom();
          pleft = geometry().left();
          pright = geometry().right();

          int TLcy,TLcx,BRcx,BRcy;
          TLcy = event->globalY()- diffTLpoint.y();
          TLcx = event->globalX()- diffTLpoint.x();
          BRcy = event->globalY() - diffBRpoint.y();
          BRcx = event->globalX() - diffBRpoint.x();
          if(resizeDir & top){                               //检测更改尺寸方向中包含的上下左右分量
              if(height() == minimumHeight()){
                  ptop = min(TLcy,ptop);
              }
              else if(height() == maximumHeight()){
                  ptop = max(TLcy,ptop);
              }
              else{
                  ptop = TLcy;
              }
          }
          else if(resizeDir & bottom){
              if(height() == minimumHeight()){
                  pbottom = max(BRcy,pbottom);
              }
              else if(height() == maximumHeight()){
                  pbottom = min(BRcy,pbottom);
              }
              else{
                  pbottom = BRcy;
              }
          }

          if(resizeDir & left){                        //检测左右分量
              if(width() == minimumWidth()){
                  pleft = min(TLcx,pleft);
              }
              else if(width() == maximumWidth()){
                  pleft = max(TLcx,pleft);
              }
              else{
                  pleft = TLcx;
              }
          }
          else if(resizeDir & right){
              if(width() == minimumWidth()){
                  pright = max(BRcx,pright);
              }
              else if(width() == maximumWidth()){
                  pright = min(BRcx,pright);
              }
              else{
                  pright = BRcx;
              }
          }
          setGeometry(QRect(QPoint(pleft,ptop),QPoint(pright,pbottom)));
      }
  }
  else testEdge();   //当不拖动窗口、不改变窗口大小尺寸的时候  检测鼠标边缘


}
void StyledWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if(resizeDir != nodir){         //还原鼠标样式

        testEdge();
        event->accept();
    }

}

void StyledWindow::closeEvent(QCloseEvent *event)
{
    if(isPlayCloseAnimation && closeAnimationState == notplay){
        playCloseAnimation();
        event->ignore();
    }
    else{
        event->accept();
    }
}


bool StyledWindow::hasTitleBar() const
{
    if(NULL != titleBar){
        return true;
    }
    return false;
}
bool StyledWindow::hasCentralWidget() const
{
    if(NULL != centralWidget){
        return true;
    }
    return false;
}

int StyledWindow::EdgeMargin() const
{
    return edgeMargin;
}

void StyledWindow::setEdgeMargin(int margin)
{
    if(margin > 0) edgeMargin = margin;
}

void StyledWindow::setTitleBar(QWidget *titleBar)
{
    if(titleBar != NULL){
        if(hasTitleBar()){
            this->titleBar->deleteLater();
        }
        titleBar->setParent(mainWidget);
        this->titleBar = titleBar;
        layout->addWidget(titleBar);
    }
}
void StyledWindow::setCentralWidget(QWidget *centralWidget)
{
   if(centralWidget != NULL){
       if(hasCentralWidget()){
           this->centralWidget->deleteLater();
       }
       centralWidget->setParent(mainWidget);
       this->centralWidget = centralWidget;
       layout->addWidget(centralWidget);
   }
}

void StyledWindow::setIsPlayCloseAnimation(bool play)
{
    isPlayCloseAnimation = play;
}

void StyledWindow::playCloseAnimation()
{
    setMinimumSize(0,0);
    QPropertyAnimation* closeAnimation = new QPropertyAnimation(this,"geometry");
    closeAnimation->setStartValue(geometry());
    closeAnimation->setEndValue(QRect(geometry().x(),geometry().y()+height()/2,width(),0));
    closeAnimation->setDuration(300);
    closeAnimationState = hasplayed;
    connect(closeAnimation,SIGNAL(finished()),this,SLOT(close()));
    closeAnimation->start(QAbstractAnimation::DeleteWhenStopped);

}

QGridLayout* StyledWindow::Layout()
{
    return layout;
}

猜你喜欢

转载自blog.csdn.net/qq_16952303/article/details/54744775
今日推荐