1. 程式人生 > >QT 建立一個 可移動、可拉伸的無邊框窗體

QT 建立一個 可移動、可拉伸的無邊框窗體

    在使用QT建立窗體的時候,為了使視窗美化,通常不使用QT自帶的邊框。會呼叫下面函式去除窗體邊框。

setWindowFlags(Qt::FramelessWindowHint)

    但是有個問題,當去除了QT自帶邊框後,窗體就變得不能移動了,也不能改變視窗大小了。這確實是個問題,該怎麼去解決呢?

    首先我們來解決視窗移動的問題:解決思路就是在滑鼠按下事件中記錄按下的位置QPoint pLast, 並標記滑鼠狀態m_bPressed  = true; 為按下狀態。

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    this->setFocus();
    if(Qt::LeftButton == event->button() && 0 == (Qt::WindowMaximized & this->windowState()))
    {
        QPoint temp=event->globalPos();
        pLast=temp;    //記錄滑鼠按下的位置
        event->ignore();
    }
    m_bPressed = true; //標記滑鼠為按下狀態
}

    然後在滑鼠移動事件中進行窗體移動

void MainWindow::mouseMoveEvent(QMouseEvent * event)
{
    if(this->isMaximized()) //如果當前是最大化,則不允許移動
        return; 
    
    if((event->buttons() & Qt::LeftButton) && m_bPressed)//是否左擊
    {
        QPoint ptemp=event->globalPos();          //當前滑鼠全域性位置      
        ptemp=ptemp-pLast;                        //計算移動變數
        ptemp=ptemp+pos();                        //視窗原始位置(pos()) + 滑鼠移動變數(ptemp) = 最終視窗位置
        move(ptemp);                              //移動視窗到新的位置
    }
}

最後需要在滑鼠鬆開事件中進行狀態復位

void MainWindow::mouseReleaseEvent(QMouseEvent * event)
{
    QApplication::restoreOverrideCursor();//恢復滑鼠指標性狀
    event->ignore();

    m_bPressed = false;
}

到這裡,就已經全部解決了無邊框視窗可以動的問題。但是,但是我們視窗拉伸縮放,進行調節視窗大小的問題還沒有解決,這裡該怎麼解決呢?首先我們將視窗的區域進行劃分,如下圖


這裡我們將一個視窗劃分為9個區域,分別為

    左上角(1,1)、中上(1,2)、右上角(1,3)

    左中  (2,1)、 中間(2,2)、右中  (2,3)

    左下角(3,1)、中下(3,2)、 右下角(3,3)

    思路應該是這樣:

    1.當滑鼠移動時候,我們首先判斷這個游標處於哪個區域

    2.當滑鼠移動到不同區域,滑鼠的形狀是不同變化的。

    3.根據(2,2)之外的區域,計算初滑鼠移動的偏移量,來重新設定視窗的座標。

    比如,滑鼠進入區域(2,2)的時候,說明是移動視窗的操作,而不是進行視窗縮放的操作。除了區域(2,2)之外,其他8個區域均是進行視窗縮放的,但是處於不同區域內,滑鼠的形狀是不一樣。

    看程式碼

void MainWindow::mouseMoveEvent(QMouseEvent * event)
{
    if(this->isMaximized()) //如果最大化,則不允許移動和拉伸
        return;

    int poss=countFlag(event->pos(),countRow(event->pos()));//計算出來滑鼠在哪個區域

    if(!event->buttons())
        setCursorType(poss);//根據不同的區域設定不同的滑鼠形狀

    if((event->buttons() & Qt::LeftButton) && m_bPressed)//是否左擊
    {
        QPoint ptemp=event->globalPos();
        ptemp=ptemp-pLast;  //滑鼠移動的偏移量
        if(m_curPos==22)    //區域(2,2)表示移動視窗
        {
            ptemp=ptemp+pos();
            move(ptemp);
        }
        else
        {
            QRect wid=geometry();

            int minWidth = this->minimumWidth();
            int minHeight = this->minimumHeight();

            switch(m_curPos)//改變視窗的大小
            {
                case 11:
                {
                    QPoint pos = wid.topLeft();

                    if(wid.width() > minWidth || ptemp.x() < 0)
                        pos.rx() = pos.rx() + ptemp.x();
                    if(wid.height() > minHeight || ptemp.y() < 0)
                        pos.ry() = pos.ry() + ptemp.y();

                    wid.setTopLeft(pos);
                    break;//左上角
                }
                case 13:
                {
                    QPoint pos = wid.topRight();

                    if(wid.width() > minWidth || ptemp.x() > 0)
                        pos.rx() = pos.rx() + ptemp.x();
                    if(wid.height() > minHeight || ptemp.y() < 0)
                        pos.ry() = pos.ry() + ptemp.y();

                    wid.setTopRight(pos);
                    break;//右上角
                }
                case 31:
                {
                    QPoint pos = wid.bottomLeft();

                    if(wid.width() > minWidth || ptemp.x() < 0)
                        pos.rx() = pos.rx() + ptemp.x();
                    if(wid.height() > minHeight || ptemp.y() > 0)
                        pos.ry() = pos.ry() + ptemp.y();

                    wid.setBottomLeft(pos);
                    break;//左下角
                }
                case 33:
                {
                    QPoint pos = wid.bottomRight();

                    if(wid.width() > minWidth || ptemp.x() > 0)
                        pos.rx() = pos.rx() + ptemp.x();
                    if(wid.height() > minHeight || ptemp.y() > 0)
                        pos.ry() = pos.ry() + ptemp.y();

                    wid.setBottomRight(pos);
                    break;//右下角
                }
                case 12:
                {
                    int topY = wid.top();
                    if(wid.height() > minHeight || ptemp.y() < 0)
                        topY = topY + ptemp.y();

                    wid.setTop(topY);
                    break;//中上角
                }
                case 21:
                {
                    int leftX = wid.left();

                    if(wid.width() > minWidth || ptemp.x() < 0)
                        leftX = leftX + ptemp.x();

                    wid.setLeft(leftX);
                    break;//中左角
                }
                case 23:
                {
                    int rightX = wid.right();

                    if(wid.width() > minWidth || ptemp.x() > 0)
                        rightX = rightX + ptemp.x();

                    wid.setRight(rightX);
                    break;//中右角
                }
                case 32:
                {
                    int botY = wid.bottom();
                    if(wid.height() > minHeight || ptemp.y() > 0)
                        botY = botY + ptemp.y();

                    wid.setBottom(botY);
                    break;//中下角
                }
            }
            setGeometry(wid);   //設定視窗的位置
        }
        pLast=event->globalPos();//更新位置
    }
    event->ignore();

}

計算滑鼠在哪個區域

int MainWindow::countFlag(QPoint p,int row)//計算滑鼠在哪一列和哪一行
{
    if(p.y()<MARGIN)
        return 10+row;
    else if(p.y()>this->height()-MARGIN)
        return 30+row;
    else
        return 20+row;
}

根據滑鼠所在位置改變滑鼠形狀

void MainWindow::setCursorType(int flag)//根據滑鼠所在位置改變滑鼠指標形狀
{
    Qt::CursorShape cursor;
    switch(flag)
    {
    case 11:
    case 33:
        cursor=Qt::SizeFDiagCursor;break;
    case 13:
    case 31:
        cursor=Qt::SizeBDiagCursor;break;
    case 21:
    case 23:
        cursor=Qt::SizeHorCursor;break;
    case 12:
    case 32:
        cursor=Qt::SizeVerCursor;break;
    case 22:
        cursor=Qt::ArrowCursor;break;
    default:
       //  QApplication::restoreOverrideCursor();//恢復滑鼠指標性狀
         break;

    }
    setCursor(cursor);
}

到這裡,就可以正常的進行視窗拉伸進行視窗縮放了。

截止現在,已經完成了一個可拉伸,可移動的無邊框視窗。

這裡還有個隱患,就是設定了無邊框視窗之後,裡面的內容就不會及時重新整理了

setWindowFlags(Qt::FramelessWindowHint)

我們應該怎麼做呢?需要重寫顯示事件

void MainWindow::showEvent(QShowEvent* event)
{
    this->setAttribute(Qt::WA_Mapped);//解決不能及時重新整理的bug
    QMainWindow::showEvent(event);
}

這裡就完美的解決了所有的問題。