[QT] 無邊框窗體改變大小 完美實現


近期,做項目用到無邊框窗體,令人蛋疼的是無邊框窗體大小的改變要像右邊框那樣,上下左右四周,而且要流暢。

網上也找了些代碼,發現居然還要連接到windows事件,這顯然不合常理,后來自己新建了demo,寫了一個小時,問題太多了,扔一邊先不管了。

今天需要改進UI界面,沒辦法了,重新整理了下思路,沒想到做出來了。下面來分享下實現的過程,也許是菜鳥專欄,高手勿噴~

1.首先大家要了解各九宮格的概念

一個窗體可以被划分為上、下、左、右、左上、左下、右上、右下、中間,除了中間部分,其他都需要寫程序處理。

在程序中定義Padding 為2,並同時定義枚舉類型。

#define PADDING 2
enum Direction { UP=0, DOWN=1, LEFT, RIGHT, LEFTTOP, LEFTBOTTOM, RIGHTBOTTOM, RIGHTTOP, NONE };

2.定義一個方法,參數為鼠標目前的全局位置。

void Dialog::region(const QPoint &cursorGlobalPoint)
{
  // 獲取窗體在屏幕上的位置區域,tl為topleft點,rb為rightbottom點 QRect rect
= this->rect(); QPoint tl = mapToGlobal(rect.topLeft()); QPoint rb = mapToGlobal(rect.bottomRight());
int x = cursorGlobalPoint.x(); int y = cursorGlobalPoint.y(); if(tl.x() + PADDING >= x && tl.x() <= x && tl.y() + PADDING >= y && tl.y() <= y) { // 左上角 dir = LEFTTOP; this->setCursor(QCursor(Qt::SizeFDiagCursor)); // 設置鼠標形狀 } else if(x >= rb.x() - PADDING && x <= rb.x() && y >= rb.y() - PADDING && y <= rb.y()) { // 右下角 dir = RIGHTBOTTOM; this->setCursor(QCursor(Qt::SizeFDiagCursor)); } else if(x <= tl.x() + PADDING && x >= tl.x() && y >= rb.y() - PADDING && y <= rb.y()) { //左下角 dir = LEFTBOTTOM; this->setCursor(QCursor(Qt::SizeBDiagCursor)); } else if(x <= rb.x() && x >= rb.x() - PADDING && y >= tl.y() && y <= tl.y() + PADDING) { // 右上角 dir = RIGHTTOP; this->setCursor(QCursor(Qt::SizeBDiagCursor)); } else if(x <= tl.x() + PADDING && x >= tl.x()) { // 左邊 dir = LEFT; this->setCursor(QCursor(Qt::SizeHorCursor)); } else if( x <= rb.x() && x >= rb.x() - PADDING) { // 右邊 dir = RIGHT; this->setCursor(QCursor(Qt::SizeHorCursor)); }else if(y >= tl.y() && y <= tl.y() + PADDING){ // 上邊 dir = UP; this->setCursor(QCursor(Qt::SizeVerCursor)); } else if(y <= rb.y() && y >= rb.y() - PADDING) { // 下邊 dir = DOWN; this->setCursor(QCursor(Qt::SizeVerCursor)); }else { // 默認 dir = NONE; this->setCursor(QCursor(Qt::ArrowCursor)); } }

3.在對話框類中定義幾個私有成員變量

bool isLeftPressDown;  // 判斷左鍵是否按下
QPoint dragPosition;   // 窗口移動拖動時需要記住的點 
Direction dir; //
窗口大小改變時,記錄改變方向

編寫對話框構造函數,初始化幾個變量並做一些其他工作。

isLeftPressDown = false;
this->dir = NONE;
this->setMinimumHeight(100);
this->setMinimumWidth(150);
this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowSystemMenuHint); // 設置成無邊框對話框
this->setMouseTracking(true);                    // 追蹤鼠標
this->setStyleSheet("QDialog{background:url(:/bg_main.png)}"); // 設置樣式背景色,可有可無

4.接着就要實現幾個重要的重載事件了

void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);

實現過程如下:

void Dialog::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        isLeftPressDown = false;
        if(dir != NONE) {
            this->releaseMouse();
            this->setCursor(QCursor(Qt::ArrowCursor));
        }
    }
}

void Dialog::mousePressEvent(QMouseEvent *event)
{
    switch(event->button()) {
    case Qt::LeftButton:
        isLeftPressDown = true;
        if(dir != NONE) {
            this->mouseGrabber();
        } else {
            dragPosition = event->globalPos() - this->frameGeometry().topLeft();
        }
        break;
    case Qt::RightButton:
        this->close();
        break;
    default:
        QDialog::mousePressEvent(event);
    }

}

void Dialog::mouseMoveEvent(QMouseEvent *event)
{
    QPoint gloPoint = event->globalPos();
    QRect rect = this->rect();
    QPoint tl = mapToGlobal(rect.topLeft());
    QPoint rb = mapToGlobal(rect.bottomRight());

    if(!isLeftPressDown) {
        this->region(gloPoint);
    } else {

        if(dir != NONE) {
            QRect rMove(tl, rb);

            switch(dir) {
            case LEFT:
                if(rb.x() - gloPoint.x() <= this->minimumWidth())
                    rMove.setX(tl.x());
                else
                    rMove.setX(gloPoint.x());
                break;
            case RIGHT:
                rMove.setWidth(gloPoint.x() - tl.x());
                break;
            case UP:
                if(rb.y() - gloPoint.y() <= this->minimumHeight())
                    rMove.setY(tl.y());
                else
                    rMove.setY(gloPoint.y());
                break;
            case DOWN:
                rMove.setHeight(gloPoint.y() - tl.y());
                break;
            case LEFTTOP:
                if(rb.x() - gloPoint.x() <= this->minimumWidth())
                    rMove.setX(tl.x());
                else
                    rMove.setX(gloPoint.x());
                if(rb.y() - gloPoint.y() <= this->minimumHeight())
                    rMove.setY(tl.y());
                else
                    rMove.setY(gloPoint.y());
                break;
            case RIGHTTOP:
                rMove.setWidth(gloPoint.x() - tl.x());
                rMove.setY(gloPoint.y());
                break;
            case LEFTBOTTOM:
                rMove.setX(gloPoint.x());
                rMove.setHeight(gloPoint.y() - tl.y());
                break;
            case RIGHTBOTTOM:
                rMove.setWidth(gloPoint.x() - tl.x());
                rMove.setHeight(gloPoint.y() - tl.y());
                break;
            default:
                break;
            }
            this->setGeometry(rMove);
        } else {
            move(event->globalPos() - dragPosition);
            event->accept();
        }
    }
    QDialog::mouseMoveEvent(event);
}

到此為止,一個無邊框窗體拖動並且改變大小的功能就實現了,Run and debug it。

總結起來,這個算法其實並不復雜,就看有幾個點能不能想到:

1)窗體矩形區域要轉換成在屏幕上的區域,我采取的方式就是取TopLeft和RightBottom兩個點來確定這個區域。

2)鼠標移動要去全局的坐標。

3)region函數中判斷坐標區間,然后改變鼠標形狀,這塊很容易出錯,如果你一下子就寫出來,那我真的很佩服。

這個糾結的網上都沒有現成代碼的問題,終於在今天圓滿解決了。如需運行,請下載實例代碼:SizableNoFrame.rar

轉載請注明出處哦:http://www.cnblogs.com/xufeiyang/p/3313104.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM