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;
}
}
......
}
暫時解決此問題