what about making panda more pythonic?

What should i make treeform work on?

  • do all 5
  • just do 1 - properties
  • just do 2 - tupples as vec
  • do just 2 and 1 keep out of vec
  • you missed more pythonic features that could be added

0 voters

what about making panda more pythonic?

its hard to say what is pythonic but this is what i think:
1:
wrapping getters and setters in properties:
node.setPos(node.getPos()+Vec3(1,1,1))
node.pos = node.pos + Vec3(1,1,1)
or simply
node.pos += Vec3(1,1,1)
this can be done for all set*, get* and is* functions

2: make fuctions that take Vec* Point* take tuples too
so in the previews code:
node.setPos(node.getPos()+(1,1,1))
node.pos = node.pos + (1,1,1)
node.pos += (1,1,1)

3: make Vec* and Point* illiterate so that it is more like a tuple
x,y,z = Vec3(1,2,3)
x,y,z = node.pos

4: why is there Vec* and Point* why not just one Vec or Point?

5: add len() to Vec*, Point* and to any thing else where it would fit
so that len(node.pos) would do node.getPos().length(). NOTE: len function uses .len()

This is it for now i think it would make panda a bit more pythonic. What do you guys think? Why it has not been done before? And should i start writing a script to parse and auto generate properties for us and hack the vectors?

-treeform

Sure, but only if it is not at the cost of speed or functionality. I think there have been good reasons for making Panda3D the way it is. I don’t want to be the kill-joy, but here are my two cent:

1:
Some of these getters/setters are more complex than they look at the first glance. Unfortunately those features are not very well documented in the API reference, since they are overloaded C++ functions and the API reference seems to be auto-generated from the C++ code.

First instead of writing

np.setPos( np.getPos() + Vec3(1,1,1) )

you could write this. The second way is faster!

np.setPos( np, Vec3(1,1,1) )

But what about this:

npA.setPos( npB, Vec3(1,1,1) )

I don’t see a way how to do this with a simple setter. It is a feature I use very often, and I would miss this very handy feature.

I would say speed if the reason. At some point, down in the C++ layer, you have to set the fields or a struct or the members of a class instance. So sooner or later you have to parse the unknown-length Python tuple, extract the floating point numbers (as Python objects first, then create C++ numbers), and assign them. This is slower than just passing the reference to an already created vector instance, where you have almost immediatly direct access to the coordinates as C++ numbers.

Having something like

v = Vec3( 1, 1, 1 )
x, y, z = v

could be useful, indeed, for example when working with non-Panda3D libraries. The C++ vector classes would have to implement part of the Python sequence protocol. This seems to work

x = v[0]

but searching through the Panda3D source code I didn’t find any getitem implementation, so might be Panda3D does some voodoo here.

On the C++ layer of Panda3D there are vectors using double precision floating point numbers (LVector3d), and vectors using single precision floating point numbers (LVector3f). Using double precision is not always necessary, and it is slower. Panda3D allows you to choose what kind of vectors you want to use. In some cases it doesn’t make sense to have double precision (colors???), in some cases it does (coordinates???).

len is part of the Python container protocol, and has a fixed semantic. It is intended to return the number of objects in a container. This is always integer. Abusing len for computing a vector’s length is possible, but it would cause lots of confusion. For example I would expect len(Vec3(…)) to return 3, the number of data elements in this vector.

enn0x

:laughing:
ordered_vector.I :

////////////////////////////////////////////////////////////////////
//     Function: ordered_vector::operator []
//       Access: Public
//  Description: Returns the nth element.
////////////////////////////////////////////////////////////////////
template<class Key, class Compare>
INLINE TYPENAME ordered_vector<Key, Compare>::CONST_REFERENCE ordered_vector<Key, Compare>::
operator [] (TYPENAME ordered_vector<Key, Compare>::SIZE_TYPE n) const {
  return _vector[n];
}

smile Close. The code you found is for using the C++ [] operator on C++ vectors. What I have been looking for is where the Python interface is implemented. I found it in liblinmath_igate.cxx, a file that gets created during the build process.

Here they register a getitem method and hook into the object protocol:

PyMethodDef Dtool_Methods_LVecBase3f[]= {
  ...
  { "__getitem__",(PyCFunction ) &Dtool_LVecBase3f_operator_172, METH_VARARGS| METH_KEYWORDS ,Dtool_LVecBase3f_operator_172_comment},
        // tp_as_mapping->mp_subscript = __getitem__
        Dtool_LVecBase3f.As_PyTypeObject().tp_as_mapping->mp_subscript = &Dtool_LVecBase3f_operator_172__getitem__;

Well, both ways end in the same implementation:

/******************************************************************
 * Python type method wrapper for
 * Rejected Remap [inline float &LVecBase3f::operator [](int i)]
 * inline float LVecBase3f::operator [](int i) const
 *******************************************************************/
static PyObject *Dtool_LVecBase3f_operator_172(PyObject *self, PyObject *args,PyObject *kwds) {
    ....

But back to the original topic, making Panda3D more pythonic.

v = Vec3( 1, 1, 1 )
x = v[0]

This is possible because the Vec3 type has mapping support implemented by filling this slot in the type definition: PyMappingMethods *tp_as_mapping

v = Vec3( 1, 1, 1 )
x, y, z = v

I’m not sure, but to make this possible too I think someone would have to implement this slot too: PySequenceMethods *tp_as_sequence

I’m not 100% certain here. I have done implementations of the number protocol and so on, for example in JSBSim Location, but I never have been tweaking around with these two type slots. And I didn’t spend time on finding out how Panda3D build process auto-generates these files. This would be where “making more pythonic” would have to start.

enn0x

I agree with the first part. Don’t give up any existing functionality. I don’t care as much about the speed. Panda3D doesn’t use Python for the speed, It uses it for it’s rapid prototyping ability (among other reasons.) If you can do anything to make the programmers’ job a little easier, that’s definitely a good thing. If you have to sacrifice a little bit of speed, I can live with it. Python is slow enough compared to C++ that optimizations might be necessary anyway. Better to save time in the prototyping phase.

Some reasons may be better than others, but that doesn’t mean we have to stick to the status quo. Pana3D is open source so it can be improved, not so it can stagnate. Adding functionality might be better than replacing it as long as it’s documented. Panda’s API is hard enough to sort through as it is.

This works for me for converting tuple to vector:

tuple=(1,2,3)
myVector=Vec3(*tuple)
#myVector reads now Vec3(1,2,3)

I say, no. It’s important to be able to easily translate a piece of python panda code into a piece of C++ panda code. It already bothers me that there are pieces of the system that only work in python (ie, actors, tasks) and that you have to learn a new way of doing things if you want to switch to C++. I don’t want any more such pieces.

i know i am a rebel here is my script for turning all getter/setter into properties.

import pandac.PandaModules as panda

def fix(clsName,cls):
    try: cls.__dict__
    except: return 
    #print clsName
    getters = {}
    setters = {}
    for name,fun in cls.__dict__.items():
        if "__" not in name and len(name) > 3:
            if "set" == name[0:3]:
                name = name[3].lower() + name[4:]
                #print "\tset:",name
                setters[name] = fun
            elif "get" == name[0:3]:
                name = name[3].lower() + name[4:]
                #print "\tget:",name
                getters[name] = fun
    for name in getters.keys():
        if name in setters:
            cls.DtoolClassDict[name] = property(getters[name], setters[name])
            del setters[name]
        else:
            cls.DtoolClassDict[name] = property(getters[name])
    for name in setters.keys():
        print name
        cls.DtoolClassDict[name] = property(fset=setters[name])
    globals()[clsName] = cls
      
for clsName,cls in panda.__dict__.items():
    fix(clsName,cls)



# ================ test ================

v = Vec3()
v.x = 3
v.y = v.x + 3
v.z += 1

print v

n = NodePath("node path")
n.pos = v
n.pos += v
n.hpr = Vec3(30,40,50)

print n.pos, n.hpr , n

oh while we are on the topic and

 
np.setPos( np.getPos() + Vec3(1,1,1) ) != np.setPos( np, Vec3(1,1,1) )

n1 = NodePath("1")
n1.setHpr(123,123,123)
n1.setPos(n1,Vec3(.1,.1,.1))

n2 = NodePath("1")
n2.setHpr(123,123,123)
n2.setPos(n2.getPos()+Vec3(.1,.1,.1))

print n1.pos,n2.pos
>>> Point3(-0.0676344, -0.00886309, 0.159208) Point3(0.1, 0.1, 0.1)

i guess we can argue some more ! Keep in mind that
1 i don’t propose we remove getters and setter that would break lots of code
2 i know this method might be slower … but then why not code in c++ all the way?
3 there is still lots of other pythonization to be done

Ok, maybe I’m a little late in this thread, but I’ve just started using Panda3D for real in a Computer Graphics Project class in my university. I’ve choose Panda3D as the engine for the project because of python and it’s ease of use.

I’ve choose Panda3D over Soya (the only other python 3D engine I’ve used) because Panda3D seems more robust and there are real projects made on it. But while using it I’m starting to feel that Panda3D developers don’t used many Python libraries around and there are a lot of things that feel uncomfortable to use.

Well, I’ve poked around DirectStart module code only to find that it only instantiates a ShowBase class to start everything. So, I thought I could just instantiate a ShowBase class on my own to better control the organization of my code. But it was useless, since the init() of show base puts a lot of stuff on the builtin namespace. I don’t feel good with it since it puts a lot of stuff I don’t know about on the global namespace. I think it’s bad that a module import does that.

My suggestion would be: put the _builtin.foo = self.foo bit of ShowBase at the DirectStart module code, so people who like this method of starting the engine would use it, and people who like to control initialization would instantiate a ShowBase class by themselves and use it’s properties (like render and taskMgr) under the name choosen for the instance and in the namespace it was instantiated.

Something that would be nice too is to have the capability to init (or not) the various subsystems of panda (render, render2d, audio, gui, tasks) and choosing when to use each one. Well, to say shortly: it would be nice to decouple all the stuff that makes Panda3D. I’m pretty sure that there is some way to do this, but it isn’t either easy or documented.

Well, just my 2 cents

:slight_smile:

True. I just hate Panda3D messing around with my builtins.
I personally don’t use directstart – only showbase. Though there’s no use in instanciating it, since showbase already puts itself and its children in python’s builtins.

Mind if I play advocatus diaboli?

I think there is a good reason why ShowBase acts the way it does. After 5 minutes with Panda3D you are able to see a window and have your first model somewhere in front of your camera. A feeling of success.

I remember the first time I dabbled around with Python and 3D. It has been pyogre (not the more comfortable new ogre-python, and without the application framework being ported to Python). After several hours of setting up resource group managers, choosing scene managers, creating a camera and viewports I eventually managed to open a window. It has been black, but I have been pretty sure I did load a model. Well, I forgot to create a light. No light --> black scene. Panda3D showbase does this all for for. Camera, lights, event handling, and much more. Just start adding your models.

Light being the catchword: Where there is light, there is darkness. Setting up everything for you means that you have to take it as it is? Cast in concrete, once and forever?

Well, not with Panda3D. ShowBase is Python code. You can modify it or replace it. Once you are more experienced (this means, you know what you are doing!). A couple of hours, and you have your own, custom tailored framework. No need to waste memory for drive or trackball mouse interface, and so on.

So Panda3D manages to do the splits: being newbie friendly and a good environment to learn about game development, while offering the potential to have everything customized for more experienced users.

By the way: While I consider myself an experienced Python developer, I still consider myself a 3D or game development apprentice.

EDIT: Oh, and I too hate someone messing around with the my global namespace. Guido (also known as the benevolent Python dictator) would frown upon you, Panda3D developers :slight_smile: But I can see the advantages too.

enn0x

I’ll throw in my two cents too and agree with all of the above. I hate the fact that ShowBase pokes around in the builtins. It’s annoying that it wants to open a window immediately upon instantiation (or even upon simply importing DirectStart).

Some of these decisions were made by the VR Studio when we were just beginning to use Python, and so we didn’t have enough experience to foresee all the implications of actions like these. But now that we’ve got this code, we’re kind of stuck with it–if we continue to use ShowBase.

But, as enn0x points out, there’s no reason for an experienced developer to be burdened with ShowBase. ShowBase does a lot of setup, true, but it’s all done in Python code, and you can fairly easily create your own ShowBase-light class that does just the subset you actually want. And until you’re ready to do that, it is nice to have a class that does everything a beginner might want to have done.

David

I’ve thought about cleaning up those builtins. The difficulty is that lots of the modules in ‘direct’ rely upon the new builtins. For example, you’ll find a lot of code that uses the global variable taskMgr without importing it explicitly. To get rid of the builtins, you’d have to systematically go through the ‘direct’ tree and make sure there are import-statements for each of the globals in question. You’d also have to fix most of the sample programs.

Josh, I’ve noticed that most code you see around imports DirectStart, so a (mostly) safe step in that direction would be removing the builtins from the showbase class and putting them on the DirectStart module code. Something like:

print 'DirectStart: Starting the game.'

from direct.showbase import ShowBase
s = ShowBase.ShowBase()

__builtin__.base = s
__builtin__.render2d = s.render2d
__builtin__.aspect2d = s.aspect2d

# and so on...

Well, not saying it would be a piece of cake, since ShowBase seems to do some other stuff that need global names defined.

enn0x, you are right here and ShowBase don’t look that hard to hack around and it would be fairly easy to do my own version. But it would be nice if ShowBase could do both ways: the easy way and the advanced way. Maybe having an init() method (not init, an explicitly called init method) that does all the beginner friendly setup, and some init* methods (i.e. initLighting, initAudio, and so) that could just as easily init those “subsystems” but still doing that in separate so I could easily choose wich one to start.

Well, but as you wisely pointed out, it’s Python code anyway :wink:
That doesn’t need to be ShowBase, it could be my own class. But anyway, if I would implement something like that I wouldn’t like it to be used only in one project of my own, I would like it to be useful to everyone that have interest in something like that. Also, despite being quite proficient with Python, I’m not totally aware of the internal setup of Panda and I may get it wrong in the beginning.

If anyone is interested in discussing API changes and alternative, generic and more “pythonic” ways of doing things, please drop me a line: kcfelix [at] gmail [dot] com.

And just one more thing: I know it’s painfull to change it in the code tree and I’m definitely not asking you to do it, but it’s very confusing to have one class modules with the exact same name of the class that it contains.

Well, hope I didn’t bother you with this looong post. I’m very happy to receive feedback on this matter.

:slight_smile:

i have no problem with the built ins. It a little unpythonic but it is handy.

Hi i want to revise my previous head post and want to start the discussion moving again because i feel its important.

I think panda3d is a fantastic library but it missing kind of a fine touch that python is known for. I been using panda3d for about a year and i am still missing kind of intiution of just writing some thing that looks like it can work and have it working. I think panda3d is allready great for protytyping any thing but i think there is still room for improvement.

Panda3d needs to be more pythonic.

After this statment i need to explain out what exaclty is more pythonic means, why isn’t panda3d pythonic, how panda3d should change to be more pythonic, and why it is good to be pythanic. I have this gut feeling what is pythonic and what is not. Gut feeling is not good enough for me so i whent to do some searching. The most obvios place to look at the interpreter it self:

Martijn explains what pythonic is in his terms at faassen.n–tree.net/blog/view/we … 05/08/06/0. The main points he is trying to convey is that doing things in a manner that is not native to pythin is bad for instance his example of an interation of a list:

in C:
for (i=0; i < mylist_length; i++) {
   do_something(mylist[i]);
}

and translated direclty in python:

i = 0
while i < mylist_length:
   do_something(mylist[i])
   i += 1

while the pythonic way is

for element in mylist:
   do_something(element)

Which is much cleaner and working with the langauge rether then against it.

From wikipedia about python also:

A common neologism in the Python community is pythonic, which can have a wide range of meanings related to program style. To say that a piece of code is pythonic is to say that it uses Python idioms well; that it is natural or shows fluency in the language. Likewise, to say of an interface or language feature that it is pythonic is to say that it works well with Python idioms; that its use meshes well with the rest of the language.

I dont want to say that panda3d library itself is written bad being unpythonic. Much of panda was written quite a long time agao when python did not have many of the features it has now. And indead at that time panda3d might have as well been most pythonic but as the langauge changes what is considderd pythonic changes too and i feel we need to reflect better coding practices in panda3d. On the other hand its hard to say what changes to panda3d will make it more pythonic and not just much more slower for the sake of some python’s almost relegios quality. These are the changes that i think will make larger improvments to the code base:

1: Big unpytnic blunder is getters and setters. Getters and setters are neede for langauge which has no option to wrap a member later as a get/set functions in a property. Why does panda3d has so many getters and setters? Well panda3d is a wrapper around a C++ librarry that user getters and setters for everything. Python acessors created by the properties are very nice part of python alngauge. Properites are much pythonic because they are more “Beautiful” and they looks less “Complex”. More rants about getters and setters here tomayko.com/writings/getters-setters-fuxors .

instead of:

node.setPos(Vec3(1,2,3))

we get:

node.pos = Vec3(1,2,3)

Well for starters first has less parens, there is this quality symbol that show exalty that we are altering state of the node by setting one of its properites to some thing. Properites look much better in exrpesions too:

pythonic:

node.pos = v*(p*2 + node.pos).length()

non pythonic:

node.setPos(v*(p*2 + node.getPos()).length())
node.setPos(node.getPos()+Vec3(1,1,1)) 
node.pos = node.pos + Vec3(1,1,1)

or simply

node.pos += Vec3(1,1,1)

I have done for all set*, get* and is* functions.

2: make Vec* and Point* illiterate so that it is more like a tuple

x,y,z = Vec3(1,2,3) 
x,y,z = node.pos

There is many kind of other stuff that can use this including matrixes and the 6 types of vectors panda3d has. Also stuff with tupples can be extandend to add and subtract tuples and stuff which many other python 3d libraries use as a simple vector.

Vec3(1,2,3) + (1,2,3)

If we put our minds to it there is many places where such niceities could come in.

3: Cleaning up the module namespace. Right now all Panda3d classes live in one big module that you later import the classes you need from. I read some place that that was for speed. I wonder how much speed you really gain - i bet loading models and textures take var more time then loading couple of code files. I think folloing allready exisistng pattern of the module would impose a much better strucutre of the framework itself. Right now if you look at the file/packadge tree you see that it has beautifull sctrucutre panda3d.org/manual/index.php/Class_Reference. All classes are located neatly in their proper palces but when we get to the python layer every thing is collapsed more or less into one big namespace. Namespaces are one honking great idea – let’s do more of those!

4: Many functions take Vec or Point should take a tupple and many functions that take a Filename should be able to just take a string. This limits repeating your self over and over converint from string to file name. Many python libraries allready use tuples for vectors and the python standard library uses strings for filenames.

5: Many of the find funcitons return a collection object. It is ok to have your own collection object to inverface with C. But something should be made To make such an object more python frendly. Right now the first thing we do is convert it to a list to be iterated:

for enemy in node.findMatches('**/enemy').asList():
    print enemy

But then node paths thems self a collections. Why not have:

for child in node:
    print child
for enemy in node['**/enemy']:
    print enemy

6: Buildin namespace pollution. Right now upon initilization of panda3d the built in namespace gets poluted with many symbols like base, taskMgr, render, render2d and others. But then it turns out if they are removed they are quite missed. I kind having them there because they make coding nice and convinient. But maybe there is abetter way this could work.

7: Another fairly minor thing that panda3d struggels from is odd nameing conventions. Panda3D uses java’s camel case while the rest of python use underscores. Wake up this is not java! The funny thing is that the c++ names are in the correct python naming convetion but then they get mappend to java naming convention out of the blue.

loader.loadModel vs loader.load_model

One goal of the panda3d maintainer is:
" I say, no. It’s important to be able to easily translate a piece of python panda code into a piece of C++ panda code. It already bothers me that there are pieces of the system that only work in python (ie, actors, tasks) and that you have to learn a new way of doing things if you want to switch to C++. I don’t want any more such pieces."

Thats an interesting goal and i think adding more pythonic features will not impact it much but things like actors/tasks/gui/openAl being python entities really bother me too! Changing getters and setters arround is really nothing because you would have to rename them any ways. Ah yeah all the C++ names are with under scroes while all python names are camel case. To help the names be more consisten why not use the same name booth in C++ and in python? Beats me!

Panda3d has a defence for doing things thy way it does: panda3d is a very old library many of the pythonic convetions did not exist when they where coded. Its improtant to move the library with the langauge so that it does not get left behind like an archeich beast. Chasing this spirit of pythonic code base is hard too. Its a hard problem full of compatibitliy, speed and preference issues. But i think there is light at the end of a tunnel and i think many people could agree on some of the improvements that could be made to panda3d to help make it be more consistent with other python libraries and work with the langauge reather then against it would be a better thing!

i have 2 things i wanted to highlight which i am bothered of:
(1):
obj.setPos( pickle.loads( data ) )
so eigther vec3 should be pickable or setpos (.pos) should accept lists

(2):
Vec3(1)5 works
5
Vec3(1) does not work

Even if we only scratch the surface of the “problems” when changing vec/point stuff, it’s at least a start.

Pickle isn’t that great. It a terrible security hazed.
setPos(1,2,3) works or setPos(*a) where a = (1,2,3)

5*Vec3(1)

Would not work in most languages so i don’t see much a problem with it.

Hypnos, its not hard to make a Vec3 pickleable. I posted some idea here:
discourse.panda3d.org/viewtopic.php?t=4254
Dunno if it actually works, but something in that direction would.

Hi, I just created an account here so I could complain about this exact issue :smiley:

I’ve been playing with Panda for about a week after deciding raw OpenGL was too much work. I’ve run into a number of tricky/non-obvious cases so far working with Panda, but over all I think it is a very well built engine. So far I have been dealing with the general weirdness (like where the heck taskMgr or camera came from when I didn’t import them), but yesterday I ran into a real show stopper. I decided I wanted to use some of those nifty search features to modify selected nodes in my graph, but when I try “for node in self.root.findAllMatches(’**/text*/’):…” I get an exception because the (conceptual) list of results I just created doesn’t support iteration! Not only that, but the object I get back isn’t a sequence of any kind, instead it looks like something out of COM/VBA?

After discovering this I started seeing the rest of Panda differently. If it weren’t for some really nice features the problems with the api would be enough for me to jump ship right now (and I don’t intend to use Panda again once my current project is finished). This makes me wonder how many good Python programmers have skipped over Panda for similar reasons. It was already hard enough swallowing an 80+Meg package that comes with its own bundled Python… And the dubious documentation.

Also what is with the weird module layout and bizarre naming? The root module is named ‘direct’? ‘pandac’?

Anyway most of the above is covered earlier in the thread. I just wanted to confirm that the api is likely chasing away users and developers.