Qt自定义标题栏并实现拖拽

#Qt自定义标题栏并实现拖拽
【最新】修改移动时偶现bug,mousePressEvent和mouseMoveEvent修改。
  很多时候,Qt自带的窗体边框不能满足我们的需求,或者我们觉得由于系统的主题影响导致界面太丑了,我们需要自行定义一个好看并和普通标题栏功能相同的控件去替代,以达到美化我们的程序界面的目的;本文简单的实现了该功能。
  下面是运行截图,由于图片选的比较丑,所以看起来不好看:
自定义标题栏
  实现了鼠标拖拽事件,没有为标题栏添加鼠标右键事件以及其他美化功能,可以自行添加。
##拖拽实现详解:
  首先需要了解Qt的一些函数的意义:
  MouseEvent中的globalPos()函数返回的是相对屏幕的位置坐标,而pos()则是返回鼠标在当前控件(即捕获该鼠标事件的控件)中的位置;
  QWidget窗体的geometry().topLeft()则返回的是当前窗体的左上角在屏幕中的位置;

  其次让我们看图说话,理解下窗体移动:
窗体移动
  然后我们每次只要把上次的位置作为起始位置,就是一次次的拖拽了。

  最后,我们列出算式:
  startPos = event->globalPos();      // 鼠标的全局初始位置,按下时记住
  curWindowPos = geometry().topleft();  // 窗体的全局位置,移动时
  endPos = event->globalPos();      // 鼠标按下发生移动之后的位置,移动时
  move(curWindowPos+(startPos-endPos)); // 根据矢量移动方向是初始位置减去末位置,移动时
  startPos = endPos;     // 将初始位置记为上次末位置,然后执行直到释放拖拽,移动时
##头文件


// WindowHeader.h
#ifndef WINDOWHEADER_H
#define WINDOWHEADER_H

#include <QLabel>
#include <QWidget>
#include <QToolButton>
#include <QHBoxLayout>

#include <QPoint>
#include <QString>

// 自定义控件,可拖拽标题栏
class WindowHeader : public QWidget
{
    Q_OBJECT
public:
    // 构造函数
    WindowHeader(const char* appName, QWidget *parent);

    // 设置关闭按钮icon
    void SetCloseImage(const char* path);
    // 设置最小化按钮icon
    void SetMinImage(const char* path);
    // 设置最大化按钮icon
    void SetMaxImage(const char* path);
    // 设置迷你模式按钮icon
    void SetMiniImage(const char* path);
    // 设置软件配置按钮icon
    void SetSettingImage(const char* path);
    // 设置皮肤按钮icon
    void SetSkinImage(const char* path);
    // 设置软件LOGO按钮icon
    void SetAppLogo(const char* path,int w=20,int h=20);

    // 获取软件名
    QString GetAppName() { return msAppName; }
protected:
    // 重写鼠标事件
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

    // 将信号中转,不直接处理信号
signals:
    void ClickedClose();
    void ClickedMin();
    void ClickedMax();
    void ClickedMini();
    void ClickedSetting();
    void ClickedSkin();

private:
    // 应用名
    QString msAppName;

    // 关闭按钮
    QToolButton* mTlbtnClose;
    // 最小化按钮
    QToolButton* mTlbtnMin;
    // 最大化按钮
    QToolButton* mTlbtnMax;
    // 迷你模式按钮
    QToolButton* mTlbtnMini;
    // 软件配置按钮
    QToolButton* mTlbtnSetting;
    // 皮肤设置按钮
    QToolButton* mTlbtnSkin;

    // 呈现应用名称
    QLabel* mLblAppName;
    // 呈现应用LOGO
    QLabel* mLblAppLogo;

    // 整体布局
    QHBoxLayout* mLytMain;

    // 鼠标上次移动开始时相对屏幕的位置
    QPoint mPntStart;
    // 鼠标是否持续按下
    bool mbKeepPressed;

    // 父窗口的指针
    QWidget *mWidParent;
};
#endif // WINDOWHEADER_H

##源文件


// WindowHeader.cpp
#include "WindowHeader.h"
#include <QMouseEvent>
#include <QPixmap>
#include <QIcon>

WindowHeader::WindowHeader(const char* appName,QWidget *parent)
    :QWidget(parent),msAppName(appName)

{
    // 父窗口指针赋值,并设置父窗口为无边框模式
    mWidParent = parent;
    mWidParent->setWindowFlags(Qt::FramelessWindowHint | mWidParent->windowFlags());

    // 初始化按钮
    mTlbtnClose = new QToolButton(this);
    mTlbtnMin = new QToolButton(this);
    mTlbtnMax = new QToolButton(this);
    mTlbtnMini = new QToolButton(this);
    mTlbtnSetting = new QToolButton(this);
    mTlbtnSkin = new QToolButton(this);

    // 初始化标签
    mLblAppName = new QLabel(this);
    mLblAppName->setText(msAppName);
    mLblAppLogo = new QLabel(this);

    // 初始化布局
    mLytMain = new QHBoxLayout(this);
    mLytMain->setContentsMargins(2,2,2,2);
    mLytMain->setSpacing(2);

    // 设置布局
    mLytMain->addWidget(mLblAppLogo);
    mLytMain->addWidget(mLblAppName);
    // 设置“弹簧”,将呈现和按钮功能分开
    mLytMain->addStretch();
    mLytMain->addWidget(mTlbtnSkin);
    mLytMain->addWidget(mTlbtnSetting);
    mLytMain->addWidget(mTlbtnMini);
    mLytMain->addWidget(mTlbtnMin);
    mLytMain->addWidget(mTlbtnMax);
    mLytMain->addWidget(mTlbtnClose);

    // 将内部按钮信号全部发送出去,内部不做业务逻辑绑定
    connect(mTlbtnClose,SIGNAL(clicked(bool)),SIGNAL(ClickedClose()));
    connect(mTlbtnMax,SIGNAL(clicked(bool)),SIGNAL(ClickedMax()));
    connect(mTlbtnMin,SIGNAL(clicked(bool)),SIGNAL(ClickedMin()));
    connect(mTlbtnMini,SIGNAL(clicked(bool)),SIGNAL(ClickedMini()));
    connect(mTlbtnSetting,SIGNAL(clicked(bool)),SIGNAL(ClickedSetting()));
    connect(mTlbtnSkin,SIGNAL(clicked(bool)),SIGNAL(ClickedSkin()));
}

// 设置应用LOGO
void WindowHeader::SetAppLogo(const char *path,int w,int h)
{
    QPixmap newPix = QPixmap(path).scaled(w,h,Qt::KeepAspectRatio);
    mLblAppLogo->setPixmap(newPix);
}

void WindowHeader::SetSkinImage(const char *path)
{
    QIcon icon(path);
    mTlbtnSkin->setIcon(icon);
}

void WindowHeader::SetSettingImage(const char *path)
{
    QIcon icon(path);
    mTlbtnSetting->setIcon(icon);
}

void WindowHeader::SetMiniImage(const char *path)
{
    QIcon icon(path);
    mTlbtnMini->setIcon(icon);
}

void WindowHeader::SetMaxImage(const char *path)
{
    QIcon icon(path);
    mTlbtnMax->setIcon(icon);
}

void WindowHeader::SetMinImage(const char *path)
{
    QIcon icon(path);
    mTlbtnMin->setIcon(icon);
}

void WindowHeader::SetCloseImage(const char *path)
{
    QIcon icon(path);
    mTlbtnClose->setIcon(icon);
}

// 重写mouseMoveEvent
void WindowHeader::mouseMoveEvent(QMouseEvent *event)
{
  // 持续按住才做对应事件
    if (mbKeepPressed)
    {
        // 将父窗体移动到父窗体之前的位置加上鼠标移动的位置【event->globalPos()- mPntStart】
        mWidParent->move(mPntParentStart + event->globalPos()- mPntMouseStart);
    }
    QWidget::mouseMoveEvent(event);
}

// 重写mousePressEvent
void WindowHeader::mousePressEvent(QMouseEvent *event)
{
    // 鼠标左键按下事件
    if (event->button() == Qt::LeftButton)
    {
        // 记录鼠标在屏幕中的位置
        mPntMouseStart = event->globalPos();
        mPntParentStart = mWidParent->geometry().topLeft();
        // 记录鼠标状态
        mbKeepPressed = true;
    }
    QWidget::mousePressEvent(event);
}

// 重写mouseReleaseEvent
void WindowHeader::mouseReleaseEvent(QMouseEvent *event)
{
    // 鼠标左键释放
    if (event->button() == Qt::LeftButton)
    {
        // 记录鼠标状态
        mbKeepPressed = false;
    }
    QWidget::mouseReleaseEvent(event);
}

原文出处:http://blog.csdn.net/motou263514/article/details/78090483

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值