1. 程式人生 > >Qt編寫控制元件時遇到underMouse判斷錯誤的情況(誤判State_MouseOver)

Qt編寫控制元件時遇到underMouse判斷錯誤的情況(誤判State_MouseOver)

在寫qt下的ribbon控制元件時,重繪了一個toolbutton,但是卻遇到一個問題就是在有彈出選單模式下,點選選單後按鈕還處於hover狀態,什麼意思,就是如圖所示:

在這裡插入圖片描述

原始碼見:https://github.com/czyt1988/SARibbon/blob/master/src/SARibbonBar/SARibbonToolButton.cpp
MenuButtonPopup按鈕在選單彈出後應該是恢復正常,現卻處於State_MouseOver狀態

經過各種列印除錯檢視原始碼,最後發現原來是Qwidget::underMouse函式返回結果不對。

一開始以為自己狀態獲取不正確,為了更好列印輸出QStyleOptionToolButton

,還過載了一個QDebug operator<<(QDebug debug, const QStyleOptionToolButton &opt)函式:

QDebug operator<<(QDebug debug, const QStyleOptionToolButton &opt)
{
    debug << "=============="
          << "\nQStyleOption("
            << (QStyleOption)opt
            <<
")" << "\n QStyleOptionComplex:" "\n subControls(" << opt.subControls << " ) " "\n activeSubControls(" << opt.activeSubControls << "\n QStyleOptionToolButton" "\n features("
<< opt.features << ")" "\n toolButtonStyle(" << opt.toolButtonStyle << ")" ; return debug; }

Qt的控制元件繪圖通過initStyleOption函式獲取widget的當前狀態

    QPainter p(this);
    QStyleOptionToolButton opt;
    initStyleOption(&opt);

於是在懷疑有可能有問題的地方加了列印(請原諒我用列印這麼粗暴的方法進行除錯)
void SARibbonToolButton::paintLargeButton(QPaintEvent *e)下,專門針對MenuButtonPopup的objectname的widget進行列印:

void SARibbonToolButton::paintLargeButton(QPaintEvent *e)
{
    Q_UNUSED(e);

    QPainter p(this);
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
......
if(this->objectName() == "MenuButtonPopup")
{

    static int s_e = 0;
    qDebug() << s_e << "" << tool;
    ++s_e;
}
......
}

一開始,最後發現,滑鼠在點選單的時候,返回的狀態居然還有State_MouseOver

QStyleOption( QStyleOption( SO_Default , LeftToRight , QFlags<QStyle::StateFlag>(State_Enabled|State_Raised|State_AutoRaise|State_MouseOver) , QRect(0,0 105x78) , SARibbonToolButton(0x4b6eee8, name = "MenuButtonPopup") ) ) 
  QStyleOptionComplex:
     subControls( QFlags<QStyle::SubControl>(SC_MdiMinButton|SC_MdiNormalButton) ) 
     activeSubControls( QFlags<QStyle::SubControl>(SC_None) 
  QStyleOptionToolButton
     features( QFlags(0x4|0x10) )
     toolButtonStyle( Qt::ToolButtonStyle(ToolButtonTextUnderIcon) )

於是開始查原始碼:QToolButton::initStyleOption

void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
{
    if (!option)
        return;

    Q_D(const QToolButton);
    option->initFrom(this);
...
}

查:QStyleOption::initFrom在QStyleOption.cpp沒找到,發現是在標頭檔案行內函數:

inline void initFrom(const QWidget *w) { init(w); }

查:QStyleOption::init

void QStyleOption::init(const QWidget *widget)
{
    QWidget *window = widget->window();
    state = QStyle::State_None;
    if (widget->isEnabled())
        state |= QStyle::State_Enabled;
    if (widget->hasFocus())
        state |= QStyle::State_HasFocus;
    if (window->testAttribute(Qt::WA_KeyboardFocusChange))
        state |= QStyle::State_KeyboardFocusChange;
    if (widget->underMouse())
        state |= QStyle::State_MouseOver;
    if (window->isActiveWindow())
        state |= QStyle::State_Active;
    if (widget->isWindow())
        state |= QStyle::State_Window;
...
}

原來狀態State_MouseOver是通過widget->underMouse()來獲取的。於是反過來驗證QWidget::underMouse是否有問題:
把原來的列印改為:

if(this->objectName() == "MenuButtonPopup")
{

    static int s_e = 0;
    qDebug() << s_e << " under mouse:" << this->underMouse() 
             << " \ncontinue:" <<this->rect().contains(this->mapFromGlobal(QCursor::pos()))
             << " \n rect:" << this->geometry()
             << " QCursor:" << QCursor::pos()
             << " " <<autoRaise << opt;
    ++s_e;
}

結果輸出:

under mouse: true  
continue: false  
 rect: QRect(342,2 105x78) QCursor: QPoint(443,746) true ============== 
QStyleOption( QStyleOption( SO_Default , LeftToRight , QFlags<QStyle::StateFlag>(State_Enabled|State_Raised|State_AutoRaise|State_MouseOver) , QRect(0,0 105x78) , SARibbonToolButton(0x41eeee8, name = "MenuButtonPopup") ) ) 
  QStyleOptionComplex:
     subControls( QFlags<QStyle::SubControl>(SC_MdiMinButton|SC_MdiNormalButton) ) 
     activeSubControls( QFlags<QStyle::SubControl>(SC_None) 
  QStyleOptionToolButton
     features( QFlags(0x4|0x10) )
     toolButtonStyle( Qt::ToolButtonStyle(ToolButtonTextUnderIcon) )

under mouse結果和this->geometry().contains(QCursor::pos())結果不一樣,這是問題的關鍵
QToolButton自身並不會出現這個問題,不知道我改動哪裡導致了這個狀態獲取的異常
目前臨時的解決方法是加多一重判斷:

void SARibbonToolButton::paintLargeButton(QPaintEvent *e)
{
    Q_UNUSED(e);

    QPainter p(this);
    QStyleOptionToolButton opt;
    initStyleOption(&opt);
    if(opt.features & QStyleOptionToolButton::MenuButtonPopup
            ||
            opt.features & QStyleOptionToolButton::HasMenu
            )
    {
        if(!this->rect().contains(this->mapFromGlobal(QCursor::pos())))
        {
            opt.state &= ~QStyle::State_MouseOver;
        }
    }
......
}

暫時解決此問題