• 18278阅读
  • 3回复

[提问]QTooltip显示和隐藏 [复制链接]

上一主题 下一主题
离线wangjieest
 

只看楼主 倒序阅读 楼主  发表于: 2012-09-27

现在对于这个过程还有一个疑问,就是它是如何定时消失掉的呢?


QTooltip
QApplication会把延时未动鼠标的事件QEvent::ToolTip发送到 QWidget..
然后QWidget在事件中判断!d->QTooltip.isEmpty()是否为空.
空的话就ignore这个事件.
非空就通过QHelpEvent得到鼠标坐标.(static_cast<QHelpEvent*>(event)->globalPos())并显示出来.
[size=; FONT-SIZE: 9pt,9pt][font='YaHei Consolas Hybrid']可以不定义类对象,而直接使用类的成员函数,一般使用静态成员函数,不是thiscall调用.所以也不需要有对象的this指针.

[size=; FONT-SIZE: 9pt,9pt][font='YaHei Consolas Hybrid']


Qt 的tool tip


今天要给我做的qt控件加个tooltip,嗯,应该不难的。不过,还是先把tooltip的来龙去脉弄清楚先的。

QEvent::ToolTip的前端.来自MouseMove中的定时器

QApplication中有一个叫作toolTipWakeUp的定时器,当鼠标移动到某个QWidget上一段时间不动后它便会被触发。当鼠标离此QWidget时,它便会被停止。
有MouseMove才能触发该定时器.例如在其他触发定时器关闭的动作后没有移动鼠标,那么定时器就没有启动.
每次MouseMove重新设置定时器.
定时器时间设为20呢?每次都达不到
相关代码在 QApplication::notify 函数中:
//处理QEvent::MouseMove消息,
//在鼠标移动事件里,每次都重新设定WakeUp定时器(因为移动了)
//在鼠标其他事件中,每次都停止WakeUp定时器(因为有其他鼠标事件产生了)
//在WakeUp定时器响应中,关闭此定时器,开启FallAsleep定时器,
//针对这个定时器id,生成事件QEvent::ToolTip.发送到QWidget上
//并且启用FallAsleep定时器用于控制ToolTip显示时长,此时d->toolTipFallAsleep.isActive()为激活状态
//鼠标再移动的话,激活的是20ms定时器,而20ms过小,都不会得到及时的处理而阻断了该定时器的功能?(因为已经显示出来了)
//而在FallAsleep响应中,关闭定时器,并且关闭Tooltip的显示(没有).然后开始新一轮的响应了.

//那么这个ToolTip显示是如何被其他事件阻断的呢?使定时器睡眠就能关闭ToolTip的显示?
//是在那个20ms的定时器里,先判断FallAsleep是否激活,然后判断一轮这些事件,有了就关闭么?
//为什么系统的ToolTip要经过很长延时才被关闭了,再哪儿关闭的?

// bool QApplication::notify(QObject *receiver, QEvent*e)内
    case QEvent::MouseMove: 内

if (e->type() == QEvent::MouseMove && mouse->buttons() == 0)
{
    d->toolTipWidget = w;
    d->toolTipPos =relpos;
    d->toolTipGlobalPos =mouse->globalPos();
    d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?20:700, this);
}

//如果在过程中出现下列事件,则使睡眠定时器
// User input and windowactivation makes tooltips sleep
switch (e->type()) {
    caseQEvent::Wheel:
    case QEvent::ActivationChange:
    case QEvent::KeyPress:
    case QEvent::KeyRelease:
    case QEvent::FocusOut:
    case QEvent::FocusIn:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
        d->toolTipFallAsleep.stop();
        // fall-through
    case QEvent::Leave:
        d->toolTipWakeUp.stop();
    default:
        break;
}



QEvent::ToolTip的发送:sendEvent()
tooltip事件是由上面提到的定时器触发的。
并在 QApplication::Event 函数中通过响应对应的定时器来发送到指定的窗口
        if (te->timerId() == d->toolTipWakeUp.timerId()){
            d->toolTipWakeUp.stop();// 先停止WakeUp定时器
            if (d->toolTipWidget) {
                QWidget *w = d->toolTipWidget->window();
                //show tooltip if WA_AlwaysShowToolTips is set, orif
                // any ancestor ofd->toolTipWidget is the active
                // window
                bool showToolTip = w->testAttribute(Qt::WA_AlwaysShowToolTips);// 判断该属性,如果有就直接跳过下面的while了.
                while (w && !showToolTip) {// 判断是否有容纳ToolTip的控件
                    showToolTip = w->isActiveWindow(); //如果没被激活
                    w = w->parentWidget();             //指向父窗口
                    w = w ? w->window() : 0;           //父窗口不是顶级窗口
                }
                if (showToolTip) { // 如果有
                    QHelpEvent e(QEvent::ToolTip, d->toolTipPos, d->toolTipGlobalPos); // 则生成ToolTip事件
                    QApplication::sendEvent(d->toolTipWidget, &e); // 发送之
                    if (e.isAccepted()) // 如果被某一个Widget接受了
                        d->toolTipFallAsleep.start(2000, this); // 以2000ms启动FallAsleep计时器
                }
            }
        }else if(te->timerId() == d->toolTipFallAsleep.timerId()) {
            d->toolTipFallAsleep.stop();
        }



QWidget对 QEvent::ToolTip的响应
好吧,tool tip的事件已经知道是怎么发送到指定的QWidget了,
现在就看看QWiget是怎么处理ToolTip事件的,

QWidget::Event()的相关代码段如下:
case QEvent::ToolTip:
        if (!d->toolTip.isEmpty())
            QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(),d->toolTip, this);
        else
            event->ignore();
        break;


至于QToolTip自身又是怎么显示的,就不详细记述了。其实,就是显示一个QLabel
打完收工~


以下是自己实现的.


#include "tooltips1.h"
#include<QPushButton>
#include <QBasicTimer>
#include <QToolTip>
tooltips1::tooltips1(QWidget *parent,Qt::WFlags flags)
: QWidget(parent, flags)
{
    ui.setupUi(this);
    setToolTip("hello");
    QPushButton *btn = new QPushButton("hello",this);
    btn->show();
}
tooltips1::~tooltips1()
{
}

bool tooltips1::event(QEvent *e)
{
    if(e->type() == QEvent::ToolTip)
    {
        //头文件声明QBasicTimer timer;
        timer->start(500,this);
    //QToolTip::showText(static_cast<QHelpEvent*>(e)->globalPos(),"hello");
    }
    else if (e->type() == QEvent::Timer)
    {
        QTimerEvent *timerevent = static_cast<QTimerEvent*>(e);
        if(timerevent->timerId()== timer->timerId())
        {
            QToolTip::hideText();
            timer->stop();
        }
    }
    return QWidget::event(e);
}

现在对于这个过程还有一个疑问,就是它是如何定时消失掉的呢?
离线kimtaikee

只看该作者 1楼 发表于: 2012-09-27
我所看的QToolTip的源码是5.0 。源码如下:
  1. class Q_WIDGETS_EXPORT QToolTip
  2. {
  3.     QToolTip();
  4. public:
  5.     static void showText(const QPoint &pos, const QString &text, QWidget *w = 0);
  6.     static void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect);
  7.     static inline void hideText() { showText(QPoint(), QString()); }
  8.     static bool isVisible();
  9.     static QString text();
  10.     static QPalette palette();
  11.     static void setPalette(const QPalette &);
  12.     static QFont font();
  13.     static void setFont(const QFont &);
  14. };

你也看到了,上面隐藏tip的做法就是给showTip的函数传个空的QString,再看QToolTip的cpp文件中有一个QTipLabel专用类,定义如下:
  1. class QTipLabel : public QLabel
  2. {
  3.     Q_OBJECT
  4. public:
  5.     QTipLabel(const QString &text, QWidget *w);
  6.     ~QTipLabel();
  7.     static QTipLabel *instance;
  8.     bool eventFilter(QObject *, QEvent *);
  9.     QBasicTimer hideTimer, expireTimer;
  10.     bool fadingOut;
  11.     void reuseTip(const QString &text);
  12.     void hideTip();
  13.     void hideTipImmediately();
  14.     void setTipRect(QWidget *w, const QRect &r);
  15.     void restartExpireTimer();
  16.     bool tipChanged(const QPoint &pos, const QString &text, QObject *o);
  17.     void placeTip(const QPoint &pos, QWidget *w);
  18.     static int getTipScreen(const QPoint &pos, QWidget *w);
  19. protected:
  20.     void timerEvent(QTimerEvent *e);
  21.     void paintEvent(QPaintEvent *e);
  22.     void mouseMoveEvent(QMouseEvent *e);
  23.     void resizeEvent(QResizeEvent *e);
  24. #ifndef QT_NO_STYLE_STYLESHEET
  25. public slots:
  26.     /** \internal
  27.       Cleanup the _q_stylesheet_parent propery.
  28.      */
  29.     void styleSheetParentDestroyed() {
  30.         setProperty("_q_stylesheet_parent", QVariant());
  31.         styleSheetParent = 0;
  32.     }
  33. private:
  34.     QWidget *styleSheetParent;
  35. #endif
  36. private:
  37.     QWidget *widget;
  38.     QRect rect;
  39. };

下面是showText的实现,如果text是空的话就调用QTipLabel::instance->hideTip();
  1. void QToolTip::showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect)
  2. {
  3.     if (QTipLabel::instance && QTipLabel::instance->isVisible()){ // a tip does already exist
  4.        if (text.isEmpty()){ // empty text means hide current tip
  5.             QTipLabel::instance->hideTip();
  6.             return;
  7.         }
  8.         else if (!QTipLabel::instance->fadingOut){
  9. ...
  10. }

而hideTip的实现如下:
  1. void QTipLabel::hideTip()
  2. {
  3.     if (!hideTimer.isActive())
  4.         hideTimer.start(300, this);
  5. }

这个timer在timer事件处理函数中经过了处理,
  1. void QTipLabel::timerEvent(QTimerEvent *e)
  2. {
  3.     if (e->timerId() == hideTimer.timerId()
  4.         || e->timerId() == expireTimer.timerId()){
  5.         hideTimer.stop();
  6.         expireTimer.stop();
  7. #if defined(Q_WS_MAC) && !defined(QT_NO_EFFECTS)
  8.         if (QApplication::isEffectEnabled(Qt::UI_FadeTooltip)){
  9.             // Fade out tip on mac (makes it invisible).
  10.             // The tip will not be deleted until a new tip is shown.
  11.                         // DRSWAT - Cocoa
  12.                         macWindowFade(qt_mac_window_for(this));
  13.             QTipLabel::instance->fadingOut = true; // will never be false again.
  14.         }
  15.         else
  16.             hideTipImmediately();
  17. #else
  18.        hideTipImmediately();
  19. #endif
  20.     }
  21. }

hideTipImmediately()函数代码如下:
  1. void QTipLabel::hideTipImmediately()
  2. {
  3.     close(); // to trigger QEvent::Close which stops the animation
  4.     deleteLater();
  5. }




离线wangjieest

只看该作者 2楼 发表于: 2012-09-28
意思是QToolTip其实调用的QTipLabel,而QTipLabel内部还有有定时器,到一定时间就close()了?

        }else if(te->timerId() == d->toolTipFallAsleep.timerId()) {
            d->toolTipFallAsleep.stop();
        }
在这个事件到达此处之前先到达了 QTipLabel 而消失了...
thx...
离线foxgod

只看该作者 3楼 发表于: 2013-09-13
回 2楼(wangjieest) 的帖子
帅哥要是我直接在一个保存图片后想做个提示用tooltip,我用QTooltip的showtext(),
QToolTip::showText(QPoint(m_pParentFrame->rect().width()-100,m_pParentFrame->rect().height()-100),strFileName,0,rect);
可是这样停留时间很短了,怎么让他停留时间长点呢,还有怎么控制tooltip的长宽高呢。
还有就是
    palette.setColor(QPalette::ToolTipText,QColor(255,255,0));
    palette.setBrush(QPalette::ToolTipBase,QColor(255,255,0)
设置文字可以,设置背景就不可以了,不知道为什么。
快速回复
限100 字节
 
上一个 下一个