[SOLVED] Making QLabel hover-aware



  • Hi.

    I am trying to make a QLabel subclass react to a mouse hover action. What I want to achieve is a magnifying glass effect over a QLabel, but first thing first...

    I have already made it clickable, but I can't figure how to implement the hovering part. Should I be using the mouseMoveEvent() or am I looking in the wrong direction? It doesn't seem to react to that...

    [code]
    void myLabel::mouseMoveEvent(QMouseEvent *event)
    {
    Q_UNUSED(event);
    qDebug() << Q_FUNC_INFO << QString("This thing is being hovered");
    emit hovered();
    }
    [/code]



  • I can't give you a perfect solution, but a while ago I tried to achieve the same thing.

    Solution 1: Use a "QTimer":http://qt-project.org/doc/qt-5.1/qtcore/qtimer.html . I made it so that about every 50ms (should depend on the size of the label, too big of a delay doesn't feel good) the program checked if the mouse was actually hovering over a magnifiable label. If so, the label was magnified.

    Solution 2: Reimplement the enterEvent() of the label and emit a signal from within. Use that signal somehow to magnify things. I had to use a protective timer in addition, so the magnification wouldn't be launched more than once when moving the mouse rapidly.

    I think you may need to activate mouseTracking for the second solution to work.



  • Hello again.

    And sorry for the delay, real life has kept me a bit busy lately.

    I will be checking both options and report back how it worked (or not) out. Thank you for the suggestions. :)



  • So far, it seems to work, and it doesn't interfere with the clicked() signal which I also implemented and is being used for some other thing:

    @#include "mylabel.h"
    #include <QDebug>
    #include <QToolButton>

    myLabel::myLabel(QWidget * parent )
    :QLabel(parent)
    {
    connect( this, SIGNAL( clicked() ), this, SLOT( slotClicked() ) );
    setMouseTracking(true);
    }

    void myLabel::slotClicked()
    {
    qDebug()<<"Clicked";
    }

    void myLabel::mousePressEvent ( QMouseEvent * event )
    {
    Q_UNUSED(event);
    emit clicked();
    }

    void myLabel::mouseMoveEvent(QMouseEvent *event)
    {
    Q_UNUSED(event);
    qDebug() << Q_FUNC_INFO << QString("This thing is being hovered");
    emit hovered();
    }

    void myLabel::resizeEvent(QResizeEvent *event) {
    QWidget::resizeEvent(event);
    resizeImage();
    }

    void myLabel::resizeImage() {
    if(this->pixmap() != NULL)
    {
    qDebug() << Q_FUNC_INFO << QString("El tamaño de este widget es %1, %2").arg(size().width()).arg(size().height());
    setPixmap(pixmap()->scaled(size(), Qt::KeepAspectRatio/ByExpanding/, Qt::SmoothTransformation));
    }
    }

    void myLabel::enterEvent(QEvent *event)
    {
    qDebug() << Q_FUNC_INFO << "enter event triggered!";
    QLabel::enterEvent(event);
    }@

    When mouse tracking is enabled, the thing is triggered as I move, so this seems perfect for my purpose. Now I just have to figure how to cut a portion of the original image matching the mouse position in the miniature to redraw the pixmap as I move the mouse, but that's another story.

    I guess this is solved, I'll come back if I find some show stopper in the way. Thank you :)


  • Lifetime Qt Champion

    Hi,

    Did you try with
    @setAttribute(Qt::WA_Hover);@

    and reimplement event to handle the HoverEnter, HoverMove and HoverLeave ?



  • YOU CAN IGNORE THIS, IT WAS A SILLY ERROR IN MY SIDE

    I am still working in the solution proposed above. So far, mouseMoveEvent() seems to be working but there's one problem I cannot understand. And it's that the hovered() signals doesn't seem to ever get emitted.

    In the principal class of my program I am connecting that signal to a method that will handle all the zoom on hover stuff, but that method never gets called, or so it seems.

    The signal is now emitted this way:

    @void myLabel::mouseMoveEvent(QMouseEvent *event)
    {
    //Q_UNUSED(event);
    qDebug() << Q_FUNC_INFO << QString("This thing is being hovered");
    double xpos = 100./width()*event->posF().x();
    double ypos = 100./height()*event->posF().y();
    //qDebug() << Q_FUNC_INFO << QString("position within this: %1 x %2").arg(xpos).arg(ypos);
    emit hovered(xpos, ypos);
    }@

    So that it will hold the coordinates (as percentages), which will be useful when calculating which part of the image is to be zoomed.

    Then, from the constructor of the main class I connect it this way:

    @ connect(ui->gv_foto_medio, SIGNAL(hovered(double,double)), this, SLOT(raton_sobre_foto_mediana(double,double)));@

    And the slot is:

    @void kooker::raton_sobre_foto_mediana(double xpos, double ypos)
    {
    qDebug() << Q_FUNC_INFO << QString("position within this: %1 x %2").arg(xpos).arg(ypos);
    }@

    But this message is never output. Setting a breakpoint at that method indeed shows that it's never called. qtcreator doesn't show any error telling me that the connection is incorrect, so that's not it. The only thing I can assume is that the signal never gets emitted, but why?



  • Please, completely disregard the above. I was connecting the wrong widget (I really need to do some cleaning). This made me lose some precious hours ;)

    SGaist, is there any advantage on the method you are describing over the current approach I am taking?

    I am looking for the simplest way to do this, and I am a bit surprised that there's no simple and straight way to detect hovering (at least on this concrete widget) in Qt (still using 4.8 here ;) ).

    Thank you everyone for all the help :)


  • Lifetime Qt Champion

    Using this method, you really get the hover related events without the need to manipulate the mouse tracking and simulate the hovering part.



  • [quote author="SGaist" date="1381009111"]Using this method, you really get the hover related events without the need to manipulate the mouse tracking and simulate the hovering part.[/quote]

    I hadn't found about this before. I'll definitely put that in my to-do list, but right now I have some other priorities regarding this project. Thank you for the information :)



  • Sgaist, I got the time to look into this. It really helped in making this much more readable, quite a few lines of code that I removed. I could also take rid of a few reimplemented members.

    However, there's a small glitch, and a somewhat strange one. I have this, as per your advice:

    @
    bool myLabel::event(QEvent *e)
    {
    QMouseEvent me = static_cast<QMouseEvent>(e);

    if(e->type() == QEvent::HoverMove)
    {
        double xpos = me->pos().x();
        double ypos = me->pos().y();
        emit hovered(xpos, ypos);
        qDebug() << Q_FUNC_INFO << QString("xpos %1, ypos %2").arg(xpos).arg(ypos);
        return true;
    }
    else if(e->type() == QEvent::HoverLeave)
    {
        emit unhovered();
        return true;
    }
    else if(e->type() == QEvent::MouseButtonPress)
    {
        emit clicked();
        return true;
    }
    else if(e->type() == QEvent::MouseButtonDblClick)
    {
        emit doubleClicked();
        return true;
    }
    
    return QLabel::event(e);
    

    }
    @

    I do this in the constructor for this class:

    @ setAttribute(Qt::WA_Hover);@

    But now I am getting this in qDebug()'s output when I hover from down to up:

    @
    virtual bool myLabel::event(QEvent*) "xpos 40, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 39, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 38, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 37, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 36, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 34, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 33, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 31, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 30, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 29, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 28, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 27, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 26, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 25, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 24, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 23, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 22, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 21, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 20, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 18, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 17, ypos 39"
    virtual bool myLabel::event(QEvent*) "xpos 15, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 14, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 12, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 11, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 10, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 9, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 8, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 7, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 6, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 4, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 2, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 1, ypos 40"
    virtual bool myLabel::event(QEvent*) "xpos 0, ypos 40"
    @

    This worked before, and I have checked that's not something silly, plus, the numbers (according to the dimensions of the widget) seem to indicate that pos.x() and pos.y() values for the casted QMouseEvent are swapped.

    In fact, if I emit unhovered(posy, posx), everything's back to normal. But......

    Can you think of any reason why x() and y() values could be swapped like that?

    In any case, thanks for the suggestion. It helped to simplify this quite a bit ;)


  • Lifetime Qt Champion

    Maybe because you cast to a QMouseEvent rather than a QHoverEvent ?



  • That was it, yes. It would have been hard to figure that myself. Thank you again :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.