2

I followed the Panda3D tutorial and got everything working so tried adding my own custom arguments using argparse. The first one I made was --no-rotate where it would stop the camera rotating which worked, but when I tried to add a --scale where you can edit the scale of the panda actor it broke. It says the scale variable is a NoneType when I set it as an integer in the function parameters.

from math import pi, sin, cos

from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor

class WalkingPanda(ShowBase):
    def __init__(self, no_rotate=False, scale=1):
        ShowBase.__init__(self)

        # Load the environment model.
        self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        self.scene.setScale(0.25, 0.25, 0.25)
        self.scene.setPos(-8, 42, 0)

        if not no_rotate:
          # Add the spinCameraTask procedure to the task manager.
          self.taskMgr.add(self.spinCameraTask, "SpinCameraTask")

        # Load and transform the panda actor.
        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.005*scale, 0.005*scale, 0.005*scale)
        self.pandaActor.reparentTo(self.render)
        # Loop its animation.
        self.pandaActor.loop("walk")

    # Define a procedure to move the camera.
    def spinCameraTask(self, task):
        angleDegrees = task.time * 6.0
        angleRadians = angleDegrees * (pi / 180.0)
        self.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3)
        self.camera.setHpr(angleDegrees, 0, 0)
        return Task.cont

from . import panda

import argparse

def cli():
    parser = argparse.ArgumentParser(prog="walking_panda")
    parser.add_argument("--no-rotate",help="Suppress Rotation",
                        action="store_true")
    parser.add_argument("--scale", type=int)
    args = parser.parse_args()

    walking = panda.WalkingPanda(**vars(args))
    walking.run()

from walking_panda.cli import cli

if __name__ == '__main__':
    cli()

I don't know why it doesn't see the scale parameter as 1 when it sees the no_rotate parameter as False by default. Any help would be appreciated.

When I run the command:

python walking_panda.py --scale 5

the program functions properly and changes the scale of the panda to 5 times the default. It's just that when I don't add the --scale argument it doesn't see the default value and it returns an error.

4
  • 4
    The code that handles the param is missing Commented Oct 4 at 15:01
  • 1
    always put full error message (traceback) because there are other useful information. Commented Oct 4 at 15:13
  • maybe create minimal working code which we could simply copy and test. Commented Oct 4 at 15:15
  • I tried to put all code in one file - to create minimal working code - and it doesn't raise your error. I put print(f"{scale = }") in __init__ and it displays 5. Better create minimal working code which we could copy to one file and test it. And check if it still gives error. Besides you didn't show FULL traceback so it hard to say which part of code makes problem. Maybe it is problem in different place. Commented Oct 4 at 15:43

1 Answer 1

2

The issue is that the default value from argparse is being used, instead of the default value defined on the __init__ method. If no default is specified for parser.add_argument, None is the default as shown in this example:

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument("--scale", type=int) # No default= parameter
_StoreAction(option_strings=['--scale'], dest='scale', nargs=None, const=None, default=None, type=<class 'int'>, choices=None, required=False, help=None, metavar=None)
>>> args = parser.parse_args()
>>> args
Namespace(scale=None)
>>> print(args.scale)
None
>>> vars(args)
{'scale': None}

However, if you set default=1 in your argparse argument, then it uses the desired default if the flag is not specified on the command line.

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument("--scale", type=int, default=1) # default set
_StoreAction(option_strings=['--scale'], dest='scale', nargs=None, const=None, default=1, type=<class 'int'>, choices=None, required=False, help=None, metavar=None)
>>> args = parser.parse_args()
>>> args
Namespace(scale=1)
>>> print(args.scale)
1
>>> vars(args)
{'scale': 1}

If your class is not going to be used other than with the command line arguments from argparse, I would recommend removing the defaults from the __init__ method so that you don't have two different places where defaults are defined.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.