现在的应用例如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;
}