Qt编写控件时遇到underMouse判断错误的情况(误判State_MouseOver)

在写qt下的ribbon控件时,重绘了一个toolbutton,但是却遇到一个问题就是在有弹出菜单模式下,点击菜单后按钮还处于hover状态,什么意思,就是如图所示:

Qt编写控件时遇到underMouse判断错误的情况(误判State_MouseOver)_第1张图片

源代码见: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(State_Enabled|State_Raised|State_AutoRaise|State_MouseOver) , QRect(0,0 105x78) , SARibbonToolButton(0x4b6eee8, name = "MenuButtonPopup") ) ) 
  QStyleOptionComplex:
     subControls( QFlags(SC_MdiMinButton|SC_MdiNormalButton) ) 
     activeSubControls( QFlags(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(State_Enabled|State_Raised|State_AutoRaise|State_MouseOver) , QRect(0,0 105x78) , SARibbonToolButton(0x41eeee8, name = "MenuButtonPopup") ) ) 
  QStyleOptionComplex:
     subControls( QFlags(SC_MdiMinButton|SC_MdiNormalButton) ) 
     activeSubControls( QFlags(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;
        }
    }
......
}

暂时解决此问题

你可能感兴趣的:(qt)