Trying to create custom FPS counter

Hello im having some problems creating a somewhat simple fps counter that would change colors when its running on x amount of frames (144 <= green, 60 <= yellow, 30<= red) and it should print it to the OnscreenText. my code:

import direct.directbase.DirectStart
import time
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject



class FPScounter(DirectObject):
    def __init__(self,):
        print("Fps Counter starting....")
        base.setBackgroundColor(1, 1, 1)
        #while True:

        self.fpscounter1 = OnscreenText(text=" framerate: "+ str(fps) + " fps", pos=(-0.81, 0.90), scale=0.07, fg = (63,191,63))
       # setFont = (fpscounter1, "AlbertusMediumRegular.ttf")

        self.fps = 1.0 / (time.time() - start_time)
    font = "AlbertusMediumRegular.ttf"
   #print("FPS: ", 1.0 / (time.time() - start_time))
    start_time = time.time()  # start time of the loop
fpscounter = FPScounter()
run()

Also this is my first time posting here so any help to actually make this work would be great!

Well, one way to do this might be to start a task that will run every frame (see the manual here), and in that task use the “global clock” (see the manual here) to get the frame-rate.

(The latter manual page doesn’t show the method used to get the frame-rate, but it does link to the API page, which does show that method, I believe.)

Thanks for that, im really noob here so i tried that and

import direct.directbase.DirectStart
import time
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task import Task


class FPScounter(DirectObject):
    def __init__(self,):
        print("Fps Counter starting....")
        base.setBackgroundColor(1, 1, 1)
        #while True:


       # setFont = (fpscounter1, "AlbertusMediumRegular.ttf")
    font = "AlbertusMediumRegular.ttf"
        #self.fps = 1.0 / (time.time() - start_time)

    def fpscounterTask(fps):
        frameTime = globalClock.getFrameTime()
        return fps.cont
        print('fpscounterTask is Done')
        return fps.done

        self.fpscounter1 = OnscreenText(text=" framerate: " + str(FrameTime) + " fps", pos=(-0.81, 0.90), scale=0.07,fg=(63, 191, 63))

    def exampleTask(task):
        if task.time < 2.0:
            return task.cont
        print('Done')
        return task.done
   #print("FPS: ", 1.0 / (time.time() - start_time))
    start_time = time.time()  # start time of the loop
fpscounter = FPScounter()
run()

i tried the example code aswell and it would not print to console…

Looking at your code, you don’t seem to be adding the task to the task-manager. (On the manual page for “tasks” that I linked to above, check the section on the “task manager” for more information.)

so like

taskMgr.add(fpscounterTask, 'fps')

i put that above the fpscounterTask function and i get error: NameError: name ‘fpscounterTask’ is not defined

The “fpscounterTask” method is defined in your “FPScounter” class. Thus, it doesn’t stand alone, but would be related to an “FPScounter” object. In this case, that would presumably be the one that you’re working in, and thus you would likely reference it via the “self” parameter.

That is, you would reference it not as “fpscounterTask”, but as “self.fpscounterTask”.

By the way, I have a “beginner’s tutorial” that takes one through the basics of using Panda3D, all the way through to building a distributable version. If you’re interested in trying it, you should find it here:

okay i will look at that, though can you just quickly tell me what i need to change here to actually run this?

import direct.directbase.DirectStart
import time
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task import Task


class FPScounter(DirectObject):
    def __init__(self,):
        print("Fps Counter starting....")
        base.setBackgroundColor(1, 1, 1)
        #while True:

        taskMgr.add(self.fpscounterTask, 'fps')
        self.fpscounter1 = OnscreenText(text=" framerate: " + str(self.FrameTime) + " fps", pos=(-0.81, 0.90), scale=0.07,fg=(63, 191, 63))
       # setFont = (fpscounter1, "AlbertusMediumRegular.ttf")
    font = "AlbertusMediumRegular.ttf"
        #self.fps = 1.0 / (time.time() - start_time)

    def fpscounterTask(fps,self):
        frameTime = globalClock.getFrameTime()
        return fps.cont
        print('fpscounterTask is Done')
        return fps.done



    def exampleTask(task):
        if task.time < 2.0:
            return task.cont
        print('Done')
        return task.done
   #print("FPS: ", 1.0 / (time.time() - start_time))
    start_time = time.time()  # start time of the loop
fpscounter = FPScounter()
run()

A few issues that I see:

  • In your “__init__” method, you’re passing “self.FrameTime” to your OnscreenText object–but you don’t have a “FrameTime” variable in your “FPScounter” class.
    • If you just want the program to run, and return to making it do what you want, you can just remove this part from the string that you’re building there.
  • In the same place, you’re specifying a foreground colour (the “fg” keyword-parameter) with only three values (presumably red, green, and blue). However, it should have four values (red, green, blue, and alpha).
    • By the way, I think that those colours should be in the range 0 to 1, not 0 to 255.
  • When you define your “fpscounterTask” method, you’re specifying the “fps” parameter first, which means that it contains the reference to the “FPScounter” object. (Which would usually be in the “self” parameter.) You’re then trying to return “fps.cont”, and the “FPScounter” class doesn’t contain a “cont” variable.
    • Instead, I think that the second parameter in your method (which you’re currently calling “self”) will contain the “task” object, which has the “cont”-value that you’re trying to return.

Fix all of that, and it seems to run! :slight_smile:

Well it was suppose to get the Frametime from the fpscounterTask function and then print it to the self.fpscounter1 onscreentext object but i have no idea how i would get it from the fpscounterTask function…

Perhaps it might be better to start your OnscreenText object with some stand-in text, and then set its text appropriately in the task.

However, you might be able to get some data from the task-object that’s returned by “taskMgr.add”, if you want the OnscreenText to start off with something relevant.

Okay so now i checked the Onscreentext_function
and i noticed the setFg() function that i could use to change the color of the onscreentext object, but when i tried to run the code like this:

import direct.directbase.DirectStart
import time
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task import Task

red = (1,0.0,1)
yellow = (1,0.9,0,1)
green = (0,0.9,0,1)

class FPScounter(DirectObject):
    def __init__(self,):
        print("Fps Counter starting....")
        base.setBackgroundColor(1, 1, 1)
        self.fpsTextobject = OnscreenText(text=" framerate: " + " fps", pos=(-0.81, 0.90), scale=0.07,bg = (0.3,0.3,0.4,1))
        #self.updateCounter = taskMgr.add(self.fpscounterTask, 'fps')

        self.fpsTextobject1 = setFg(green)
       # setFont = (fpscounter1, "AlbertusMediumRegular.ttf")
    #font = "AlbertusMediumRegular.ttf"
        #self.fps = 1.0 / (time.time() - start_time)
   # def fpscounterTask(self):
        #self.frameTime = globalClock.getFrameTime()
       # print('fpscounterTask is Done')
       # return fps.done
   #print("FPS: ", 1.0 / (time.time() - start_time))
    start_time = time.time()  # start time of the loop
fpscounter = FPScounter()
run()

it just says NameError: name ‘setFg’ is not defined and i f i try add

self.setFg(green)

instead i just get AttributeError: ‘FPScounter’ object has no attribute ‘setFg’ and yes the onscreentext has been imported to the py file.

When you call just “setFG” alone, you’re not specifying an object or class that it should called from; you’re treating it as though it were a function defined outside of any class. When you call “self.setFG”, you’re treating it as though it were a method of your FPScounter class–or more specifically, of the instance of that class that you’re constructing at that point (since this happens in an “__init__” method).

Do I gather correctly that you’re not terribly familiar with object-oriented programming? If I am correct, then I recommend that you look up some tutorials for object-oriented programming in Python, as you may otherwise keep stumbling over such issues.

Yeah im not too familiar with python but im more familiar with C# and im trying to get used to the python.

though is it possible to create the code with c++ and then convert that code to python and then get it to run with the panda3D? not too sure if panda3D has any tools to convert one language to another?

I don’t think that there are such conversion tools. However! They may not be called for: if you’re familiar with object-oriented programming in C++, and have a C++ compiler available, you can just develop in C++. You don’t have to use Python, I believe.

import direct.directbase.DirectStart
import time
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task import Task

red = (1,0,0,1)
yellow = (1,0.9,0,1)
green = (0,0.9,0,1)


class FPScounter(DirectObject):
    def __init__(self,):
        self.frameTime = 0
        print("Fps Counter starting....")
        base.setBackgroundColor(1, 1, 1)
        self.fpsTextobject = OnscreenText(text=" framerate: " + str(round(self.frameTime))+ " fps", pos=(0.80, 0.90), scale=0.07,bg = (0.3,0.3,0.4,1)) # placeholder
        taskMgr.add(self.fpscounterTask, 'fps1')

        self.fpsTextobject.setFg(green)
        self.maxfps = 144

    def fpscounterTask(self,task):
        self.frameTime = globalClock.getAverageFrameRate()
        #self.frameTime = 30
        color = green
        if self.frameTime <= 144:
            color = green
        if self.frameTime <= 60:
            color = yellow
        if self.frameTime <= 30:
            color = red

        self.fpsTextobject.destroy()
        self.fpsTextobject = OnscreenText(text=" framerate: " + str(round(self.frameTime)) + " fps", pos=(1.03, 0.90),scale=0.07, bg=(0.3, 0.3, 0.4, 1), fg=(color))
        #self.fpsTextobject.setFg(color)

        return task.cont
fpscounter = FPScounter()
run()

solved the issue. Thanks for the help. also how do i set text alignment?

1 Like

Congratulations on getting it working! And I’m glad if I’ve been of service. :slight_smile:

As to text-alignment, see the manual here for that information!