Qt 自定义标题栏

简述
    最近做的一个项目需要自定义标题栏,查看了官方文档和一些大牛写的例子,思路基本上一致,都是先设置成无边框的窗口,然后自己再自定义一个标题栏,需要实现最大化,最大化还原,最小化,关闭,窗口拖动,窗口大小拉伸。代码实在Qt4.8上实现的,可直接运行。
    主要参考了两个博友的代码  前行之路还需前行和青峰碧陋室。

实现
mytitlebar.h
#include 
#include 
#include 
#include 

enum ButtonType
{
    MIN_BUTTON = 0,         // 最小化和关闭按钮;
    MIN_MAX_BUTTON ,        // 最小化、最大化和关闭按钮;
    ONLY_CLOSE_BUTTON       // 只有关闭按钮;
};

class MyTitleBar : public QWidget
{
    Q_OBJECT
public:
    explicit MyTitleBar(QWidget *parent = 0);

    //设置标题栏背景色
    void setBackgroundColor(int r, int g, int b);
    //设置标题栏图标
    void setTitleIcon(QString filePath, QSize IconSize = QSize(25 , 25));
    //设置标题栏内容
    void setTitleContent(QString titleContent , int titleFontSize = 9);
    //设置标题栏长度
    void setTitleWidth(int width);
    // 设置标题栏上按钮类型;
    void setButtonType(ButtonType buttonType);
    // 设置窗口边框宽度;
    void setWindowBorderWidth(int borderWidth);

    // 保存/获取 最大化前窗口的位置及大小;
    void saveRestoreInfo(const QPoint point, const QSize size);
    void getRestoreInfo(QPoint& point, QSize& size);

private:

    void paintEvent(QPaintEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

    //控件初始化
    void initControl();
    //信号槽绑定
    void initConnecent();

signals:
    // 按钮触发的信号;
    void signalButtonMinClicked();
    void signalButtonRestoreClicked();
    void signalButtonMaxClicked();
    void signalButtonCloseClicked();
    
public slots:
    // 按钮触发的槽;
    void onButtonMinClicked();
    void onButtonRestoreClicked();
    void onButtonMaxClicked();
    void onButtonCloseClicked();

private:
    QLabel* m_pIcon;                    // 标题栏图标;
    QLabel* m_pTitleContent;            // 标题栏内容;
    QPushButton* m_pButtonMin;          // 最小化按钮;
    QPushButton* m_pButtonRestore;      // 最大化还原按钮;
    QPushButton* m_pButtonMax;          // 最大化按钮;
    QPushButton* m_pButtonClose;        // 关闭按钮;

    // 标题栏背景色;
    int m_colorR;
    int m_colorG;
    int m_colorB;

    // 最大化,最小化变量;
    QPoint m_restorePos;
    QSize m_restoreSize;
    // 移动窗口的变量;
    bool m_isPressed;
    QPoint m_startMovePos;

    // 标题栏内容;
    QString m_titleContent;
    // 按钮类型;
    ButtonType m_buttonType;
    // 窗口边框宽度;
    int m_windowBorderWidth;
};
mytitlebar.cpp
#include "mytitlebar.h"
#include 
#include 
#include 
#include 
#include 
#include 

#define BUTTON_HEIGHT 35        // 按钮高度;
#define BUTTON_WIDTH 35         // 按钮宽度;
#define TITLE_HEIGHT 35         // 标题栏高度;

MyTitleBar::MyTitleBar(QWidget *parent) :
    QWidget(parent)
  , m_colorR(153)
  , m_colorG(153)
  , m_colorB(153)
  , m_isPressed(false)
  , m_buttonType(MIN_MAX_BUTTON)
  , m_windowBorderWidth(0)
{

    initControl();
    initConnecent();

    setBackgroundColor(160,160,160);
    setTitleIcon("C:/Users/niu/Desktop/Qss/07_Qss/images/qq.png", QSize(20,20));
    setTitleContent("   自定义窗口" , 10);
}

/*************类的私有函数**********************/
//控件初始化
void MyTitleBar::initControl()
{
    m_pIcon = new QLabel;
    m_pTitleContent = new QLabel;

    m_pButtonMin = new QPushButton;
    m_pButtonRestore = new QPushButton;
    m_pButtonMax = new QPushButton;
    m_pButtonClose = new QPushButton;

    m_pButtonMin->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
    m_pButtonRestore->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
    m_pButtonMax->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));
    m_pButtonClose->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT));

    m_pButtonMax->setVisible(true);
    m_pButtonRestore->setVisible(false);

    m_pTitleContent->setObjectName("TitleContent");
    m_pButtonMin->setObjectName("ButtonMin");
    m_pButtonRestore->setObjectName("ButtonRestore");
    m_pButtonMax->setObjectName("ButtonMax");
    m_pButtonClose->setObjectName("ButtonClose");

    m_pButtonMin->setToolTip("最小化");
    m_pButtonRestore->setToolTip("还原");
    m_pButtonMax->setToolTip("最大化");
    m_pButtonClose->setToolTip("关闭");

    QHBoxLayout* mylayout = new QHBoxLayout(this);
    mylayout->addWidget(m_pIcon);

    mylayout->addWidget(m_pTitleContent);
    mylayout->addWidget(m_pButtonMin);
    mylayout->addWidget(m_pButtonRestore);
    mylayout->addWidget(m_pButtonMax);
    mylayout->addWidget(m_pButtonClose);
    mylayout->setSpacing(0);
    mylayout->setContentsMargins(5, 0, 2, 0);


    m_pTitleContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    this->setFixedHeight(TITLE_HEIGHT);
    this->setWindowFlags(Qt::FramelessWindowHint);
}

//信号槽绑定
void MyTitleBar::initConnecent()
{
    connect(m_pButtonMin, SIGNAL(clicked()), this, SLOT(onButtonMinClicked()));
    connect(m_pButtonRestore, SIGNAL(clicked()), this, SLOT(onButtonRestoreClicked()));
    connect(m_pButtonMax, SIGNAL(clicked()), this, SLOT(onButtonMaxClicked()));
    connect(m_pButtonClose, SIGNAL(clicked()), this, SLOT(onButtonCloseClicked()));
}


/**************提供的外部接口**********************/

//设置标题栏背景色
void MyTitleBar::setBackgroundColor(int r, int g, int b)
{
    // 标题栏背景色;
    m_colorR = r;
    m_colorG = g;
    m_colorB = b;

    update();
}

//设置标题栏图标
void MyTitleBar::setTitleIcon(QString filePath, QSize IconSize)
{
    QPixmap titleIcon(filePath);
    m_pIcon->setPixmap(titleIcon.scaled(IconSize));
}

//设置标题栏内容
void MyTitleBar::setTitleContent(QString titleContent , int titleFontSize)
{
    // 设置标题字体大小;
    QFont font = m_pTitleContent->font();
    font.setPointSize(titleFontSize);
    m_pTitleContent->setFont(font);
    // 设置标题内容;
    m_pTitleContent->setText(titleContent);
    m_titleContent = titleContent;
}

//设置标题栏长度
void MyTitleBar::setTitleWidth(int width)
{
    this->setFixedWidth(width);
}

// 设置标题栏上按钮类型;
void MyTitleBar::setButtonType(ButtonType buttonType)
{
    m_buttonType = buttonType;

    switch (buttonType)
    {
    case MIN_BUTTON:
        {
            m_pButtonRestore->setVisible(false);
            m_pButtonMax->setVisible(false);
        }
        break;
    case MIN_MAX_BUTTON:
        {
            m_pButtonRestore->setVisible(false);
        }
        break;
    case ONLY_CLOSE_BUTTON:
        {
            m_pButtonMin->setVisible(false);
            m_pButtonRestore->setVisible(false);
            m_pButtonMax->setVisible(false);
        }
        break;
    default:
        break;
    }
}

// 设置窗口边框宽度;
void MyTitleBar::setWindowBorderWidth(int borderWidth)
{
    m_windowBorderWidth = borderWidth;
}

// 保存/获取 最大化前窗口的位置及大小;
void MyTitleBar::saveRestoreInfo(const QPoint point, const QSize size)
{
    m_restorePos = point;
    m_restoreSize = size;
}

void MyTitleBar::getRestoreInfo(QPoint& point, QSize& size)
{
    point = m_restorePos;
    size = m_restoreSize;
}



/*************事件处理函数**********************/
void MyTitleBar::paintEvent(QPaintEvent *event)
{

    //设置背景色
    QPainter painter(this);
    QPainterPath pathBack;
    pathBack.setFillRule(Qt::WindingFill);
    pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter.fillPath(pathBack, QBrush(QColor(m_colorR, m_colorG, m_colorB)));
    if (this->width() != (this->parentWidget()->width() - m_windowBorderWidth))
    {
        this->setFixedWidth(this->parentWidget()->width() - m_windowBorderWidth);
    }

    QWidget::paintEvent(event);
}

//鼠标双击事件
void MyTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
    // 只有存在最大化、还原按钮时双击才有效;
    if (m_buttonType == MIN_MAX_BUTTON)
    {
        // 通过最大化按钮的状态判断当前窗口是处于最大化还是原始大小状态;
        // 或者通过单独设置变量来表示当前窗口状态;
        if (m_pButtonMax->isVisible())
        {
            onButtonMaxClicked();
        }
        else
        {
            onButtonRestoreClicked();
        }
    }

    return QWidget::mouseDoubleClickEvent(event);
}

//鼠标按下事件
//通过mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件实现了鼠标拖动标题栏移动窗口的效果;
void MyTitleBar::mousePressEvent(QMouseEvent *event)
{
    if (m_buttonType == MIN_MAX_BUTTON)
    {
        // 在窗口最大化时禁止拖动窗口;
        if (m_pButtonMax->isVisible())
        {
            m_isPressed = true;
            m_startMovePos = event->globalPos();
        }
    }
    else
    {
        m_isPressed = true;
        m_startMovePos = event->globalPos();
    }

    return QWidget::mousePressEvent(event);
}

//鼠标移动事件
void MyTitleBar::mouseMoveEvent(QMouseEvent *event)
{
    if (m_isPressed)
    {
        QPoint movePoint = event->globalPos() - m_startMovePos;
        QPoint widgetPos = this->parentWidget()->pos();
        m_startMovePos = event->globalPos();
        this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y());
    }
    return QWidget::mouseMoveEvent(event);
}

void MyTitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    m_isPressed = false;
    return QWidget::mouseReleaseEvent(event);
}

/*************按钮触发的槽**********************/
void MyTitleBar::onButtonMinClicked()
{
    emit signalButtonMinClicked();
}

void MyTitleBar::onButtonRestoreClicked()
{
    m_pButtonRestore->setVisible(false);
    m_pButtonMax->setVisible(true);
    emit signalButtonRestoreClicked();
}

void MyTitleBar::onButtonMaxClicked()
{
    m_pButtonMax->setVisible(false);
    m_pButtonRestore->setVisible(true);
    emit signalButtonMaxClicked();
}

void MyTitleBar::onButtonCloseClicked()
{
    emit signalButtonCloseClicked();
}
widget.h
#include 
#include "mytitlebar.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    void initTitleBar();

    //界面大小拉伸
    int countFlag(QPoint p, int row);
    void setCursorType(int flag);
    int countRow(QPoint p);

private slots:
    //最大化,最小化,关闭 槽函数
    void onButtonMinClicked();
    void onButtonRestoreClicked();
    void onButtonMaxClicked();
    void onButtonCloseClicked();
    
protected:
    //界面大小拉伸
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    //窗口重绘事件
    void paintEvent(QPaintEvent *event);

public:
    Ui::Widget *ui;

    MyTitleBar *m_titleBar;

    //界面大小拉伸
    bool isLeftPressed;
    int curPos;
    QPoint pLast;
};
widght.cpp
#include "widget.h"
#include "ui_widget.h"
#include "mytitlebar.h"

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

#define MARGIN 10  //四个角的长度

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //设置在不按鼠标的情况下也触发鼠标移动事件,注意QMainWindow的情况:centralWidget()->setMouseTracking(true);
    //鼠标跟踪即使没有按下鼠标也会触发鼠标移动事件
    this->setMouseTracking(true);
    //设置鼠标是否按时标志,默认false
    isLeftPressed=false;
    curPos=0;//标记鼠标左击时的位置
    this->setMinimumSize(400,300);//设置最小尺寸

    //QCursor cursor;
    //cursor.setShape(Qt::ArrowCursor);//设置鼠标为箭头形状
    //QWidget::setCursor(Qt::ArrowCursor);//当放在主窗口上时,为手形

    //设置去边框和属性设置在窗口最小化时,点击任务栏窗口可以显示出原窗口;
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
    // 关闭窗口时释放资源;
    setAttribute(Qt::WA_DeleteOnClose);

    //初始化标题栏
    initTitleBar();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::initTitleBar()
{
    m_titleBar = new MyTitleBar(this);
    m_titleBar->move(0,0);

    connect(m_titleBar, SIGNAL(signalButtonMinClicked()), this, SLOT(onButtonMinClicked()));
    connect(m_titleBar, SIGNAL(signalButtonRestoreClicked()), this, SLOT(onButtonRestoreClicked()));
    connect(m_titleBar, SIGNAL(signalButtonMaxClicked()), this, SLOT(onButtonMaxClicked()));
    connect(m_titleBar, SIGNAL(signalButtonCloseClicked()), this, SLOT(onButtonCloseClicked()));

}

void Widget::paintEvent(QPaintEvent *event)
{
    //设置背景色;
    QPainter painter(this);
    QPainterPath pathBack;
    pathBack.setFillRule(Qt::WindingFill);
    pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
    painter.fillPath(pathBack, QBrush(QColor(160,160,160)));

    return QWidget::paintEvent(event);
}


void Widget::onButtonMinClicked()
{
    showMinimized();
}

void Widget::onButtonRestoreClicked()
{
    QPoint windowPos;
    QSize windowSize;
    m_titleBar->getRestoreInfo(windowPos, windowSize);
    setGeometry (QRect(windowPos,windowSize));
}

void Widget::onButtonMaxClicked()
{
    //最大化前,先获取窗口的位置和宽高
    m_titleBar->saveRestoreInfo(this->pos(), QSize(this->width(), this->height()));
    //获取屏幕的位置和大小
    QRect desktopRect = QApplication::desktop()->availableGeometry();
    QRect FactRect = QRect(desktopRect.x(), desktopRect.y(), desktopRect.width() , desktopRect.height());
    //设置窗口的位置和大小
    setGeometry (FactRect);

}

void Widget::onButtonCloseClicked()
{
    close();
}


//窗口拉大拉小
int Widget::countFlag(QPoint p, int row)
{
    if(p.y()this->height()-MARGIN)
        return 30+row;
    else
        return 20+row;
}

void Widget::setCursorType(int flag)
{
    Qt::CursorShape cursor;
        switch(flag)
        {
        case 11:
            cursor=Qt::ArrowCursor;break;
        case 33:
            cursor=Qt::SizeFDiagCursor;break;
        case 13:
            cursor=Qt::ArrowCursor;break;
        case 31:
            cursor=Qt::SizeBDiagCursor;break;
        case 21:
        case 23:
            cursor=Qt::SizeHorCursor;break;
        case 12:
            cursor=Qt::ArrowCursor;break;
        case 32:
            cursor=Qt::SizeVerCursor;break;
        case 22:
            cursor=Qt::ArrowCursor;break;
        default:
             QApplication::restoreOverrideCursor();//恢复鼠标指针性状
             break;

        }
        setCursor(cursor);
}

int Widget::countRow(QPoint p)
{
    return (p.x()(this->width()-MARGIN)?3:2);
}

//鼠标按下事件
void Widget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "窗口鼠标按下事件mousePressEvent";
    if(event->button()==Qt::LeftButton)
    {
        this->isLeftPressed=true;

        //鼠标按下是的屏幕坐标
        QPoint temp=event->globalPos();
        pLast=temp;
        curPos=countFlag(event->pos(),countRow(event->pos()));
        event->ignore();
    }
}

//鼠标放开事件
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    qDebug() << "窗口鼠标放开事件mouseReleaseEvent";
    if(isLeftPressed)
        isLeftPressed=false;
    QApplication::restoreOverrideCursor();//恢复鼠标指针性状
    event->ignore();
}

void Widget::mouseDoubleClickEvent(QMouseEvent *event)
{
    qDebug() << "鼠标事件双击";
}

//鼠标移动事件
void Widget::mouseMoveEvent(QMouseEvent *event)
{qDebug() << "鼠标移动事件";
    int poss=countFlag(event->pos(),countRow(event->pos()));
        setCursorType(poss);
        if(isLeftPressed)//是否左击
        {
            //返回鼠标的屏幕坐标
            QPoint ptemp=event->globalPos();
            ptemp=ptemp-pLast;

            //如果鼠标在中间区域按下,则移动窗口
            if(curPos==22)//移动窗口
            {
                ptemp=ptemp+pos();
                //move(ptemp);
            }
            //如果鼠标在中间区域按下,则窗口大小拉伸
            else
            {
                QRect wid=geometry();

                switch(curPos)//改变窗口的大小
                {

              //  case 11:wid.setTopLeft(wid.topLeft()+ptemp);break;//左上角
              //  case 13:wid.setTopRight(wid.topRight()+ptemp);break;//右上角
                case 31:wid.setBottomLeft(wid.bottomLeft()+ptemp);break;//左下角
                case 33:wid.setBottomRight(wid.bottomRight()+ptemp);break;//右下角
              //  case 12:wid.setTop(wid.top()+ptemp.y());break;//中上角
                case 21:wid.setLeft(wid.left()+ptemp.x());break;//中左角
                case 23:wid.setRight(wid.right()+ptemp.x());break;//中右角
                case 32:wid.setBottom(wid.bottom()+ptemp.y());break;//中下角
                }
                setGeometry(wid);
            }
            pLast=event->globalPos();//更新位置,返回鼠标的全局位置
        }
        event->ignore();

}
main.c
#include "widget.h"
#include 
#include "mytitlebar.h"
#include 

#include "mywindow.h"


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForTr(codec);
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);

    Widget w;
    w.show();

    //MyWindow w;
    //w.show();
    
    return a.exec();
}




 


你可能感兴趣的:(Qt)