【Qt】自定义标题栏

Qt的QWidget类极其子类(QMainWindow,QLabel,QLineEdit,QPushButton等等都为QWidget的子类)会有一个默认的标题栏,这个标题栏又丑又死板,有些时候它并不能满足我们的设计需求。

此时就需要调用QWidget的setWindowFlags(Qt::FrameLessWindowHint)方法将原有的标题栏隐藏掉,自己整一个。

但是这样会产生一个新的问题,没有了最大化、最小化、关闭按钮,而且窗口也不能拖拽,鼠标滑过窗口边缘时亦无拖拽缩放的鼠标样式和功能。

下面去一一解决这些遇到的问题。

分析需求

  • 最大化、最小化、关闭按钮及其对应事件。

        在窗口的右侧手动布置最小化,最大化和关闭按钮,并绑定它们点击信号对应的槽函数。不用Layout的方式布局,因为这个窗口以后或许要作为父类让子类继承,不论怎样,它的内容都会用到Layout,如果两个Layout都设置该窗口为父窗口,则会造成警告。尽管可以在析构函数中,析构该Layout,但是我们不采用这种方式,就用move的方式在窗口尺寸发生变化时自动更新位置。

  • 双击标题栏窗口最大化或恢复原状。

        可以监听窗口的双击事件,通过一个类成员变量来记录当前窗口状态是否为最大化状态,若为最大化状态,双击就恢复原状,若不是最大化状态,双击就将窗口最大化。

  • 鼠标按压标题栏可以拖拽窗体位置。

        监听鼠标按压事件,如果按压时鼠标位置位于标题栏区域内,就设置为可拖拽(即类成员变量中一个布尔值记录是否可拖拽状态),监听鼠标移动事件时,如果状态为可拖拽状态,就重新设置窗口位置。

  • 鼠标滑过窗口边缘位置时切换鼠标样式。

        在鼠标移动事件中,实时追踪鼠标位置,如果鼠标滑过特定边缘区域,就切换对应的鼠标样式。

  • 鼠标在窗口边缘位置按压后移动鼠标可以缩放窗体。

        如果鼠标恰在某一边缘位置,并且通过监听鼠标按压事件得知鼠标已经按压,就将存储可缩放状态的布尔值设置为真,在鼠标移动事件中,实现窗体跟随鼠标动作进行缩放。

  • 窗口阴影

        在窗口常规状态窗口四周应有阴影,并且在窗口最大化状态时取消阴影。

  • 内容布局

        内容布局自动适应窗口最大化和常规状态。

效果展示

代码参考

头文件:

#ifndef XMAINWINDOW_H
#define XMAINWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class CloseButton:public QAbstractButton{
    Q_OBJECT
public:
    explicit CloseButton(QWidget* parent=nullptr);
    ~CloseButton();
protected:
    void enterEvent(XEvent* event)override;
    void leaveEvent(QEvent* event)override;
    void mousePressEvent(QMouseEvent* event)override;
    void mouseReleaseEvent(QMouseEvent* event)override;
    void paintEvent(QPaintEvent* event)override;
private:
    bool _isEnter=false;
    bool _isPress=false;
};

class FlexButton:public QAbstractButton{
    Q_OBJECT
public:
    explicit FlexButton(QWidget* parent=nullptr);
    ~FlexButton();
    void setIsMax(bool isMax);
protected:
    void enterEvent(XEvent* event)override;
    void leaveEvent(QEvent* event)override;
    void mousePressEvent(QMouseEvent* event)override;
    void mouseReleaseEvent(QMouseEvent* event)override;
    void paintEvent(QPaintEvent* event)override;
private:
    bool _isEnter=false;
    bool _isPress=false;
    bool _isMax=false;
};

class MiniButton:public QAbstractButton{
    Q_OBJECT
public:
    explicit MiniButton(QWidget* parent=nullptr);
    ~MiniButton();
protected:
    void enterEvent(XEvent* event)override;
    void leaveEvent(QEvent* event)override;
    void mousePressEvent(QMouseEvent* event)override;
    void mouseReleaseEvent(QMouseEvent* event)override;
    void paintEvent(QPaintEvent *event) override;
private:
    bool _isEnter=false;
    bool _isPress=false;
};

class XMainwindow:public QWidget{
    Q_OBJECT
public:
    explicit XMainwindow(QWidget* parent=nullptr);
    ~XMainwindow();
protected:
    void showEvent(QShowEvent* event)override;
    void hideEvent(QHideEvent* event)override;
    void resizeEvent(QResizeEvent* event)override;
    void paintEvent(QPaintEvent *event)override;
    void mouseDoubleClickEvent(QMouseEvent* event)override;
    void mousePressEvent(QMouseEvent* event)override;
    void mouseReleaseEvent(QMouseEvent* event)override;
    void mouseMoveEvent(QMouseEvent* event)override;
private:
    void cursorPos_(const QMouseEvent* event);
    void dragResize_(const QMouseEvent* event);
    QLayout* layout_();
private slots:
    void minSlot_();
    void flexSlot_();
    void closeSlot_();
private:
    CloseButton* _closeButton;    //关闭按钮,自己画的
    FlexButton* _flexButton;    //最大化|复原按钮
    MiniButton* _miniButton;    //最小化按钮
private:
    enum CURSOR{normal,leftTop,leftBottom,rightTop,rightBottom,left,right,top,bottom};//鼠标边缘位置
    bool _isMax=false;    //窗口是否为最大化状态
    bool _canMove=false;    //窗口是否可移动
    bool _canResize=false;    //窗口是否可缩放
    QPoint _recordPos;    //记录鼠标按压点
    QPoint _recoredWindowLeftTopPos;    //记录窗口左上角坐标
    QRect _recoredWindowRect;    //记录缩放前窗口尺寸
    CURSOR _cursor=normal;    //记录鼠标边缘位置,方便在按压时得知应做什么缩放操作
    QRect _canMoveRect;    //可拖拽区,当鼠标在可拖拽区按压才可移动窗口,双击此区域最大化或复原   
};


#endif

源文件: 

#include "xmainwindow.h"

CloseButton::CloseButton(QWidget* parent):QAbstractButton(parent){
    setCursor(Qt::PointingHandCursor);
    setFixedSize(50,25);
    setToolTip("关闭");
}
CloseButton::~CloseButton(){}
void CloseButton::enterEvent(XEvent* event){
    Q_UNUSED(event);
    _isEnter=true;
    update();
}
void CloseButton::leaveEvent(QEvent* event){
    Q_UNUSED(event);
    _isEnter=false;
    update();
}
void CloseButton::mousePressEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos())){
        _isPress=true;
        update();
    }
}
void CloseButton::mouseReleaseEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos()))emit clicked();
    _isPress=false;
    update();
}
void CloseButton::paintEvent(QPaintEvent* event){
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    if(_isEnter&&!_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Sup5);
        painter.drawRoundedRect(rect(),2,2);
    }else if(_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Sup3);
        painter.drawRoundedRect(rect(),2,2);
    }
    QPen pen;
    if(_isEnter||_isPress){
        pen=QPen(X::Core7);
    }else{
        pen=QPen(X::Core3);
    }
    pen.setWidthF(1.5);
    painter.setPen(pen);
    float x=(width()-10)/2.0;
    float y=(height()-10)/2.0;
    QPointF tl=QPointF(x,y);
    QPointF br=QPointF(x+10,y+10);
    QPointF tr=QPointF(x+10,y);
    QPointF bl=QPointF(x,y+10);
    painter.drawLine(tl,br);
    painter.drawLine(tr,bl);
}

FlexButton::FlexButton(QWidget* parent):QAbstractButton(parent){
    setCursor(Qt::PointingHandCursor);
    setFixedSize(50,25);
    setToolTip("最大化");
}
FlexButton::~FlexButton(){}
void FlexButton::setIsMax(bool isMax){
    _isMax?setToolTip("复原"):setToolTip("最大化");
    _isMax=isMax;
    update();
}
void FlexButton::enterEvent(XEvent* event){
    Q_UNUSED(event);
    _isEnter=true;
    update();
}
void FlexButton::leaveEvent(QEvent* event){
    Q_UNUSED(event);
    _isEnter=false;
    update();
}
void FlexButton::mousePressEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos())){
        _isPress=true;
        update();
    }
}
void FlexButton::mouseReleaseEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos()))emit clicked();
    _isPress=false;
    update();
}
void FlexButton::paintEvent(QPaintEvent* event){
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    if(_isEnter&&!_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core6);
        painter.drawRoundedRect(rect(),2,2);
    }else if(_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core5);
        painter.drawRoundedRect(rect(),2,2);
    }
    QPen pen(X::Core3);
    pen.setWidthF(1.5);
    painter.setPen(pen);
    float x=(width()-10)/2.0;
    float y=(height()-10)/2.0;
    if(!_isMax){
        painter.drawRect(QRectF(x,y,10,10));
    }else{
        QPainterPath path1,path2,path;
        path1.addRect(QRectF(x,y+2,8,8));
        path2.addRect(QRectF(x+2,y,8,8));
        path=path1+path2;
        painter.drawPath(path);
        painter.drawPath(path1);
    }
}
MiniButton::MiniButton(QWidget* parent):QAbstractButton(parent){
    setCursor(Qt::PointingHandCursor);
    setFixedSize(50,25);
    setToolTip("最小化");
}
MiniButton::~MiniButton(){}
void MiniButton::enterEvent(XEvent* event){
    Q_UNUSED(event);
    _isEnter=true;
    update();
}
void MiniButton::leaveEvent(QEvent* event){
    Q_UNUSED(event);
    _isEnter=false;
    update();
}
void MiniButton::mousePressEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos())){
       _isPress=true;
       update();
    }
}
void MiniButton::mouseReleaseEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&rect().contains(event->pos()))emit clicked();
    _isPress=false;
    update();
}
void MiniButton::paintEvent(QPaintEvent* event){
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    if(_isEnter&&!_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core6);
        painter.drawRoundedRect(rect(),2,2);
    }else if(_isPress){
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core5);
        painter.drawRoundedRect(rect(),2,2);
    }
    QPen pen(X::Core3);
    pen.setWidthF(1.5);
    painter.setPen(pen);
    float x=(width()-10)/2.0;
    float y=height()/2.0;
    painter.drawLine(QPointF(x,y),QPointF(x+10,y));
}

XMainwindow::XMainwindow(QWidget* parent):QWidget(parent){
    setWindowFlag(Qt::FramelessWindowHint);
    setMouseTracking(true);
    setCursor(Qt::ArrowCursor);
    setAttribute(Qt::WA_TranslucentBackground);
    setStyleSheet("QWidget{background-color:green;}");

    _closeButton=new CloseButton(this);
    _flexButton=new FlexButton(this);
    _miniButton=new MiniButton(this);

    connect(_closeButton,SIGNAL(clicked()),this,SLOT(closeSlot_()));
    connect(_flexButton,SIGNAL(clicked()),this,SLOT(flexSlot_()));
    connect(_miniButton,SIGNAL(clicked()),this,SLOT(minSlot_()));
}
XMainwindow::~XMainwindow(){}
void XMainwindow::showEvent(QShowEvent* event){
    QLayout* lay=layout_();
    if(!lay)return;
    int left,top,right,bottom;
    lay->getContentsMargins(&left,&top,&right,&bottom);
    if(_isMax){
        _flexButton->setIsMax(true);
        lay->setContentsMargins(left-10,top-35,right-10,bottom-10);
    }else{
        _flexButton->setIsMax(false);
        lay->setContentsMargins(left+10,top+35,right+10,bottom+10);
    }
    QWidget::showEvent(event);
}
void XMainwindow::hideEvent(QHideEvent* event){
    QLayout* lay=layout_();
    if(!lay)return;
    int left,top,right,bottom;
    lay->getContentsMargins(&left,&top,&right,&bottom);
    if(_isMax){
        _flexButton->setIsMax(true);
        lay->setContentsMargins(left+10,top+35,right+10,bottom+10);
    }else{
        _flexButton->setIsMax(false);
        lay->setContentsMargins(left-10,top-35,right-10,bottom-10);
    }
    QWidget::hideEvent(event);
}
void XMainwindow::resizeEvent(QResizeEvent* event){
    QWidget::resizeEvent(event);
    if(_isMax){
        _closeButton->move(QPoint(width()-_closeButton->width(),0));
        _flexButton->move(QPoint(_closeButton->x()-_flexButton->width(),0));
        _miniButton->move(QPoint(_flexButton->x()-_miniButton->width(),0));
        _canMoveRect=QRect(0,0,_miniButton->x(),25);
    }else{
        _closeButton->move(QPoint(width()-_closeButton->width()-10,10));
        _flexButton->move(QPoint(_closeButton->x()-_flexButton->width(),10));
        _miniButton->move(QPoint(_flexButton->x()-_miniButton->width(),10));
        _canMoveRect=QRect(10,10,_miniButton->x(),25);
    }
}
void XMainwindow::paintEvent(QPaintEvent* event){
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);
    if(!_isMax){
        QColor color(X::Core3);
        for(int i=0;i<10;i++){
            QPainterPath path;
            path.setFillRule(Qt::WindingFill);
            path.addRoundedRect(10-i, 10-i, width()-(10-i)*2, height()-(10-i)*2,2,2);
            color.setAlpha(150 - qSqrt(i)*50);
            painter.setPen(color);
            painter.drawPath(path);
        }
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core7);
        painter.drawRoundedRect(QRect(10,10,width()-20,height()-20),2,2);
    }else{
        painter.setPen(Qt::NoPen);
        painter.setBrush(X::Core7);
        painter.drawRect(rect());
    }
}
void XMainwindow::mouseDoubleClickEvent(QMouseEvent* event){
    if(_canMoveRect.contains(event->pos()))flexSlot_();
}
void XMainwindow::mousePressEvent(QMouseEvent* event){
    if(event->button()==Qt::LeftButton&&!_isMax){
        if(_cursor!=normal){
            _recordPos=event->XGlobalPos();
            _canResize=true;
            _recoredWindowRect=frameGeometry();
        }else{
            if(_canMoveRect.contains(event->pos())){
                _recordPos=event->XGlobalPos();
                _recoredWindowLeftTopPos=frameGeometry().topLeft();
                _canMove=true;
            }
        }
    }
}
void XMainwindow::mouseReleaseEvent(QMouseEvent* event){
   _canResize=false;
   _canMove=false;
}
void XMainwindow::mouseMoveEvent(QMouseEvent* event){
    if(!_isMax){
        if(_canMove){
            QPoint distance=event->XGlobalPos()-_recordPos;
            move(_recoredWindowLeftTopPos+distance);
        }else if(_canResize){
            dragResize_(event);
        }else{
            cursorPos_(event);
        }
    }
}
void XMainwindow::cursorPos_(const QMouseEvent* event){
    int lx=x();
    int ty=y();
    int rx=lx+width();
    int by=ty+height();
    int mx=event->XGlobalPos().x();
    int my=event->XGlobalPos().y();
    bool nl=(lx-10mx);
    bool nt=(ty-10my);
    bool nr=(rx-10mx);
    bool nb=(by-10my);
    if(nl&&nt){
       setCursor(Qt::SizeFDiagCursor);
       _cursor=leftTop;
    }else if(nl&&nb){
       setCursor(Qt::SizeBDiagCursor);
       _cursor=leftBottom;
    }else if(nr&&nt){
       setCursor(Qt::SizeBDiagCursor);
       _cursor=rightTop;
    }else if(nr&&nb){
       setCursor(Qt::SizeFDiagCursor);
       _cursor=rightBottom;
    }else if(nt){
       setCursor(Qt::SizeVerCursor);
       _cursor=top;
    }else if(nb){
       setCursor(Qt::SizeVerCursor);
       _cursor=bottom;
    }else if(nl){
       setCursor(Qt::SizeHorCursor);
       _cursor=left;
    }else if(nr){
       setCursor(Qt::SizeHorCursor);
       _cursor=right;
    }else{
       setCursor(Qt::ArrowCursor);
       _cursor=normal;
    }
}
void XMainwindow::dragResize_(const QMouseEvent* event){
    QPoint distance=event->XGlobalPos()-_recordPos;
    int x,y,w,h,wf,hf;
    if(_cursor==leftTop){
        wf=_recoredWindowRect.width()-distance.x();
        hf=_recoredWindowRect.height()-distance.y();
        w=qMax(wf,minimumWidth());
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x()+_recoredWindowRect.width()-w;
        y=_recoredWindowRect.y()+_recoredWindowRect.height()-h;
        setGeometry(x,y,w,h);
    }else if(_cursor==leftBottom){
        wf=_recoredWindowRect.width()-distance.x();
        hf=_recoredWindowRect.height()+distance.y();
        w=qMax(wf,minimumWidth());
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x()+_recoredWindowRect.width()-w;
        y=_recoredWindowRect.y();
        setGeometry(x,y,w,h);
    }else if(_cursor==rightTop){
        wf=_recoredWindowRect.width()+distance.x();
        hf=_recoredWindowRect.height()-distance.y();
        w=qMax(wf,minimumWidth());
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x();
        y=_recoredWindowRect.y()+_recoredWindowRect.height()-h;
        setGeometry(x,y,w,h);
    }else if(_cursor==rightBottom){
        wf=_recoredWindowRect.width()+distance.x();
        hf=_recoredWindowRect.height()+distance.y();
        w=qMax(wf,minimumWidth());
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x();
        y=_recoredWindowRect.y();
        setGeometry(x,y,w,h);
    }else if(_cursor==top){
        hf=_recoredWindowRect.height()-distance.y();
        w=_recoredWindowRect.width();
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x();
        y=_recoredWindowRect.y()+_recoredWindowRect.height()-h;
        setGeometry(x,y,w,h);
    }else if(_cursor==bottom){
        hf=_recoredWindowRect.height()+distance.y();
        w=_recoredWindowRect.width();
        h=qMax(hf,minimumHeight());
        x=_recoredWindowRect.x();
        y=_recoredWindowRect.y();
        setGeometry(x,y,w,h);
    }else if(_cursor==left){
        wf=_recoredWindowRect.width()-distance.x();
        w=qMax(wf,minimumWidth());
        h=_recoredWindowRect.height();
        x=_recoredWindowRect.x()+_recoredWindowRect.width()-w;
        y=_recoredWindowRect.y();
        setGeometry(x,y,w,h);
    }else if(_cursor==right){
        wf=_recoredWindowRect.width()+distance.x();
        w=qMax(wf,minimumWidth());
        h=_recoredWindowRect.height();
        x=_recoredWindowRect.x();
        y=_recoredWindowRect.y();
        setGeometry(x,y,w,h);
    }else{}
}
QLayout* XMainwindow::layout_(){
    QRegularExpression exp=QRegularExpression(".*");
    QList layouts=findChildren(exp,Qt::FindDirectChildrenOnly);
    foreach(QLayout* layout,layouts){
        if(layout->parentWidget()==this){
            return layout;
        }
    }
    return nullptr;
}
void XMainwindow::minSlot_(){
    showMinimized();
}
void XMainwindow::flexSlot_(){
    QLayout* lay=layout_();
    if(!lay)return;
    int left,top,right,bottom;
    lay->getContentsMargins(&left,&top,&right,&bottom);
    if(_isMax){
        _flexButton->setIsMax(false);
        _isMax=false;
        lay->setContentsMargins(left+10,top+10,right+10,bottom+10);
        showNormal();
    }else{
        _flexButton->setIsMax(true);
        _isMax=true;
        lay->setContentsMargins(left-10,top-10,right-10,bottom-10);
        showMaximized();
    }
}
void XMainwindow::closeSlot_(){
    close();
}

说明 

        其中,不属于Qt的东西(如X::Core1)是我自己定义的宏,有些是为了统一配色,有些是为了兼容不同的Qt版本。自己替换内容即可。

后语

        我看到很多这方面的博客都为收费内容,如果这篇免费内容有帮助到你,麻烦点个赞,谢谢!

你可能感兴趣的:(Qt,qt,开发语言,ui)