1. 程式人生 > >Qt之無邊框視窗建立

Qt之無邊框視窗建立

為什麼要去邊框?

美觀

個人感覺系統自帶的邊框美觀上稍微欠缺一點,這也是好多軟體去掉邊框的原因吧。

自定義

去掉邊框後,就能自由在任何位置新增一個自己的邊框、標題欄之類的,甚至可以在上面新增一些工具選單。

如何去掉邊框?

Qt自帶的函式

使用下面這個函式就能去掉邊框:

setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);

//第一個引數是設定無邊框。第二個引數是允許工作列按鈕右鍵選單,第三個引數是允許最小化與還原。

使用第一個引數即可。

遇到問題

去掉邊框後,發現滑鼠也無法移動視窗,也無法自由調整大小。要說最小最大化、關閉的話,這個可以新增兩個按鈕來實現。
於是,我網上開始查詢一些資料、自己也思考了一下,大概如下解決:

關於移動視窗

這個自然是重寫滑鼠點選、移動事件了。

步驟一:滑鼠點選,記錄滑鼠點選時的位置。
步驟二:滑鼠移動,根據滑鼠當前的位置和起始位置,計算出滑鼠的位移,然後設定視窗的新位置。

關於自由調整大小

這個是比較麻煩的,網上找了好多資料,都沒有完美的實現方法。

網上比較多的是下面兩種做法:

一種是呼叫Windows API來實現,但是這樣就失去了跨平臺的意義。

一種是通過重寫滑鼠的region和move事件來實現,但是有些小BUG。

不完美的實現

通過第二種方法和我的除錯以及一些思考,提出以下做法:

條件一:首先,滑鼠region窗體時,而且距離邊框進入某個閾值PADDING時,此時被認為可以自動調整窗體大小。而且此效果優先於移動視窗的效果。

條件二:滿足第一步的情況下,按下滑鼠左鍵,並且移動滑鼠,將改變窗體的大小。不過根據滑鼠的位置會朝不同的方向改變。比如在窗體左邊界附近,那麼只會左側的窗體擴充套件或者收縮。

在前面兩個條件下,確實能滿足自由調整大小,但是會出現一個問題。在主窗體中,某些子元件在滑鼠進入時,主窗體會失去對滑鼠的檢測。可是在滑鼠點選的時候,主窗體又會再次獲得滑鼠的檢測。所以會出現,當滑鼠進入主窗體,啟用region效果,然後滑鼠進入子元件後,滑鼠位移了好長一段距離,主窗體的region效果仍然存在,此時如果滑鼠左擊,同時觸發第二個條件,然後窗體一下子大小就變了。以上是我進過反覆除錯得到的結論。

然後考慮到出現此BUG的最主要緣故是,子元件獲得滑鼠移動的檢測時,主窗體會丟失滑鼠的檢測。但是網上找了好多方法都沒有解決這個問題的,網上有些說設定mouseTracking可以解決,但是有些元件可以解決,有些元件無效。

於是我想了一個辦法:既然無法解決滑鼠的事情,就考慮重置region那個效果。當子元件獲得滑鼠檢測的時候,也就是mouseenter的時候,會發出一個mouseenter的訊號,然後父元件接收這個訊號的時候,觸發losemouse事件,重置region效果。這樣只需要對每個邊界處的子元件,編寫mouseenter事件就可以解決上面的BUG了。

具體程式碼:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>

//PADDING用於設定改變視窗時滑鼠偵測的邊距
#define PADDING 4
enum Direction
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
    LEFTTOP,
    LEFTBOTTOM,
    RIGHTBOTTOM,
    RIGHTTOP,
    NONE
};

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

    public slots:
        void loseMouse();//當滑鼠焦點被子元件獲取時,需要還原滑鼠形狀和changeDir。
        void setMaxSize();

    private:
        void region(const QPoint &cursorGlobalPoint);
        void mouseReleaseEvent(QMouseEvent *event);
        void mouseMoveEvent(QMouseEvent *event);
        void mousePressEvent(QMouseEvent *event);



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

#endif // MAINWINDOW_H

mainwindow.cpp

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

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    resize(970, 700);
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);
    //第一個引數是設定無邊框。第二個引數是允許工作列按鈕右鍵選單,第三個引數是允許最小化與還原。

    isLeftPressDown = false;
    isMaxSize = false;
    changeDir = NONE;
    setMaximumWidth(QApplication::desktop()->width());
    setMaximumHeight(QApplication::desktop()->height());
    setMinimumHeight(570);
    setMinimumWidth(985);
    setMouseTracking(true);//追蹤滑鼠
    //this->setStyleSheet("QDialog{background:url(:/bg_main.png)}");//設定樣式背景色,可有可無

    this->setFocusPolicy(Qt::ClickFocus);//主視窗設定滑鼠點選焦點,新建歌單時有用
}

void MainWindow::setMaxSize()
{
    if (this->isMaxSize)
    {
        this->isMaxSize = false;
        this->showNormal();
    }
    else
    {
        this->isMaxSize = true;
        this->showMaximized();
    }
}

void MainWindow::region(const QPoint &cursorGlobalPoint)
{
    if (isMaxSize)
        return;
    //獲取窗體在螢幕上的位置區域,tl為topleft點,rb為rightbottom點
    QRect rect = this->rect();
    QPoint topleft = mapToGlobal(rect.topLeft());
    QPoint rightbottom = mapToGlobal(rect.bottomRight());

    int x = cursorGlobalPoint.x();
    int y = cursorGlobalPoint.y();

    if(topleft.x()+PADDING >= x &&
        topleft.x() <= x &&
        topleft.y()+PADDING >= y &&
        topleft.y() <= y)
    {
        // 左上角
        changeDir = LEFTTOP;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));//設定滑鼠形狀
    }
    else if(x >= rightbottom.x()-PADDING &&
        x <= rightbottom.x() &&
        y >= rightbottom.y()-PADDING &&
        y <= rightbottom.y())
    {
        // 右下角
        changeDir = RIGHTBOTTOM;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    }
    else if(x <= topleft.x()+PADDING &&
        x >= topleft.x() &&
        y >= rightbottom.y()-PADDING &&
        y <= rightbottom.y())
    {
        //左下角
        changeDir = LEFTBOTTOM;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    }
    else if(x <= rightbottom.x() &&
        x >= rightbottom.x()-PADDING &&
        y >= topleft.y() &&
        y <= topleft.y()+PADDING)
    {
        // 右上角
        changeDir = RIGHTTOP;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    }
    else if(x <= topleft.x()+PADDING &&
        x >= topleft.x())
    {
        // 左邊
        changeDir = LEFT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    }
    else if(x <= rightbottom.x() &&
        x >= rightbottom.x()-PADDING)
    {
        // 右邊
        changeDir = RIGHT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    }
    else if(y >= topleft.y() &&
        y <= topleft.y()+PADDING)
    {
        // 上邊
        changeDir = UP;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    }
    else if(y <= rightbottom.y() &&
        y >= rightbottom.y()-PADDING)
    {
        // 下邊
        changeDir = DOWN;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    }
    else
    {
        // 預設
        changeDir = NONE;
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void MainWindow::loseMouse()
{
    changeDir = NONE;
    this->setCursor(QCursor(Qt::ArrowCursor));
}

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

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if (isMaxSize)
        return;
    if (event->button() == Qt::LeftButton)
    {
        isLeftPressDown = true;
        if(changeDir != NONE)
            this->mouseGrabber();
        else
            dragPosition = event->globalPos() - this->frameGeometry().topLeft();
    }
    else
        QWidget::mousePressEvent(event);
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if (isMaxSize)
        return;
    QPoint globalPoint = event->globalPos();
    QRect rect = this->rect();
    QPoint topleft = mapToGlobal(rect.topLeft());
    QPoint bottomright = mapToGlobal(rect.bottomRight());

    if(!isLeftPressDown)
    {
        this->region(globalPoint);
    }
    else if(changeDir != NONE)
    {
        QRect rMove(topleft, bottomright);
        switch(changeDir)
        {
            case LEFT:
                if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
                    rMove.setX(topleft.x());
                else
                    rMove.setX(globalPoint.x());
                break;
            case RIGHT:
                rMove.setWidth(globalPoint.x() - topleft.x());
                break;
            case UP:
                if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
                    rMove.setY(topleft.y());
                else
                    rMove.setY(globalPoint.y());
                break;
            case DOWN:
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            case LEFTTOP:
                if(bottomright.x() - globalPoint.x() <= this->minimumWidth())
                    rMove.setX(topleft.x());
                else
                    rMove.setX(globalPoint.x());
                if(bottomright.y() - globalPoint.y() <= this->minimumHeight())
                    rMove.setY(topleft.y());
                else
                    rMove.setY(globalPoint.y());
                break;
            case RIGHTTOP:
                rMove.setWidth(globalPoint.x() - topleft.x());
                rMove.setY(globalPoint.y());
                break;
            case LEFTBOTTOM:
                rMove.setX(globalPoint.x());
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            case RIGHTBOTTOM:
                rMove.setWidth(globalPoint.x() - topleft.x());
                rMove.setHeight(globalPoint.y() - topleft.y());
                break;
            default:
                break;
        }
        this->setGeometry(rMove);
    }
    else
    {
        move(event->globalPos() - dragPosition);
        //event->accept();
    }
    QWidget::mouseMoveEvent(event);
}