Search code examples
python-3.xeventspyqt5tooltipqpushbutton

PyQt5: count seconds mouse is over QPushButton in tooltip


The following running code gives a window with a button on it . The tooltip displays when the mouse enters the button.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtTest import QTest

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.layout = QVBoxLayout()
        self.button = QPushButton("MyButton")
        
        global i
        i = 0

        self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")
        self.button.leaveEvent = self.clear()
        self.layout.addWidget(self.button)
        
        timer = QTimer(self)
        timer.timeout.connect(self.start_counting)
        timer.start(1000)
        
        self.widget = QWidget()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)
    
    def clear(self):
        global i
        i = 0
        
    def start_counting(self):
        if self.button.underMouse() == True:
            global i
            i = i + 1
            self.button.setToolTip(str(i) + " seconds has passed since you move your mouse onto MyButton")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit( app.exec_() )

My goal is to count the number of seconds the mouse is inside the button and live display it using the tooltip. More precisely, I need to make sure all of the following is happening:

  1. Every time the mouse enters the button, the count starts at 0 seconds and the tooltip shows up.
  2. While the mouse stays inside the button (stationary or moving), the tooltip stays shown with the number of seconds (the text displayed inside the tooltip) updated over time.
  3. When the mouse leaves the button, the count is cleared to zero.

As seen in the code, I have attempted to use underMouse to achieve my goals. My attempt is a partial success as the tooltip does update itself when the mouse moves inside the button. However, the tooltip does not update itself when the mouse stays stationary inside the button. Also, the count does not seem to be cleared when the mouse moves outside of the button .

What am I missing ?


Solution

  • One solution is to use an event-filter to monitor the enter and leave events, and also use an elapsed-timer to get an accurate measure of how long the mouse has been over the target widget. Below is a basic demo based on your example that implements the above. It also tries to match the normal behaviour of tooltips, but if necessary you can easily adjust the code to suit your own needs:

    import sys
    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    
    class MainWindow(QMainWindow):
        def __init__(self):
            super().__init__()
            self.layout = QVBoxLayout()
            self.button = QPushButton("MyButton")
            self.layout.addWidget(self.button)
            self.widget = QWidget()
            self.widget.setLayout(self.layout)
            self.setCentralWidget(self.widget)
            self.position = QPoint()
            self.counter = QElapsedTimer()
            self.timer = QTimer()
            self.timer.timeout.connect(self.start_counting)
            self.button.installEventFilter(self)
    
        def eventFilter(self, source, event):
            if event.type() == QEvent.Enter and source is self.button:
                self.counter.start()
                self.timer.start(1000)
                QTimer.singleShot(500, self.start_counting)
            elif event.type() == QEvent.Leave and source is self.button:
                self.timer.stop()
                QToolTip.hideText()
            return super().eventFilter(source, event)
    
        def start_counting(self):
            if self.button.underMouse():
                if not QToolTip.isVisible():
                    self.position = QCursor.pos()
                count = int(self.counter.elapsed() / 1000)
                QToolTip.showText(self.position, (
                    f'{count} seconds have passed since '
                    'you moved your mouse onto MyButton'
                    ))
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit( app.exec_() )