Qt日記(3)-360新版特性介面實現(3)

pamxy發表於2013-06-08

轉自:http://blog.csdn.net/gzshun/article/details/7626756

1.畫關閉按鈕
拖動圖片的功能放後面說,先把關閉按鈕給畫出來。
現在很多軟體,類似QQ,360,91助手......等等軟體,介面上都有很多很漂亮的按鈕,當滑鼠移上去,按鈕變為高亮,當滑鼠點選下去,按鈕變為暗色凹下,當滑鼠移開,按鈕又恢復原來的樣子。
這效果都是由多張圖片切換而來的。

new出一個QToolButton,設定關閉按鈕圖示。按鈕圖片一般都以4種狀態提供,將4張圖片拼成1張png。所以每次只需取出圖片中的1/4就行,然後將關閉按鈕移動到右上角。

想在Qt中實現這3種效果,很簡單,只需在窗體中安裝事件過濾器即可。
涉及到函式:eventFilter, installEventFilter.
幾種按鈕的enum:

  1. enum EButtonMouseState  
  2. {  
  3.     EButtonMouseDefault = 0,  
  4.     EButtonMouseEnter,  
  5.     EButtonMousePress,  
  6.     EButtonMouseNone  
  7. };  
  8. void Preview360::createEventFilter()  
  9. {  
  10.     //安裝事件過濾器   
  11.     m_pButtonClose->installEventFilter(this);  
  12. }  
  13. //設定按鈕圖示   
  14. void Preview360::setButtonIcon(QToolButton *btn, EButtonMouseState state)  
  15. {  
  16.     QPixmap pixmap(":/images/btn_close.png");  
  17.     int nWidth = pixmap.width()/4;  
  18.     int nHeight = pixmap.height();  
  19.     btn->setIcon(QIcon(pixmap.copy(QRect(state * nWidth, 0, nWidth, nHeight))));  
  20.     btn->setIconSize(QSize(nWidth, nHeight));  
  21. }  
  22. bool Preview360::eventFilter(QObject *target, QEvent *event)  
  23. {  
  24.     EButtonMouseState state = EButtonMouseNone;  
  25.     //對幾種事件進行處理,這下載對Qt的事件過濾器,很清楚了,實踐結合理論=...   
  26.     if (target == m_pButtonClose)  
  27.     {  
  28.         if (event->type() == QEvent::Enter)  
  29.         {  
  30.             state = EButtonMouseEnter;  
  31.         }  
  32.         else if (event->type() == QEvent::Leave)  
  33.         {  
  34.             state = EButtonMouseDefault;  
  35.         }  
  36.         else if (((QMouseEvent *)event)->button() == Qt::LeftButton)  
  37.         {  
  38.             if (event->type() == QEvent::MouseButtonPress)  
  39.             {  
  40.                 state = EButtonMousePress;  
  41.                 //若點選在關閉按鈕上,不拖動影象   
  42.                 m_mousePressFlag = false;  
  43.             }  
  44.             else if (event->type() == QEvent::MouseButtonRelease)  
  45.             {  
  46.                 state = EButtonMouseDefault;  
  47.             }  
  48.         }  
  49.   
  50.         if (state != EButtonMouseNone)  
  51.         {  
  52.             setButtonIcon((QToolButton *)target, state);  
  53.         }  
  54.     }  
  55.     //處理完自定義攔截的事件,還要呼叫一次QWidget預設的事件過濾函式   
  56.     return QWidget::eventFilter(target, event);  
  57. }  
enum EButtonMouseState
{
    EButtonMouseDefault = 0,
    EButtonMouseEnter,
    EButtonMousePress,
    EButtonMouseNone
};
void Preview360::createEventFilter()
{
    //安裝事件過濾器
    m_pButtonClose->installEventFilter(this);
}
//設定按鈕圖示
void Preview360::setButtonIcon(QToolButton *btn, EButtonMouseState state)
{
    QPixmap pixmap(":/images/btn_close.png");
    int nWidth = pixmap.width()/4;
    int nHeight = pixmap.height();
    btn->setIcon(QIcon(pixmap.copy(QRect(state * nWidth, 0, nWidth, nHeight))));
    btn->setIconSize(QSize(nWidth, nHeight));
}
bool Preview360::eventFilter(QObject *target, QEvent *event)
{
    EButtonMouseState state = EButtonMouseNone;
    //對幾種事件進行處理,這下載對Qt的事件過濾器,很清楚了,實踐結合理論=...
    if (target == m_pButtonClose)
    {
        if (event->type() == QEvent::Enter)
        {
            state = EButtonMouseEnter;
        }
        else if (event->type() == QEvent::Leave)
        {
            state = EButtonMouseDefault;
        }
        else if (((QMouseEvent *)event)->button() == Qt::LeftButton)
        {
            if (event->type() == QEvent::MouseButtonPress)
            {
                state = EButtonMousePress;
                //若點選在關閉按鈕上,不拖動影象
                m_mousePressFlag = false;
            }
            else if (event->type() == QEvent::MouseButtonRelease)
            {
                state = EButtonMouseDefault;
            }
        }

        if (state != EButtonMouseNone)
        {
            setButtonIcon((QToolButton *)target, state);
        }
    }
    //處理完自定義攔截的事件,還要呼叫一次QWidget預設的事件過濾函式
    return QWidget::eventFilter(target, event);
}


2.窗體的移動

窗體和圖片的移動涉及到滑鼠事件,只需事件滑鼠的一些事件就可以達到預期的效果,主要的事件是:

  1. void mousePressEvent(QMouseEvent *);  
  2. void mouseReleaseEvent(QMouseEvent *);  
  3. void mouseMoveEvent(QMouseEvent *);  
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);


3.圖片的移動
在操作上,有3個涉及到圖片的移動:
(1).滑鼠拖動圖片
(2).滑鼠拖動圖片後,釋放滑鼠,圖片恢復原位
(3).右鍵滑鼠,圖片右移
(4).鍵盤方向鍵控制圖片的移動
(5).點選介面的4個按鈕,分別移動到按鈕對應的介面

4.關於滑鼠控制的移動

這些主要是涉及到一些邏輯的程式碼,直接貼出:

  1. void Preview360::mousePressEvent(QMouseEvent *e)  
  2. {  
  3.     if (e->button() == Qt::LeftButton)  
  4.     {  
  5.         m_mouseSrcPos = e->pos();  
  6.         //在y座標小於40的區域,滑鼠拖動使窗體移動   
  7.         if (m_mouseSrcPos.y() <= 40)  
  8.         {  
  9.             m_mouseMoveWindowFlag = true;  
  10.         }  
  11.         else  
  12.         {  
  13.         //大於40的區域,滑鼠拖動使圖片移動   
  14.             m_currentFgXpos = m_pLabelFgTotal->x();  
  15.             m_mousePressFlag = true;  
  16.         }  
  17.     }  
  18.     //右鍵滑鼠,右移圖片   
  19.     else if (e->button() == Qt::RightButton)  
  20.     {  
  21.         if (getLabelMove())  
  22.         {  
  23.             if (m_currentFgIndex > 0)  
  24.             {  
  25.                 m_currentFgIndex--;  
  26.                 moveCurrentPage(false); //右移   
  27.             }  
  28.         }  
  29.     }  
  30. }  
  31.   
  32. void Preview360::mouseReleaseEvent(QMouseEvent *e)  
  33. {  
  34.     int xpos = 0;  
  35.   
  36.     if (m_mousePressFlag)  
  37.     {  
  38.         if (getLabelMove())  
  39.         {  
  40.             m_mouseDstPos = e->pos();  
  41.   
  42.             xpos = m_mouseDstPos.x() - m_mouseSrcPos.x();  
  43.   
  44.             if (xpos > 0)//右移   
  45.             {  
  46.                 if (xpos >= WINDOW_ONEBUTTON_WIDTH)  
  47.                 {  
  48.                     if (m_currentFgIndex > 0)  
  49.                     {  
  50.                         m_currentFgIndex--;  
  51.                         moveCurrentPage(false); //右移   
  52.                     }  
  53.                     else  
  54.                     {  
  55.                         moveCurrentPage(true); //左移   
  56.                     }  
  57.                 }  
  58.                 else  
  59.                 {  
  60.                     moveCurrentPage(true); //左移   
  61.                 }  
  62.             }  
  63.             else //左移   
  64.             {  
  65.                 if (xpos <= -WINDOW_ONEBUTTON_WIDTH)  
  66.                 {  
  67.                     if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)  
  68.                     {  
  69.                         m_currentFgIndex++;  
  70.                         moveCurrentPage(true); //左移   
  71.                     }  
  72.                     else  
  73.                     {  
  74.                         moveCurrentPage(false); //右移   
  75.                     }  
  76.                 }  
  77.                 else  
  78.                 {  
  79.                     moveCurrentPage(false); //右移   
  80.                 }  
  81.             }  
  82.   
  83.             m_mousePressFlag = false;  
  84.         }  
  85.     }  
  86.     else if (m_mouseMoveWindowFlag)  
  87.     {  
  88.         m_mouseMoveWindowFlag = false;  
  89.     }  
  90. }  
  91.   
  92. void Preview360::mouseMoveEvent(QMouseEvent *e)  
  93. {  
  94.     int x = 0;  
  95.   
  96.     if (m_mousePressFlag)  
  97.     {  
  98.         if (getLabelMove())  
  99.         {  
  100.             m_mouseDstPos = e->pos();  
  101.             x = m_mouseDstPos.x() - m_mouseSrcPos.x();  
  102.   
  103.             setLabelMove(false);  
  104.             m_pLabelFgTotal->move(m_currentFgXpos + x, WINDOW_START_Y);  
  105.             setLabelMove(true);  
  106.         }  
  107.     }  
  108.     else if (m_mouseMoveWindowFlag)  
  109.     {  
  110.         m_mouseDstPos = e->pos();  
  111.         this->move(this->pos() + m_mouseDstPos - m_mouseSrcPos);  
  112.     }  
  113. }  
void Preview360::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
    {
        m_mouseSrcPos = e->pos();
        //在y座標小於40的區域,滑鼠拖動使窗體移動
        if (m_mouseSrcPos.y() <= 40)
        {
            m_mouseMoveWindowFlag = true;
        }
        else
        {
        //大於40的區域,滑鼠拖動使圖片移動
            m_currentFgXpos = m_pLabelFgTotal->x();
            m_mousePressFlag = true;
        }
    }
    //右鍵滑鼠,右移圖片
    else if (e->button() == Qt::RightButton)
    {
        if (getLabelMove())
        {
            if (m_currentFgIndex > 0)
            {
                m_currentFgIndex--;
                moveCurrentPage(false); //右移
            }
        }
    }
}

void Preview360::mouseReleaseEvent(QMouseEvent *e)
{
    int xpos = 0;

    if (m_mousePressFlag)
    {
        if (getLabelMove())
        {
            m_mouseDstPos = e->pos();

            xpos = m_mouseDstPos.x() - m_mouseSrcPos.x();

            if (xpos > 0)//右移
            {
                if (xpos >= WINDOW_ONEBUTTON_WIDTH)
                {
                    if (m_currentFgIndex > 0)
                    {
                        m_currentFgIndex--;
                        moveCurrentPage(false); //右移
                    }
                    else
                    {
                        moveCurrentPage(true); //左移
                    }
                }
                else
                {
                    moveCurrentPage(true); //左移
                }
            }
            else //左移
            {
                if (xpos <= -WINDOW_ONEBUTTON_WIDTH)
                {
                    if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)
                    {
                        m_currentFgIndex++;
                        moveCurrentPage(true); //左移
                    }
                    else
                    {
                        moveCurrentPage(false); //右移
                    }
                }
                else
                {
                    moveCurrentPage(false); //右移
                }
            }

            m_mousePressFlag = false;
        }
    }
    else if (m_mouseMoveWindowFlag)
    {
        m_mouseMoveWindowFlag = false;
    }
}

void Preview360::mouseMoveEvent(QMouseEvent *e)
{
    int x = 0;

    if (m_mousePressFlag)
    {
        if (getLabelMove())
        {
            m_mouseDstPos = e->pos();
            x = m_mouseDstPos.x() - m_mouseSrcPos.x();

            setLabelMove(false);
            m_pLabelFgTotal->move(m_currentFgXpos + x, WINDOW_START_Y);
            setLabelMove(true);
        }
    }
    else if (m_mouseMoveWindowFlag)
    {
        m_mouseDstPos = e->pos();
        this->move(this->pos() + m_mouseDstPos - m_mouseSrcPos);
    }
}


5.點選介面4個按鈕進行移動

  1. void Preview360::slotChangeCurrentPage(CLabel *label)  
  2. {  
  3.     int index = 0;  
  4.   
  5.     for (int i = 0; i < WINDOW_PAGE_COUNT; i++)  
  6.     {  
  7.         if (label == m_pLabelBtnArray[i])  
  8.         {  
  9.         //找出當前按鈕的序號   
  10.             index = i;  
  11.             break;  
  12.         }  
  13.     }  
  14.   
  15.     //移動的幾種可能性,對於x座標   
  16.     //index=0, 將label移動到-680*0   
  17.     //index=1, 將label移動到-680*1   
  18.     //index=2, 將label移動到-680*2   
  19.     //index=3, 將label移動到-680*3   
  20.     //點選左邊的按鈕 右移   
  21.     if (index < m_currentFgIndex)  
  22.     {  
  23.         while(index != m_currentFgIndex)  
  24.         {  
  25.             m_currentFgIndex--;  
  26.             moveCurrentPage(false);  
  27.         }  
  28.     }  
  29.     else if (index > m_currentFgIndex) //點選右邊的按鈕 左移   
  30.     {  
  31.         while(index != m_currentFgIndex)  
  32.         {  
  33.             m_currentFgIndex++;  
  34.             moveCurrentPage(true);  
  35.         }  
  36.     }  
  37.     else  
  38.     {  
  39.         //...   
  40.     }  
  41. }  
void Preview360::slotChangeCurrentPage(CLabel *label)
{
    int index = 0;

    for (int i = 0; i < WINDOW_PAGE_COUNT; i++)
    {
        if (label == m_pLabelBtnArray[i])
        {
        //找出當前按鈕的序號
            index = i;
            break;
        }
    }

    //移動的幾種可能性,對於x座標
    //index=0, 將label移動到-680*0
    //index=1, 將label移動到-680*1
    //index=2, 將label移動到-680*2
    //index=3, 將label移動到-680*3
    //點選左邊的按鈕 右移
    if (index < m_currentFgIndex)
    {
        while(index != m_currentFgIndex)
        {
            m_currentFgIndex--;
            moveCurrentPage(false);
        }
    }
    else if (index > m_currentFgIndex) //點選右邊的按鈕 左移
    {
        while(index != m_currentFgIndex)
        {
            m_currentFgIndex++;
            moveCurrentPage(true);
        }
    }
    else
    {
        //...
    }
}


6.鍵盤方向鍵的控制
捕捉對應的按鍵事件,只需重新實現鍵盤事件:
void keyPressEvent(QKeyEvent *);
左上鍵:圖片右移
右下鍵:圖片左移

  1. void Preview360::keyPressEvent(QKeyEvent *e)  
  2. {  
  3.     if (getLabelMove())  
  4.     {  
  5.         switch(e->key())  
  6.         {  
  7.         case Qt::Key_Left:  
  8.         case Qt::Key_Up:  
  9.             if (m_currentFgIndex > 0)  
  10.             {  
  11.                 m_currentFgIndex--;  
  12.                 moveCurrentPage(false); //右移   
  13.             }  
  14.             break;  
  15.   
  16.         case Qt::Key_Right:  
  17.         case Qt::Key_Down:  
  18.             if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)  
  19.             {  
  20.                 m_currentFgIndex++;  
  21.                 moveCurrentPage(true); //左移   
  22.             }  
  23.             break;  
  24.   
  25.         default:  
  26.             break;  
  27.         }  
  28.     }  
  29. }  
void Preview360::keyPressEvent(QKeyEvent *e)
{
    if (getLabelMove())
    {
        switch(e->key())
        {
        case Qt::Key_Left:
        case Qt::Key_Up:
            if (m_currentFgIndex > 0)
            {
                m_currentFgIndex--;
                moveCurrentPage(false); //右移
            }
            break;

        case Qt::Key_Right:
        case Qt::Key_Down:
            if (m_currentFgIndex < WINDOW_PAGE_COUNT-1)
            {
                m_currentFgIndex++;
                moveCurrentPage(true); //左移
            }
            break;

        default:
            break;
        }
    }
}


7.圖片移動的關鍵函式
移動圖片的具體函式,要緩慢的移動圖片,也就是在迴圈裡面延遲一下,並響應UI事件,防止介面凍結(未響應)。
在程式中,出現setLabelMove和getLabelMove函式,這兩個函式主要是用來防止多個事件同時移動介面,導致錯誤。所以這裡相當於給圖片的移動加鎖,保證移動操作的唯一性。

  1. void Preview360::moveCurrentPage(bool direction)  
  2. {  
  3.     int currentXpos = 0;//當前label的x座標   
  4.     int destXpos = 0;//目標x座標   
  5.   
  6.     //改變當前頁面對應的按鈕   
  7.     changeCurrentButton();  
  8.   
  9.     //圖片的幾個分割點   
  10.     //0-680, 680-1360, 1360-2040, 2040-2720   
  11.     //真:向左移;  假:向右移   
  12.     if (direction)  
  13.     {  
  14.         //左移的幾種可能性,對於x座標   
  15.         //index=0, 將label移動到-680*0   
  16.         //index=1, 將label移動到-680*1   
  17.         //index=2, 將label移動到-680*2   
  18.         //index=3, 將label移動到-680*3   
  19.         setLabelMove(false);  
  20.         currentXpos = m_pLabelFgTotal->x();  
  21.         destXpos = -WINDOW_WIDTH * m_currentFgIndex;  
  22.         while(currentXpos > destXpos)  
  23.         {  
  24.             m_pLabelFgTotal->move(currentXpos-WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  25.             currentXpos = m_pLabelFgTotal->x();  
  26.             qApp->processEvents(QEventLoop::AllEvents);  
  27.         }  
  28.         m_pLabelFgTotal->move(destXpos, WINDOW_START_Y);  
  29.         setLabelMove(true);  
  30.     }  
  31.     else  
  32.     {  
  33.         //右移的幾種可能性,對於x座標,與左移一致   
  34.         //index=0, 將label移動到-680*0   
  35.         //index=1, 將label移動到-680*1   
  36.         //index=2, 將label移動到-680*2   
  37.         //index=3, 將label移動到-680*3   
  38.         setLabelMove(false);  
  39.         currentXpos = m_pLabelFgTotal->x();  
  40.         destXpos = -WINDOW_WIDTH * m_currentFgIndex;  
  41.         while(currentXpos < destXpos)  
  42.         {  
  43.             m_pLabelFgTotal->move(currentXpos+WINDOW_PAGE_MOVE, WINDOW_START_Y);  
  44.             currentXpos = m_pLabelFgTotal->x();  
  45.             qApp->processEvents(QEventLoop::AllEvents);  
  46.         }  
  47.         m_pLabelFgTotal->move(destXpos, WINDOW_START_Y);  
  48.         setLabelMove(true);  
  49.     }  
  50. }  
void Preview360::moveCurrentPage(bool direction)
{
    int currentXpos = 0;//當前label的x座標
    int destXpos = 0;//目標x座標

    //改變當前頁面對應的按鈕
    changeCurrentButton();

    //圖片的幾個分割點
    //0-680, 680-1360, 1360-2040, 2040-2720
    //真:向左移;  假:向右移
    if (direction)
    {
        //左移的幾種可能性,對於x座標
        //index=0, 將label移動到-680*0
        //index=1, 將label移動到-680*1
        //index=2, 將label移動到-680*2
        //index=3, 將label移動到-680*3
        setLabelMove(false);
        currentXpos = m_pLabelFgTotal->x();
        destXpos = -WINDOW_WIDTH * m_currentFgIndex;
        while(currentXpos > destXpos)
        {
            m_pLabelFgTotal->move(currentXpos-WINDOW_PAGE_MOVE, WINDOW_START_Y);
            currentXpos = m_pLabelFgTotal->x();
            qApp->processEvents(QEventLoop::AllEvents);
        }
        m_pLabelFgTotal->move(destXpos, WINDOW_START_Y);
        setLabelMove(true);
    }
    else
    {
        //右移的幾種可能性,對於x座標,與左移一致
        //index=0, 將label移動到-680*0
        //index=1, 將label移動到-680*1
        //index=2, 將label移動到-680*2
        //index=3, 將label移動到-680*3
        setLabelMove(false);
        currentXpos = m_pLabelFgTotal->x();
        destXpos = -WINDOW_WIDTH * m_currentFgIndex;
        while(currentXpos < destXpos)
        {
            m_pLabelFgTotal->move(currentXpos+WINDOW_PAGE_MOVE, WINDOW_START_Y);
            currentXpos = m_pLabelFgTotal->x();
            qApp->processEvents(QEventLoop::AllEvents);
        }
        m_pLabelFgTotal->move(destXpos, WINDOW_START_Y);
        setLabelMove(true);
    }
}

防止介面凍結:
qApp->processEvents(QEventLoop::AllEvents);
該函式用來處理所有事件,保證了介面的正常執行。


8.將控制元件置頂
在最後補充下,要將需要的控制元件raise到棧頂:

  1. //raise these widget   
  2. m_pLabelBg1->raise();  
  3. m_pButtonClose->raise();  
  4.   
  5. for (int i = 0; i < WINDOW_BUTTON_COUNT; i++)  
  6. {  
  7.     m_pLabelBtnArray[i]->raise();  
  8. }  
//raise these widget
m_pLabelBg1->raise();
m_pButtonClose->raise();

for (int i = 0; i < WINDOW_BUTTON_COUNT; i++)
{
    m_pLabelBtnArray[i]->raise();
}

多個控制元件涉及到重疊的問題,只有這樣重新調整棧順序才能達到需要的效果。

原始碼下載地址:360新版特性介面

作者  : gzshun.
E-Mail: gzshuns#163.com (@)
轉載請註明出處:http://blog.csdn.net/gzshun/article/details/7626756

相關文章