QT 自定义窗体的简单实现(二)

                                                 QT 自定义窗体的简单实现(二)

一、简述

        记--自定义窗体的简单实现,自定义窗体模板。

         有时候我们需要自定义标题栏、比如添加一个按钮、一张图标之类的,但是原有的窗体并没有直接方便可用的接口,此时可以不要默认窗体的边框、标题,自己定制一个;若去掉窗体的默认边框会导致无法移动窗体和改变窗体大小(原有的窗体就是通过标题栏移动窗体、通过边框改变窗体大小的), 所以去掉窗体默认的边框之后我们需要自行实现移动和改变窗体大小的功能。

       例子打包:https://wwa.lanzous.com/iGn3Uhiv2yh

二、效果

三、工程结构

四、源文件

MyWindow.pro文件

QT       += core gui
RC_ICONS += res/system.ico
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = MyWindow
TEMPLATE = app


SOURCES += main.cpp\
        mainwindow.cpp \
    unframehelper.cpp

HEADERS  += mainwindow.h \
    unframehelper.h

FORMS    += mainwindow.ui

RESOURCES += \
    myres.qrc
 mainwindow.h文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "unframehelper.h"

//自定义Label,实现双击事件
#include <QLabel>
#include <QMouseEvent>
class MyLabel : public QLabel
{
    Q_OBJECT
public:
    explicit MyLabel(QWidget *parent = Q_NULLPTR) :QLabel(parent){}

    ~MyLabel(){}
signals:
    void doubleClicked();
protected:
    void mouseDoubleClickEvent(QMouseEvent* event) {
        if (Qt::LeftButton == event->button()) {//左键单击
            emit doubleClicked();//发射信号
        }
    }
};

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
protected:

private slots:
    void on_pushButtonCloseWindow_clicked();

    void on_pushButtonMinSize_clicked();

    void on_pushButtonMaxSize_clicked();

    void on_title_doubleClick();

private:
    Ui::MainWindow *ui;
    UnframeHelper *m_frameHelper;//无边框工具类
};

#endif // MAINWINDOW_H

 unframehelper.h文件

#ifndef UNFRAMEHELPER_H
#define UNFRAMEHELPER_H

#include <QObject>
#include <QWidget>
#include <QEvent>
#include <QPoint>
#include <QRect>

enum Border {
    E_Border_NULL = 0,
    E_Border_NORMAL,        // 正常
    E_Border_LEFT,          // 左边框
    E_Border_LEFT_TOP,      // 左上角
    E_Border_TOP,           // 上边框
    E_Border_RIGHT_TOP,     // 右上角
    E_Border_RIGHT,         // 有边框
    E_Border_RIGHT_BOTTOM,  // 右下角
    E_Border_BOTTOM,        // 下边框
    E_Border_LEFT_BOTTOM    // 左下角
};

class UnframeHelper : public QObject
{
    Q_OBJECT
public:
    explicit UnframeHelper(QWidget *winPtr, QObject *parent = 0);
    void setTitleHeight(int titleHeight); //设置标题高度--可移动区域
    void setBorderWidth(int borderWidth); //设置边框宽度
private:
    int updateCursor(QPoint mousePoint);
    void changeSize(int offSizeX, int offSizeY);
protected:
    bool eventFilter(QObject *watched, QEvent *event);//事件过滤器

signals:

public slots:

private:
    QWidget *m_winPtr = nullptr;
    QPoint m_mouseBeginPos; //用来记录鼠标的按下的开始位置
    QPoint m_winBeginPos; //用来记录鼠标的按下的窗体开始位置
    int m_titleHeight; //标题高度
    bool m_movePress = false; //用来记录鼠标是否在标题栏处按下
    bool m_LeftBtnPress = false; //用来鼠标左键是否按下
    int m_winBorderWidth; //窗体边框宽度
    int m_border_type = E_Border_NULL; // 边框类型
    QRect m_winBeginRect; //用来记录鼠标左键按下时窗体的位置和尺寸
};

#endif // UNFRAMEHELPER_H

main.cpp文件

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

 mainwindow.cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

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

    //无边框实现移动、窗体大小变化
    m_frameHelper = new UnframeHelper(this);
    m_frameHelper->setTitleHeight(ui->labelWindowTitle->height());

    //标题双击事件
    connect(ui->labelWindowTitle, SIGNAL(doubleClicked()), this, SLOT(on_title_doubleClick()));

    //状态栏
    this->statusBar()->addWidget(new QLabel(tr("Hello World")));
    this->statusBar()->addPermanentWidget(new QLabel(tr("CopyRight @ Genven_Liang")));
}

MainWindow::~MainWindow()
{
    delete m_frameHelper;
    delete ui;
}

//双击标题等同点击最大化按钮
void MainWindow::on_title_doubleClick()
{
    on_pushButtonMaxSize_clicked();
}

//关闭窗口
void MainWindow::on_pushButtonCloseWindow_clicked()
{
    this->close();
}

//最小化窗口
void MainWindow::on_pushButtonMinSize_clicked()
{
    this->showMinimized();
}

//最大化窗口
void MainWindow::on_pushButtonMaxSize_clicked()
{
    if (!this->isMaximized()) {
        this->showMaximized();
        ui->pushButtonMaxSize->setStyleSheet("QPushButton{border-image:url(:/res/res/restore.png);} QPushButton:hover{border-image:url(:/res/res/restoreHover.png);}");
        ui->pushButtonMaxSize->setToolTip(tr("还原"));
    } else {
        this->showNormal();
        ui->pushButtonMaxSize->setStyleSheet("QPushButton{border-image:url(:/res/res/max.png);} QPushButton:hover{border-image:url(:/res/res/maxHover.png);}");
        ui->pushButtonMaxSize->setToolTip(tr("最大化"));
    }
}

mainwindow.cpp文件

#include "unframehelper.h"
#include <QDebug>
#include <QMouseEvent>
#include "mainwindow.h"
#include <QStatusBar>

UnframeHelper::UnframeHelper(QWidget *winPtr, QObject *parent) : QObject(parent),m_winPtr(winPtr)
{
    m_titleHeight = -1;
    m_winBorderWidth = 2;

    winPtr->setWindowFlags(Qt::FramelessWindowHint); //无边框
    winPtr->installEventFilter(this); //为窗体安装事件过滤器

    //实时跟踪鼠标, 不然默认上层子控件获取鼠标事件,窗体不处理鼠标移动事件
    winPtr->setMouseTracking(true);
}

void UnframeHelper::setTitleHeight(int titleHeight)
{
    m_titleHeight = titleHeight;
}

void UnframeHelper::setBorderWidth(int borderWidth)
{
    m_winBorderWidth = borderWidth;
}

//事件过滤器
bool UnframeHelper::eventFilter(QObject *watched, QEvent *event)
{
    QEvent::Type evType = event->type();
    if (!m_LeftBtnPress && evType == QEvent::MouseButtonPress) { //鼠标按下
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        if(mouseEvent->button() == Qt::LeftButton ) {//鼠标左键按下
             m_LeftBtnPress = true;
             QPoint p = mouseEvent->globalPos();
             m_mouseBeginPos = p;//左键时,鼠标相对窗体的位置
             m_winBeginPos = m_winPtr->pos();
             m_winBeginRect = m_winPtr->frameGeometry();//记录窗体的大小位置
             int x = p.x();
             int y = p.y();
             int wx = m_winBeginRect.x();
             int wy = m_winBeginRect.y();
             if (y > (wy+m_winBorderWidth)
                 && y <= (wy+m_titleHeight)
                 && x > (wx+m_winBorderWidth)
                 && x <(wx+m_winBeginRect.width()-m_winBorderWidth)
                 )
             {//只能通过标题栏拖拽窗体
                 m_movePress = true;
             }
         }
    } else if (evType == QEvent::MouseButtonRelease) { //鼠标按下后释放
        m_movePress = false;
        m_LeftBtnPress = false;
    } else if (evType == QEvent::Leave) { //离开窗体
        if (!m_LeftBtnPress) {
            m_winPtr->unsetCursor();//恢复鼠标样式
        }
    } else if (evType == QEvent::MouseMove) { //鼠标移动
        if (m_movePress) {//窗体拖动
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            QPoint pt = mouseEvent->globalPos();
            //当前鼠标相对窗体的位置-刚按下左键时的相对位置=鼠标移动的大小
            //鼠标移动的大小+窗体原来的位置=窗体移动后的位置
            m_winPtr->move((pt - m_mouseBeginPos)+ m_winBeginPos);
        } else if (E_Border_NORMAL < m_border_type && m_LeftBtnPress) {//窗体大小位置变化
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            QPoint pt = mouseEvent->globalPos();
            changeSize(pt.x() - m_mouseBeginPos.x(), pt.y() - m_mouseBeginPos.y());
        }
    } else if (!m_LeftBtnPress && evType == QEvent::HoverMove) { //鼠标悬停移动在窗体上
        //更新鼠标样式
        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
        int ret = updateCursor(mouseEvent->pos());
        if (E_Border_NULL != ret) {
            m_border_type = ret;
        }
    }
    return QObject::eventFilter(watched, event);
}

//更新鼠标样式
int UnframeHelper::updateCursor(QPoint mousePoint)
{
    int x  = mousePoint.x();
    int y = mousePoint.y();
    if (m_winBorderWidth >= x) {//左侧
        if (m_winBorderWidth >= y) {//左上角
            m_winPtr->setCursor(Qt::SizeFDiagCursor);
            return E_Border_LEFT_TOP;
        } else if(y >= (m_winPtr->height() - m_winBorderWidth)) {//左下角
            m_winPtr->setCursor(Qt::SizeBDiagCursor);
            return E_Border_LEFT_BOTTOM;
        } else {//左
            m_winPtr->setCursor(Qt::SizeHorCursor);
            return E_Border_LEFT;
        }
    } else if(x >= m_winPtr->width()-m_winBorderWidth) {//右侧
        if (m_winBorderWidth >= y) {//右上角
            m_winPtr->setCursor(Qt::SizeBDiagCursor);
            return E_Border_RIGHT_TOP;
        } else if (y >= (m_winPtr->height()- m_winBorderWidth)) {//右下角
            m_winPtr->setCursor(Qt::SizeFDiagCursor);
            return E_Border_RIGHT_BOTTOM;
        } else {// 右边
            m_winPtr->setCursor(Qt::SizeHorCursor);
            return E_Border_RIGHT;
        }
    } else if(m_winBorderWidth >= y) {//上侧
        m_winPtr->setCursor(Qt::SizeVerCursor);
        return E_Border_TOP;
    } else if (y >= (m_winPtr->height() - m_winBorderWidth)){//下侧
        m_winPtr->setCursor(Qt::SizeVerCursor);
        return E_Border_BOTTOM;
    } else if (!m_LeftBtnPress) { //不是按下状态,其它位置恢复鼠标样式
        m_winPtr->unsetCursor();
        return E_Border_NORMAL;
    }
    return E_Border_NULL;//没有变化,保持原样
}

//改变窗体大小和位置
void UnframeHelper::changeSize(int offSizeX, int offSizeY)
{
    if (E_Border_RIGHT == m_border_type) {
        int newWidth = m_winBeginRect.width() + offSizeX;
        if (newWidth > 0) {
            m_winPtr->setGeometry(m_winBeginRect.x(), m_winBeginRect.y(), newWidth, m_winBeginRect.height());
        }
    } else if (E_Border_LEFT == m_border_type) {
        int newX = m_winBeginRect.x() + offSizeX;
        int newWidth = m_winBeginRect.width() - offSizeX;
        if ( newWidth >0 ) {
            m_winPtr->setGeometry(newX, m_winBeginRect.y(), newWidth, m_winBeginRect.height());
        }
    } else if (E_Border_TOP == m_border_type) {
        int newY = m_winBeginRect.y() + offSizeY;
        int newHeight = m_winBeginRect.height() - offSizeY;
        if (newHeight >0 ) {
            m_winPtr->setGeometry(m_winBeginRect.x(), newY, m_winBeginRect.width(), newHeight);
        }
    } else if (E_Border_BOTTOM == m_border_type) {
        int newHeight = m_winBeginRect.height() + offSizeY;
        if (newHeight > 0) {
            m_winPtr->setGeometry(m_winBeginRect.x(), m_winBeginRect.y(), m_winBeginRect.width(), newHeight);
        }
    } else if (E_Border_LEFT_TOP == m_border_type) {
        int newX = m_winBeginRect.x() + offSizeX;
        int newY = m_winBeginRect.y() + offSizeY;
        int newWidth = m_winBeginRect.width() - offSizeX;
        int newHeight = m_winBeginRect.height() - offSizeY;
        if (newHeight > 0 && newWidth > 0) {
            m_winPtr->setGeometry(newX, newY, newWidth, newHeight);
        }
    } else if (E_Border_RIGHT_TOP == m_border_type) {
        int newY = m_winBeginRect.y() + offSizeY;
        int newWidth = m_winBeginRect.width() + offSizeX;
        int newHeight = m_winBeginRect.height() - offSizeY;
        if (newHeight > 0 && newWidth > 0) {
            m_winPtr->setGeometry(m_winBeginRect.x(), newY, newWidth, newHeight);
        }
    } else if (E_Border_RIGHT_BOTTOM == m_border_type) {
        int newWidth = m_winBeginRect.width() + offSizeX;
        int newHeight = m_winBeginRect.height() + offSizeY;
        if (newHeight > 0 && newWidth > 0) {
            m_winPtr->setGeometry(m_winBeginRect.x(), m_winBeginRect.y(), newWidth, newHeight);
        }
    } else if (E_Border_LEFT_BOTTOM == m_border_type) {
        int newX = m_winBeginRect.x() + offSizeX;
        int newWidth = m_winBeginRect.width() - offSizeX;
        int newHeight = m_winBeginRect.height() + offSizeY;
        if (newHeight > 0 && newWidth > 0) {
            m_winPtr->setGeometry(newX, m_winBeginRect.y(), newWidth, newHeight);
        }
    }
}

 

五、总结

5.1 坐标系说明

     左上角时坐标原点,x轴往屏幕下方增长,y轴往右边增长。屏幕和窗体都是以左上角为原点的坐标系的。

5.2 窗体移动

    鼠标左键按下、记录鼠标位置P1,鼠标移动时的实时位置P相对与P1的偏差就是窗体的偏差
     使用void move(const QPoint &p);函数将窗体移动到指定的点p  (窗体的左上角设置为p点)

5.3 窗体改变大小

     在4个边框和4给角的位置可以将窗体拉伸:左、左上角、上、右上角、右、右下角、下、左下角    
    在这8个位置时悬停和按下时候、鼠标的样式分别变化为,左右箭头、左斜箭头、上下箭头、右斜箭头、左右箭头、左斜箭头、上下箭头、右斜箭头、
    在这8个位置的时候按下并拖动时候、进行窗体拉伸。上,下、左、右四个方向只在一个方向拉伸,4个角同时往两个方向拉伸。
    使用QWidget::setGeometry(int ax, int ay, int aw, int ah)函数重新设置窗体的位置和宽高。

   鼠标事件的globalPos()是鼠标位置相对于屏幕原点的位置、pos()是鼠标位置相对于窗体的位置。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页