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版本。自己替换内容即可。
我看到很多这方面的博客都为收费内容,如果这篇免费内容有帮助到你,麻烦点个赞,谢谢!