Search code examples
pythonlabelpyqt5frame-rate

Python desktop FPS displaying in label


I can't manage to display current fps on the label. The problem is i want it to loop and keep changing forever, not to be set once. My idea was to loop it but untill sys.exit() the controlls like my label_fps does not apear.

import sys
import time
from PyQt5 import QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QDialog
from main_view import Ui_MainWindow


class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(ApplicationWindow, self).__init__()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)


def main():
    app = QtWidgets.QApplication(sys.argv)
    application = ApplicationWindow()
    application.show()

    def fps_display():
        start_time = time.time()
        counter = 1
        # All the logic()
        time.sleep(0.1)
        time_now = time.time()
        fps = str((counter / (time_now - start_time)))
        application.ui.label_fps.setText(fps)
    #while(True): does not work
    fps_display()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Solution

  • As you have pointed out, the Qt app isn't displayed until sys.exit() is called and you therefore can't stay in a loop before this.

    I think what you therefore need to do is use a QTimer. This will emit a signal every n milliseconds. This signal can then be connected to your fps_display() function so that it is called every time the signal is emitted. For example, you can create a QTimer that will emit a signal every 0.1 seconds using:

    timer = QTimer()
    timer.setInterval(100)  # 100 milliseconds = 0.1 seconds
    timer.start()  # Set the timer running
    

    The signal that will be emitted when the timer has run out is timeout(). It is therefore this signal that we want to connect to the function that handles the updating of the FPS label as so:

    timer.timeout.connect(fps_display)
    

    Now, putting this together and moving the location of fps_display() so that it is a method of the ApplicationWindow class, we get:

    import sys
    import time
    from PyQt5 import QtGui, QtWidgets
    from PyQt5.QtWidgets import QApplication, QWidget, QDialog
    from PyQt5.QtCore import QTimer, pyqtSlot  # Import new bits needed
    from main_view import Ui_MainWindow
    
    
    class ApplicationWindow(QtWidgets.QMainWindow):
        def __init__(self):
            super(ApplicationWindow, self).__init__()
    
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
    
            # Add in creating and connecting the timer 
            self.timer = QTimer()
            self.timer.setInterval(100)  # 100 milliseconds = 0.1 seconds
            self.timer.timeout.connect(self.fps_display)  # Connect timeout signal to function
            self.timer.start()  # Set the timer running
    
        @pyqtSlot()  # Decorator to tell PyQt this method is a slot that accepts no arguments
        def fps_display(self):
            start_time = time.time()
            counter = 1
            # All the logic()
            time.sleep(0.1)
            time_now = time.time()
            fps = str((counter / (time_now - start_time)))
            self.ui.label_fps.setText(fps)
    
    
    def main():
        app = QtWidgets.QApplication(sys.argv)
        application = ApplicationWindow()
        application.show()
    
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

    I have moved fps_display() for convenience, but it by no means needs to be a method of ApplicationWindow to be called by the timer's signal.

    That should now hopefully be able to do what you are after. I have had to make a slightly different script to yours as I don't have the UI file you are using so hopefully I have converted back over okay and it all works!