在写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(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;
}
}
......
}
暂时解决此问题