Panda3D Eclipse / Pydev setup

Hi guys!

(Edit: Unless you’re dead set on using Eclipse/PyDev, I would recommend giving Sublime Text 2 a go. See this thread: https://discourse.panda3d.org/viewtopic.php?p=84204)

There are already a number of helper posts on how to get Panda3D working in Eclipse with Pydev, but I have yet to see one fixing code completion on classes imported from panda3d.core. Maybe it works better if you use the bundled python runtime, I don’t know. Still If you’re like me and using your own installation, I’m here to help you fix your code completion.

If you haven’t setup Panda3D in Eclipse before you’ll want to read these posts.

[url]Code complete in Pydev and Eclipse with Panda3D]
[url]urgent help with eclipse]

Now, the code completion. Due to the black magic (see http://www.panda3d.org/blog/?p=22) used to import from panda3d.core, Pydev is unable see the classes within. We can help Pydev by creating some predefined compilations:

http://pydev.org/manual_101_interpreter.html#id2

There are A LOT of classes in panda3d.core so doing it by hand is not an option. Instead you can use the below script to generate a file called panda3d.core.pypredef. Place it anywhere you like, and then configure Pydev as mentioned in the above link. The script adds all classes, “constants” and functions in panda3d.core. It also adds all member functions of said classes. I haven’t been able to make it add function arguments, but I’m working on it. Enjoy!

Edit: Updated the code with rndbit’s fixes.

# -*- coding: UTF-8 -*-

import inspect
from panda3d.core import *
import libpanda, libpandaexpress

from panda3d.fx import *
import libpandafx

from panda3d.dtoolconfig import *
import libp3dtoolconfig

from panda3d.physics import *
import libpandaphysics

from panda3d.direct import *
import libp3direct

from panda3d.egg import *
import libpandaegg

from panda3d.ode import *
import libpandaode

from panda3d.bullet import *
import libpandabullet

from panda3d.vision import *
import libp3vision

#from panda3d.physx import *
#import libpandaphysx

from panda3d.ai import *
import libpandaai

#from panda3d.awesomium import *
#import libp3awesomium

#from panda3d.speedtree import *
#import libpandaspeedtree

from panda3d.rocket import *
import _rocketcore, _rocketcontrols, libp3rocket

BUILD = {
    'fx'           : [libpandafx]
    ,'core'        : [libpanda, libpandaexpress]
    ,"dtoolconfig" : [libp3dtoolconfig]
    ,"physics"     : [libpandaphysics]
    ,"fx"          : [libpandafx]
    ,"direct"      : [libp3direct]
    ,"egg"         : [libpandaegg]
    ,"ode"         : [libpandaode]
    ,"bullet"      : [libpandabullet]
    ,"vision"      : [libp3vision]
#    ,"physx"       : [libpandaphysx]
    ,"ai"          : [libpandaai]
#    ,"awesomium"   : [libp3awesomium]
#    ,"speedtree"   : [libpandaspeedtree]
    ,"rocket"      : [_rocketcore, _rocketcontrols, libp3rocket],
}

indent = '    '
importptrn = 'from panda3d.%s import %s'
skip = [
                'DtoolGetSupperBase',
                'DtoolClassDict'
             ]

def record(t, name, f, noclasses=False, baseIndent=''):
    if name.startswith('__'): return
    if isinstance(t, int):
        f.write('%s%s = int\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, float):
        f.write('%s%s = float\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, str):
        f.write('%s%s = str\n\n' % (baseIndent, name))
        return True
    elif inspect.isclass(t):
        if noclasses: return
        try:
            f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, name, baseIndent, baseIndent))
        except:
            f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, t.__name__, baseIndent, baseIndent))
        
        for _name in dir(t):
            if _name in skip:
                continue
            try:
                _t = eval('%s.%s' %(name, _name))
            except:
                return False
            record(_t, _name, f, noclasses=True, baseIndent=('%s    ' % baseIndent))
             
        return True
         
    elif callable(t):
        if hasattr(t, '__objclass__'):
            f.write('%sdef %s(self):\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
        else:
            f.write('%sdef %s():\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
            return True
    return False

def generateCore(base_name, modules):
 
    f = open('predef/panda3d.%s.pypredef' % base_name, 'w+')

    for module in modules:
        for name in dir(module):
            try:
                exec(importptrn % (base_name, name))
                t = eval(name)
             
                print "Building %s..." % name
                record(t, name, f)
             
            except Exception, e:
                print e
                continue
         
    f.close()
     
if __name__ == '__main__':
    for (name, module) in BUILD.items():
        generateCore(name, module)

I will try this later, but thanks :slight_smile:

Very useful!
I’ve just tried and it works like a charm!

i updated script a bit. minor bug corrected that could have resulted in less predefined symbols generated. also it makes predefines for all major modules.

also tiny trick for pydev and you can enjoy autocompletion for stuff like base and so on:

assert isinstance(base, ShowBase)
assert isinstance(loader, Loader)
assert isinstance(render, NodePath)
assert isinstance(taskMgr, direct.task.Task.TaskManager)
assert isinstance(globalClock, ClockObject)
# -*- coding: UTF-8 -*-

import inspect
from panda3d.core import *
import libpanda, libpandaexpress

from panda3d.fx import *
import libpandafx

from panda3d.dtoolconfig import *
import libp3dtoolconfig

from panda3d.physics import *
import libpandaphysics

from panda3d.direct import *
import libp3direct

from panda3d.egg import *
import libpandaegg

from panda3d.ode import *
import libpandaode

from panda3d.bullet import *
import libpandabullet

from panda3d.vision import *
import libp3vision

#from panda3d.physx import *
#import libpandaphysx

from panda3d.ai import *
import libpandaai

#from panda3d.awesomium import *
#import libp3awesomium

#from panda3d.speedtree import *
#import libpandaspeedtree

from panda3d.rocket import *
import _rocketcore, _rocketcontrols, libp3rocket

BUILD = {
    'fx'           : [libpandafx]
    ,'core'        : [libpanda, libpandaexpress]
    ,"dtoolconfig" : [libp3dtoolconfig]
    ,"physics"     : [libpandaphysics]
    ,"fx"          : [libpandafx]
    ,"direct"      : [libp3direct]
    ,"egg"         : [libpandaegg]
    ,"ode"         : [libpandaode]
    ,"bullet"      : [libpandabullet]
    ,"vision"      : [libp3vision]
#    ,"physx"       : [libpandaphysx]
    ,"ai"          : [libpandaai]
#    ,"awesomium"   : [libp3awesomium]
#    ,"speedtree"   : [libpandaspeedtree]
    ,"rocket"      : [_rocketcore, _rocketcontrols, libp3rocket],
}

indent = '    '
importptrn = 'from panda3d.%s import %s'
skip = [
                'DtoolGetSupperBase',
                'DtoolClassDict'
             ]

def record(t, name, f, noclasses=False, baseIndent=''):
    if name.startswith('__'): return
    if isinstance(t, int):
        f.write('%s%s = int\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, float):
        f.write('%s%s = float\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, str):
        f.write('%s%s = str\n\n' % (baseIndent, name))
        return True
    elif inspect.isclass(t):
        if noclasses: return
        f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, t.__name__, baseIndent, baseIndent))
        for _name in dir(t):
            if _name in skip:
                continue
            try:
                _t = eval('%s.%s' %(name, _name))
            except:
                return False
            record(_t, _name, f, noclasses=True, baseIndent=('%s    ' % baseIndent))
             
        return True
         
    elif callable(t):
        if hasattr(t, '__objclass__'):
            f.write('%sdef %s(self):\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
        else:
            f.write('%sdef %s():\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
            return True
    return False

def generateCore(base_name, modules):
 
    f = open('panda3d.%s.pypredef' % base_name, 'w+')

    for module in modules:
        for name in dir(module):
            try:
                exec(importptrn % (base_name, name))
                t = eval(name)
             
                print "Building %s..." % name
                record(t, name, f)
             
            except Exception, e:
                print e
                continue
         
    f.close()
     
if __name__ == '__main__':
    for (name, module) in BUILD.items():
        generateCore(name, module)

i tried this and after restarting it worked… almost. It works for CollisionTraverser (and a couple of others) but not for Vec4 and Point3. They are still marked as errors in this line:

from panda3d.core import Vec4, Point3, CollisionTraverser, CollisionNode, CollisionRay, CollisionHandlerQueue, loadPrcFileData

Any idea why this is?

Make sure you are using Panda3D 1.8.0. But what is the error message?

David

The problem (missing Vec2, Vec3,…) lies in this line:

f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, t.__name__, baseIndent, baseIndent))

It works for some classes if you use the name that was passed to record():

f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, name, baseIndent, baseIndent))

Here’s a part of a diff between the results:

> class LVector3f:
132116c132116
< class Vec3D:
---
> class LVector3d:
132327c132327
< class Vec3F:
---
> class LVector3f:
132538c132538
< class Vec4:
---
> class LVector4f:
132705c132705
< class Vec4D:
---
> class LVector4d:
132872c132872
< class Vec4F:
---
> class LVector4f:
136097c136097
< class string:
---
> class CString:
136110c136110
< class xel:
---
> class pixel:
136148c136148
< class CPTADouble:

As you can see it now works for Vec23F, Vec4 and the others but in some cases like LVector4f the result seems just wrong :frowning:

EDIT: Whooops! Why didn’t I see this before? There is an offset of one class with that change… LVector3f becomes Vec4

EDIT 2: Hm ok I think the fix is still correct. The offset in the above diff output just means that the new result is missing one definition at the top.

Alrighty people, this at least to me seems almost perfect at the moment.

  1. Minor correction to get back Vec3 and friends:
try:
    f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, name, baseIndent, baseIndent))
except:
    f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, t.__name__, baseIndent, baseIndent))

Full code:

# -*- coding: UTF-8 -*-

import inspect
from panda3d.core import *
import libpanda, libpandaexpress

from panda3d.fx import *
import libpandafx

from panda3d.dtoolconfig import *
import libp3dtoolconfig

from panda3d.physics import *
import libpandaphysics

from panda3d.direct import *
import libp3direct

from panda3d.egg import *
import libpandaegg

from panda3d.ode import *
import libpandaode

from panda3d.bullet import *
import libpandabullet

from panda3d.vision import *
import libp3vision

#from panda3d.physx import *
#import libpandaphysx

from panda3d.ai import *
import libpandaai

#from panda3d.awesomium import *
#import libp3awesomium

#from panda3d.speedtree import *
#import libpandaspeedtree

from panda3d.rocket import *
import _rocketcore, _rocketcontrols, libp3rocket

BUILD = {
    'fx'           : [libpandafx]
    ,'core'        : [libpanda, libpandaexpress]
    ,"dtoolconfig" : [libp3dtoolconfig]
    ,"physics"     : [libpandaphysics]
    ,"fx"          : [libpandafx]
    ,"direct"      : [libp3direct]
    ,"egg"         : [libpandaegg]
    ,"ode"         : [libpandaode]
    ,"bullet"      : [libpandabullet]
    ,"vision"      : [libp3vision]
#    ,"physx"       : [libpandaphysx]
    ,"ai"          : [libpandaai]
#    ,"awesomium"   : [libp3awesomium]
#    ,"speedtree"   : [libpandaspeedtree]
    ,"rocket"      : [_rocketcore, _rocketcontrols, libp3rocket],
}

indent = '    '
importptrn = 'from panda3d.%s import %s'
skip = [
                'DtoolGetSupperBase',
                'DtoolClassDict'
             ]

def record(t, name, f, noclasses=False, baseIndent=''):
    if name.startswith('__'): return
    if isinstance(t, int):
        f.write('%s%s = int\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, float):
        f.write('%s%s = float\n\n' % (baseIndent, name))
        return True
    elif isinstance(t, str):
        f.write('%s%s = str\n\n' % (baseIndent, name))
        return True
    elif inspect.isclass(t):
        if noclasses: return
        try:
            f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, name, baseIndent, baseIndent))
        except:
            f.write('%sclass %s:\n%s    def __init__(self):\n%s        pass\n' % (baseIndent, t.__name__, baseIndent, baseIndent))
        
        for _name in dir(t):
            if _name in skip:
                continue
            try:
                _t = eval('%s.%s' %(name, _name))
            except:
                return False
            record(_t, _name, f, noclasses=True, baseIndent=('%s    ' % baseIndent))
             
        return True
         
    elif callable(t):
        if hasattr(t, '__objclass__'):
            f.write('%sdef %s(self):\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
        else:
            f.write('%sdef %s():\n%s    pass\n' % (baseIndent, t.__name__, baseIndent))
            return True
    return False

def generateCore(base_name, modules):
 
    f = open('predef/panda3d.%s.pypredef' % base_name, 'w+')

    for module in modules:
        for name in dir(module):
            try:
                exec(importptrn % (base_name, name))
                t = eval(name)
             
                print "Building %s..." % name
                record(t, name, f)
             
            except Exception, e:
                print e
                continue
         
    f.close()
     
if __name__ == '__main__':
    for (name, module) in BUILD.items():
        generateCore(name, module) 
  1. I made panda3d importer module which aids pydev autocompletion. What it does is importing most common stuff, and asserts their types to help pydev figure them out.
  • p3d.py *
'''
* The Great panda3d/pydev autocompletion *
*        code is in public domain        *
Created on Nov 29, 2012
@author: rndbit

1. Use generated predefines
2. Insert 'direct' and 'panda3d' to forced builtins
3. Edit pycompletionserver.py (located somewhere like in C:\Program Files\Eclipse\plugins\org.python.pydev_2.7.1.2012100913\pysrc\ )
   Insert after 'currDirModule = None':
   try:
       from pandac.PandaModules import loadPrcFileData
       loadPrcFileData("", "window-type none")
       import direct
       import direct.directbase.DirectStart
   except:
       pass
'''

import direct, __builtin__, panda3d
from panda3d import core, dtoolconfig, physics, fx, egg, ode, bullet, vision, rocket, ai #, physx, awesomium, speedtree
import direct.directbase.DirectStart
from panda3d.core import Vec2, Vec2D, Vec2F, Vec3, Vec3F, Vec3D, Vec4, Vec4F, Vec4D,\
        Point2, Point2D, Point2F, Point3, Point3D, Point3F, Point4, Point4D, Point4F,\
        VBase2, VBase2D, VBase2F, VBase3, VBase3D, VBase3F, VBase4, VBase4D, VBase4F
from direct.stdpy.file import *
from direct.stdpy import threading2 as threading

if not hasattr(__builtin__, 'base'):
    base          = None
    aspect2d      = None
    aspect2dp     = None
    bboard        = None
    bpdb          = None
    camera        = None
    config        = None
    cpMgr         = None
    deltaProfiler = None
    directNotify  = None
    direct        = None
    eventMgr      = None
    globalClock   = None
    jobMgr        = None
    loader        = None
    messenger     = None
    onScreenDebug = None
    ostream       = None
    pixel2d       = None
    pixel2dp      = None
    render        = None
    render2d      = None
    render2dp     = None
    taskMgr       = None
    vfs           = None

assert isinstance(base, direct.showbase.ShowBase.ShowBase)
assert isinstance(aspect2d, core.NodePath)
assert isinstance(aspect2dp, core.NodePath)
assert isinstance(bboard, direct.showbase.BulletinBoard.BulletinBoard)
assert isinstance(bpdb, direct.showbase.BpDb.BpDb)
assert isinstance(camera, core.NodePath)
assert isinstance(config, core.DConfig)
assert isinstance(cpMgr, core.ConfigPageManager)
assert isinstance(deltaProfiler, direct.directutil.DeltaProfiler.DeltaProfiler)
assert isinstance(directNotify, direct.directnotify.DirectNotify.DirectNotify)
assert isinstance(eventMgr, direct.showbase.EventManager.EventManager)
assert isinstance(globalClock, core.ClockObject)
assert isinstance(jobMgr, direct.showbase.JobManager.JobManager)
assert isinstance(loader, direct.showbase.Loader.Loader)
assert isinstance(messenger, direct.showbase.Messenger.Messenger)
assert isinstance(onScreenDebug, direct.showbase.OnScreenDebug.OnScreenDebug)
assert isinstance(ostream, core.ostream)
assert isinstance(pixel2d, core.NodePath)
assert isinstance(pixel2dp, core.NodePath)
assert isinstance(render, core.NodePath)
assert isinstance(render2d, core.NodePath)
assert isinstance(render2dp, core.NodePath)
assert isinstance(taskMgr, direct.task.Task.TaskManager)
assert isinstance(vfs, core.VirtualFileSystem)

And usage is as simple as from p3d import *

  1. A tiny trick for pydev autocompletion server to get proper panda3d autocompletions. Following snippet in pyautocompletionserver.py makes all the difference:
try:
    import direct
    import direct.directbase.DirectStart
except:
    pass

See p3d.py comment above for more detailed instructions.

EDIT:
Actually its not perfect yet, we need function docs in docstrings \o/

EDIT2:
Forgot to mention that path Panda3D-1.8.0\direct has to be added to libraries.

rndbit thank you, only issue remains for me.

This crashes:

from direct.showbase.ShowBase import ShowBase
from p3d import *

ShowBase()

The error is:
raise StandardError, “Attempt to spawn multiple ShowBase instances!”
StandardError: Attempt to spawn multiple ShowBase instances!

If I comment “from p3d import *” the code runs fine however.

Hey. I updated my previous post with new version of p3d - grab it.
It wont solve your problem however as p3d creates instance of ShowBase already therefore you should not create it yourself again. All you need to do is just call base.run() to run main loop and thats it.

thanks, how can I edit p3d.py so that it doesn’t create an instance of ShowBase()?

I tried commenting this line but it appears the instance is being generated elsewhere.

isinstance(base, direct.showbase.ShowBase.ShowBase)

i think you should comment this out:

import direct.directbase.DirectStart

But i think it will not quite work. You really should just not initialize ShowBase yourself and just start main loop as i said earlier. In case there is some kind of limitation preventing you from doing so - tell me and maybe we can figure out workaround.

@rndbit, thanks for keeping the code updated (I’ve been off Panda3d for a while). I’ve updated the original post.

You say to place the script anywhere, but that doesn’t make sense to me. I can’t put this on my desktop and then expect it to work. Could you please explain how this works more thoroughly? So far I’ve got Eclipse working with panda3d and just want the auto-complete, however this step confuses the hell out of me. My best guess was to create p3dautocomplete.py in C:\Panda3D-1.8.1 directory, and then I “reconfigured” my python interpreter. It didn’t do anything at all. I also just tried running the script, and that gave an error. So, again, please elaborate a bit more on the steps for getting this to work. Thanks.

use script from my post. you are supposed to run script so it generates predef files used in autocompletion. then those predef files should be added in pydev config

When I run the script I got the following error:

Traceback (most recent call last):
  File "gen.py", line 43, in <module>
    from panda3d.rocket import *
  File "/usr/share/panda3d/panda3d.py", line 204, in __getattr__
    for obj in self.__manager__.libimport(lib).__dict__:
  File "/usr/share/panda3d/panda3d.py", line 112, in libimport
    return __import__(name)
ImportError: libRocketCore.so.1: cannot open shared object file: No such file or directory

I use Ubuntu Raring/13.04 and I downloaded and installed the librocket package. I suspect I have to link it to the panda installation but I cannot figure out how. Any ideas where I went wrong?

at least in windows librocket is bundled with panda. should probably be true for other platforms. script has to run in panda3d environment. means if you can run demo - script will work. unless ofc there are some unforeseen conditions we arent aware of.

I ran the script in /usr/share/panda3d.
I had to install librocket separately, perhaps that is the problem?