Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Buttons appear when hover over media player



  • Hi,

    I am using QMediaPlayer and QVideoWidget to create a media player to show a couple of videos in my application. I followed the following tutorial to create play, pause and stop buttons and it is working.

    [https://doc.qt.io/qt-5/qtmultimedia-multimediawidgets-player-example.html](link url)

    However, I want my play, pause and stop buttons to be shown on media player when user hover over media player and hidden when mouse is not on the media player similar to Youtube:

    youtube.png

    When I use the enterevent and the leaveevent of the QVideoWidget, if I get cursor over the button created by the enterevent of the video widget, it starts to call leaveevent and enterevent again and again, the buttons are flicking.

    How can I do this properly?
    l really appreciate any help you can provide.



  • @mehmety888
    Hi,

    You can do it with event filter like below. You should catch the mouse position in the frame or in the view widget. Also you should make the m_p_button_frame as a Tool window like you did in your constructor. You do not need to have enterEvent and leaveEvent. You should delete them.

    bool VideoWidget::eventFilter(QObject *in_p_watched, QEvent *in_p_event)
    {
    
        if(this == in_p_watched || m_p_button_frame == in_p_watched)
        {
            QMouseEvent *p_mouse_event = dynamic_cast<QMouseEvent *>(in_p_event);
            if (Q_NULLPTR != p_mouse_event)
            {
                QPoint mouse_position = p_mouse_event->pos();
                QRect rect = this->rect();
                rect.setX(rect.x()+10);
                rect.setY(rect.y()+10);
                rect.setWidth(rect.width()-20);
                rect.setHeight(rect.height()-20);
                if (false == rect.contains(mouse_position))
                {
                    m_p_button_frame->hide();
                }
                else if (true == this->rect().contains(mouse_position))
                {
                    QPoint global_point = QWidget::mapToGlobal(this->pos());
                    m_p_button_frame->setGeometry(global_point.x()+10,global_point.y(),200,50);
                    m_p_button_frame->show();
                }
            }
        }
        return QObject::eventFilter(in_p_watched, in_p_event);
    
    }
    


  • Can you post your code?
    I think if the buttons are the child widgets of QVideoWidget then it should work.



  • My code is like below:

    VideoWidget.h

    #include <QVideoWidget>
    class QPushButton;
    class QFrame;
    class VideoView;
    
    class VideoWidget: public QVideoWidget
    {
        Q_OBJECT
        Q_DISABLE_COPY(VideoWidget)
    
    public:
        explicit VideoWidget(QWidget *in_p_parent = Q_NULLPTR);
    protected:
        void enterEvent(QEvent *in_p_event) Q_DECL_OVERRIDE;
        void leaveEvent(QEvent *in_p_event) Q_DECL_OVERRIDE;
    private:
        QFrame *m_p_button_frame = Q_NULLPTR;
        QPushButton *m_p_add_play_button = Q_NULLPTR;
        QPushButton *m_p_add_stop_button = Q_NULLPTR;
    };
    

    VideoWidget.cpp:

    #include "VideoWidget.h"
    #include <QPushButton>
    #include <QFrame>
    #include <QHBoxLayout>
    #include "VideoView.h"
    #include <QMouseEvent>
    #include <QEvent>
    #include <QApplication>
    
    VideoWidget::VideoWidget(QWidget *in_p_parent) : QVideoWidget(in_p_parent)
    {
        qApp->installEventFilter(this);
        setMouseTracking(true);
        setAttribute(Qt::WA_Hover, true);
        m_p_button_frame = new QFrame(this);
        QHBoxLayout *p_layout = new QHBoxLayout;
        m_p_button_frame->setLayout(p_layout);
        m_p_add_play_button = new QPushButton(m_p_button_frame);
        m_p_add_play_button->setText("Play");
        m_p_add_stop_button = new QPushButton(m_p_button_frame);
        m_p_add_stop_button->setText("Stop");
        p_layout->addWidget(m_p_add_play_button);
        p_layout->addWidget(m_p_add_stop_button);
        VideoView *p_parent = dynamic_cast<VideoView *>(in_p_parent);
        connect(m_p_add_play_button, &QPushButton::clicked, p_parent, &VideoView::slotPlayVideo);
        connect(m_p_add_stop_button, &QPushButton::clicked, p_parent, &VideoView::slotStopVideo);
    
        m_p_button_frame->setAttribute(Qt::WA_ShowWithoutActivating, true);
        m_p_button_frame->setAttribute( Qt::WA_TranslucentBackground );
        Qt::WindowFlags flags = Qt::Tool;
        flags |= Qt::FramelessWindowHint;
        flags |= Qt::WindowStaysOnTopHint;
        m_p_button_frame->setWindowFlags(flags);
    }
    void VideoWidget::enterEvent(QEvent *in_p_event)
    {
        QVideoWidget::enterEvent(in_p_event);
        QPoint global_point = QWidget::mapToGlobal(this->pos());
        m_p_button_frame->setGeometry(global_point.x(),global_point.y(),100,30);
        m_p_button_frame->show();
    
    }
    void VideoWidget::leaveEvent(QEvent *in_p_event)
    {
        QVideoWidget::leaveEvent(in_p_event);
        m_p_button_frame->hide();
    }
    


  • @mehmety888
    Why do you set the button frame to be a tool window? That makes it a window, not a child widget, so this mouse thing won't work.



  • @Bonnie

    I removed Tool flag, but pushbuttons are not visible now.


  • Lifetime Qt Champion

    Hi,

    Why are you installing an event filter ?

    Are you sure the hover related methods are called ?
    What if you do not set your custom class as parent of your frame ?



  • @mehmety888 said in Buttons appear when hover over media player:

    @Bonnie

    I removed Tool flag, but pushbuttons are not visible now.

    I think this is simply because when it is not a window any more, you shouldn't set its geometry by global coordinate, but its parent's coordinate, so mapToGlobal is not needed.

    And there're lots of code from your post I don't think is necessary:
    these

        qApp->installEventFilter(this);
        setMouseTracking(true);
        setAttribute(Qt::WA_Hover, true);
    

    and these all

        m_p_button_frame->setAttribute(Qt::WA_ShowWithoutActivating, true);
        m_p_button_frame->setAttribute( Qt::WA_TranslucentBackground );
        Qt::WindowFlags flags = Qt::Tool;
        flags |= Qt::FramelessWindowHint;
        flags |= Qt::WindowStaysOnTopHint;
        m_p_button_frame->setWindowFlags(flags);
    


  • @SGaist Hi,
    I was also trying to do it using event filter, I forgot to remove it.
    I also tried using hover, it is not necessary now, I will remove it.

    If I don't set my custom class as parent, and I get cursor over the button created by the enterevent of the video widget, it starts to call leaveevent and enterevent again and again, the buttons are flicking.



  • @Bonnie Thank you for your feedback.

    When I do the changes you mentioned, enterevent and leaveevent works fine, but video gets in front of pushbuttons and frame unless I click on frame position. Then it plays / stops video (buttons and frame are not visible)



  • @mehmety888
    Hi,

    You can do it with event filter like below. You should catch the mouse position in the frame or in the view widget. Also you should make the m_p_button_frame as a Tool window like you did in your constructor. You do not need to have enterEvent and leaveEvent. You should delete them.

    bool VideoWidget::eventFilter(QObject *in_p_watched, QEvent *in_p_event)
    {
    
        if(this == in_p_watched || m_p_button_frame == in_p_watched)
        {
            QMouseEvent *p_mouse_event = dynamic_cast<QMouseEvent *>(in_p_event);
            if (Q_NULLPTR != p_mouse_event)
            {
                QPoint mouse_position = p_mouse_event->pos();
                QRect rect = this->rect();
                rect.setX(rect.x()+10);
                rect.setY(rect.y()+10);
                rect.setWidth(rect.width()-20);
                rect.setHeight(rect.height()-20);
                if (false == rect.contains(mouse_position))
                {
                    m_p_button_frame->hide();
                }
                else if (true == this->rect().contains(mouse_position))
                {
                    QPoint global_point = QWidget::mapToGlobal(this->pos());
                    m_p_button_frame->setGeometry(global_point.x()+10,global_point.y(),200,50);
                    m_p_button_frame->show();
                }
            }
        }
        return QObject::eventFilter(in_p_watched, in_p_event);
    
    }
    


  • @DzCode
    Thanks, that solve the problem. However, when I move mouse fast, frame are not hidden sometimes.


Log in to reply