Keybindings

from direct.showbase.DirectObject import DirectObject

class Controls(DirectObject):
    """Map hard-states to state controls and events to event controls.
    
    To distinguish keyboard keys from dictionary keys "keyboard keys"
    will be called "boardkeys" for short. Note that for our purposes
    "mouse buttons" can be considered "boardkeys" as well because they
    have also have an -up event after the button is released.
    
    Control mapping has many advantages. State mappings can be queried when
    needed without setting up additional listeners. Both State and
    Event mappings can be changed at runtime. And both allow multiple events to
    trigger a single control."""
    def __init__(self,keybindings={},translation={},activate = True):
        #controls is a dictionary of what control keys are currently pressed
        #The keyOn/keyOff events update this, and the task queries it for input
        self.controls = {}
        #keybindings is a dictionary to map boardkeys to their controls.
        self.stateBindings(keybindings,False)
        #translation is a dictionary to map boardkey events to
        #custom controls events
        self.eventBindings(translation,False)
        if activate:#bind controls by default
            self.bindControls()
    def stateBindings(self,keybindings,activate = True):
        """Setup state control dictionary.
        
        keybindings must be a dictionary of the form:
        {'control name1':['boardkey event name1',etc.],etc.} """
        self.keybindings = keybindings.copy()
        self.controls = {} #blank the self.conrols in preparation for re-bind
        for k in self.keybindings.keys():
            self.controls[k] = False
        if activate:#re-bind the controls by default
            self.bindControls()
    def eventBindings(self,translation,activate = True):
        """Translate hard-event to event control.
        
        translation must be a dictionary of the form:
        {'control event name1':['boardkey event name1',etc.],etc.}"""
        self.translation = translation.copy()
        if activate:#re-bind the controls by default
            self.bindControls()
    def __translator(self,control,*params):
        if params:
            messenger.send(control,list(params))
        else:
            messenger.send(control)
    def bindControls(self):
        """Map the controls to the boardkeys that activate them."""
        self.ignoreAll()
        for k,events in self.translation.items():
            for v in events:
                self.accept(v,self.__translator,[k,])
        for control,boardkeys in self.keybindings.items():
            for key in boardkeys:
                self.acceptOnce(key,self.__keyOn,[control,])#no autorepeats
                self.accept(key+'-up',self.__keyOff,[control,key])
    def __keyOn(self,control):
        self.controls[control] = True#set control to active
    def __keyOff(self,control,event):
        self.controls[control] = False#set control to off
        self.acceptOnce(event,self.__keyOn,[control,])#listen once again


An example of how to use a controls module. You should be able to figure it out.

from controls import *
import direct.directbase.DirectStart
class World(DirectObject):
    def __init__(self):
        self.stateRight = {'up':['arrow_up','8',],
            'down':['arrow_down','2',],'left':['arrow_left','4',],
            'right':['arrow_right','6'],'fire':['space',],}
        self.eventRight = {'print control states':['f1',],
            'switch to left handed':['page_up','page_down']}
        self.stateLeft = {'up':['w',],
            'down':['s',],'left':['a',],
            'right':['d'],'fire':['space',],}
        self.eventLeft = {'print control states':['f12','enter'],
            'switch to right handed':['2',]}
        self.con = Controls(self.stateRight,self.eventRight)
        self.accept('print control states',self.OnPrint)
        self.accept('switch to left handed',self.OnLeft)
        self.accept('switch to right handed',self.OnRight)
    def OnPrint(self):
        print self.con.controls
    def OnLeft(self):
        print 'left handed'
        self.con.stateBindings(self.stateLeft,False)
        self.con.eventBindings(self.eventLeft)
    def OnRight(self):
        print 'right handed'
        self.con.eventBindings(self.eventRight,False)
        self.con.stateBindings(self.stateRight)
World()
run()


Due to some changes in the way panda creates keyboard events as of 1.3.0, (As of 1.3.0 the keyboard auto-repeat sends a “-repeat” event instead of another key down event.) the script is taking some unnecessary extra steps. To fix it, change these two methods:

Omit the event parameter and the last line in __keyOff() :

    def __keyOff(self,control,):
        self.controls[control] = False#set control to off

Change the acceptOnce() in bindControls() to accept():

    def bindControls(self):
        """Map the controls to the boardkeys that activate them."""
        self.ignoreAll()
        for k,events in self.translation.items():
            for v in events:
                self.accept(v,self.__translator,[k,])
        for control,boardkeys in self.keybindings.items():
            for key in boardkeys:
                self.accept(key,self.__keyOn,[control,])
                self.accept(key+'-up',self.__keyOff,[control,])

Note that both versions will still work in both versions of panda, but it is probably more efficient to use the correct version.