Ejemplo n.º 1
0
class panda3dIOClass( DirectObject.DirectObject ):
  # set gui key to None if you want to call toggleConsole from outside this class
  gui_key = CONSOLE_TOGGLE_KEY
  autocomplete_key = CONSOLE_AUTOCOMPLETE_KEY
  autohelp_key = CONSOLE_AUTOHELP_KEY
  
  scroll_up_repeat_key = CONSOLE_SCROLL_UP_KEY + "-repeat"
  scroll_down_repeat_key = CONSOLE_SCROLL_DOWN_KEY + "-repeat"
  
  # change size of text and number of characters on one line
  # scale of frame ( must be small (0.0x)
  scale = PANDA3D_CONSOLE_SCALE
  # to define a special font, if loading fails the default font is used (without warning)
  font = PANDA3D_CONSOLE_FONT
  fontWidth = PANDA3D_CONSOLE_FONT_WIDTH
  # frame position and size (vertical & horizontal)
  h_pos   = PANDA3D_CONSOLE_HORIZONTAL_POS
  h_size  = PANDA3D_CONSOLE_HORIZONTAL_SIZE
  # v_size + v_pos should not exceed 2.0, else parts of the interface will not be visible
  # space above the frame ( must be below 2.0, best between 0.0 and 1.0 )
  v_pos   = PANDA3D_CONSOLE_VERTICAL_POS
  # vertical size of the frame ( must be at max 2.0, best between 0.5 and 2.0 )
  v_size  = PANDA3D_CONSOLE_VERTICAL_SIZE
  linelength = int((h_size/scale - 5) / fontWidth)
  print "max number of characters on a length:", linelength
  numlines = int(v_size/scale - 5)
  defaultTextColor  = (0.0,0.0,0.0,1.0)
  autoCompleteColor = (0.9,0.9,0.9,1.0)
  def __init__( self, parent ):
    self.parent = parent
    
    # line wrapper
    self.linewrap = textwrap.TextWrapper()
    self.linewrap.width = self.linelength
    
    # calculate window size
    left   = (self.h_pos) / self.scale
    right  = (self.h_pos + self.h_size) / self.scale
    bottom = (self.v_pos) / self.scale
    top    = (self.v_pos + self.v_size) /self.scale
    
    # panda3d interface
    self.consoleFrame = DirectFrame ( relief = DGG.GROOVE
                                    , frameColor = (200, 200, 200, 0.5)
                                    , scale=self.scale
                                    , frameSize = (0, self.h_size / self.scale, 0, self.v_size / self.scale) )
    self.windowEvent( base.win )
    
    # try to load the defined font
    try:
      fixedWidthFont = loader.loadFont(self.font)
    except:
      print "pandaInteractiveConsole.py :: could not load the defined font %s" % str(self.font)
      fixedWidthFont = DGG.getDefaultFont()
    # if font is not valid use default font
    if not fixedWidthFont.isValid():
      if self.font is None:
        print "pandaInteractiveConsole.py :: could not load the defined font %s" % str(self.font)
      fixedWidthFont = DGG.getDefaultFont()
    
    # text entry line
    self.consoleEntry = DirectEntry ( self.consoleFrame
                                    , text        = ""
                                    , command     = self.onEnterPress
                                    , width       = self.h_size/self.scale - 2
                                    , pos         = (1, 0, 1.5)
                                    , initialText = ""
                                    , numLines    = 1
                                    , focus       = 1
                                    , entryFont   = fixedWidthFont)
    
    # output lines
    self.consoleOutputList = list()
    for i in xrange( self.numlines ):
      label = OnscreenText( parent = self.consoleFrame
                          , text = ""
                          , pos = (1, -i+3+self.numlines)
                          , align=TextNode.ALeft
                          , mayChange=1
                          , scale=1.0
                          , fg = self.defaultTextColor )
      label.setFont( fixedWidthFont )
      self.consoleOutputList.append( label )
    
    # list of the last commands of the user
    self.userCommandList = list()
    self.userCommandListLength = 100
    for i in xrange(self.userCommandListLength):
      self.userCommandList.append('')
    self.userCommandPos = 0
    
    # buffer for data
    self.textBuffer = list()
    self.textBufferLength = 1000
    for i in xrange(self.textBufferLength):
      self.textBuffer.append(['', DEFAULT_COLOR])
    self.textBufferPos = self.textBufferLength-self.numlines
    
    # toggle the window at least once to activate the events
    self.toggleConsole()
    self.toggleConsole()
    
    # call the help-command on start
    self.onEnterPress("help")
    
  
  
  # write a string to the panda3d console
  def write( self, printString, color=defaultTextColor ):
    # remove not printable characters (which can be input by console input)
    printString = re.sub( r'[^%s]' % re.escape(string.printable[:95]), "", printString)
    
    splitLines = self.linewrap.wrap(printString)
    for line in splitLines:
      self.textBuffer.append( [line, color] )
      self.textBuffer.pop(0)
    
    self.updateOutput()
  
  def updateOutput( self ):
    for lineNumber in xrange(self.numlines):
      lineText, color = self.textBuffer[lineNumber + self.textBufferPos]
      self.consoleOutputList[lineNumber].setText( lineText )
      self.consoleOutputList[lineNumber]['fg'] = color
  
  # toggle the gui console
  def toggleConsole( self ):
    self.consoleFrame.toggleVis()
    hidden = self.consoleFrame.isHidden()
    self.consoleEntry['focus'] != hidden
    if hidden:
      self.ignoreAll()
      self.accept( self.gui_key, self.toggleConsole )
      
      #playerController.enableInput()
      #unpause("v")
    else:
      self.ignoreAll()
      
      #playerController.disableInput()
      #pause("v")
      
      self.accept( CONSOLE_SCROLL_UP_KEY, self.scroll, [-5] )
      self.accept( self.scroll_up_repeat_key, self.scroll, [-5] )
      self.accept( CONSOLE_SCROLL_DOWN_KEY, self.scroll, [5] )
      self.accept( self.scroll_down_repeat_key, self.scroll, [5] )
      self.accept( 'window-event', self.windowEvent)
      
      self.accept( CONSOLE_PREVIOUS_COMMAND_KEY , self.scrollCmd, [ 1] )
      self.accept( CONSOLE_NEXT_COMMAND_KEY, self.scrollCmd, [-1] )
      
      self.accept( self.gui_key, self.toggleConsole )
      #self.accept( self.autocomplete_key, self.autocomplete )
      #self.accept( self.autohelp_key, self.autohelp )
      
      # accept v, c and x, where c & x copy's the whole console text
      #messenger.toggleVerbose()
      #for osx use ('meta')
      if sys.platform == 'darwin':
        self.accept( 'meta', self.unfocus )
        self.accept( 'meta-up', self.focus )
        self.accept( 'meta-c', self.copy )
        self.accept( 'meta-x', self.cut )
        self.accept( 'meta-v', self.paste )
      #for windows use ('control')
      if sys.platform == 'win32' or sys.platform == 'linux2':
        self.accept( 'control', self.unfocus )
        self.accept( 'control-up', self.focus )
        self.accept( 'control-c', self.copy )
        self.accept( 'control-x', self.cut )
        self.accept( 'control-v', self.paste )
  
  
#  def autohelp( self ):
#    currentText = self.consoleEntry.get()
#    currentPos = self.consoleEntry.guiItem.getCursorPosition()
#    self.parent.autohelp( currentText, currentPos )
  
  def focus( self ):
    self.consoleEntry['focus'] = 1
  
  def unfocus( self ):
    self.consoleEntry['focus'] = 0
  
  def copy( self ):
    copy = self.consoleEntry.get()
    clipboard.setText( copy )
  
  def paste( self ):
    oldCursorPos = self.consoleEntry.guiItem.getCursorPosition()
    clipboardText = clipboard.getText()
    
    # compose new text line
    oldText = self.consoleEntry.get()
    newText = oldText[0:oldCursorPos] + clipboardText + oldText[oldCursorPos:]
    
    clipboardTextLines = newText.split(os.linesep)
    
    for i in xrange( len(clipboardTextLines)-1 ):
      currentLine = clipboardTextLines[i]
      # we only want printable characters
      currentLine = re.sub( r'[^' + re.escape(string.printable[:95]) + ']', "", currentLine)
      
      # set new text and position
      self.consoleEntry.set( currentLine )
      self.onEnterPress( currentLine )
    currentLine = clipboardTextLines[-1]
    currentLine = re.sub( r'[^' + re.escape(string.printable[:95]) + ']', "", currentLine)
    self.consoleEntry.set( currentLine )
    self.consoleEntry.setCursorPosition( len(self.consoleEntry.get()) )
    self.focus()
  
  def cut( self ):
    clipboard.setText( self.consoleEntry.get() )
    self.consoleEntry.enterText('')
    self.focus()
  
  def scroll( self, step ):
    self.textBufferPos += step
    self.textBufferPos = min( self.textBufferLength-self.numlines, max( 0, self.textBufferPos ) )
    self.updateOutput()
  
  def scrollCmd( self, step ):
    oldCmdPos = self.userCommandPos
    self.userCommandPos += step
    self.userCommandPos = min( self.userCommandListLength-1, max( 0, self.userCommandPos ) )
    self.userCommandList[oldCmdPos] = self.consoleEntry.get()
    newCmd = self.userCommandList[self.userCommandPos]
    self.consoleEntry.set( newCmd )
    self.consoleEntry.setCursorPosition( len(newCmd) )
  
  def onEnterPress( self, textEntered ):
    # set to last message
    self.textBufferPos = self.textBufferLength-self.numlines
    # clear line
    self.consoleEntry.enterText('')
    self.focus()
    # add text entered to user command list & remove oldest entry
    self.userCommandList.insert( 1, textEntered )
    self.userCommandList[0] = ''
    self.userCommandList.pop( -1 )
    self.userCommandPos = 0
    
    # call the interpreter to handle the input
    interpreter = cliClass()
    result = interpreter.interpreter(textEntered)
    # write the entered text to the output
    self.write(textEntered, (0.0, 0.0, 1, 1))
    print textEntered
    # write each line seperately to the output
    for line in result.split('\n'):
        line = "        " + line
        self.write(line, (0, 0, 0, 1))
        print line
  
  def windowEvent( self, window ):
    """
    This is a special callback.
    It is called when the panda window is modified.
    """
    wp = window.getProperties()
    width = wp.getXSize() / float(wp.getYSize())
    height = wp.getYSize() / float(wp.getXSize())
    if width > height:
      height = 1.0
    else:
      width = 1.0
    # aligned to center
    consolePos = Vec3(-self.h_size/2, 0, -self.v_size/2)
    # aligned to left bottom
    #consolePos = Vec3(-width+self.h_pos, 0, -height+self.v_pos)
    # aligned to right top
    #consolePos = Vec3(width-self.h_size, 0, height-self.v_size)
    # set position
    self.consoleFrame.setPos( consolePos )
Ejemplo n.º 2
0
  def __call__(self): return self.impl()
    
class _GetchUnix:
  def __init__(self):
    import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac
  def __call__(self):
    import sys, tty, termios
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
      tty.setraw(sys.stdin.fileno())
      ch = sys.stdin.read(1)
    finally:
      termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return ch
class _GetchWindows:
  def __init__(self):
    import msvcrt
  def __call__(self):
    import msvcrt
    return msvcrt.getch()

class _GetchMacCarbon:
  """
  A function which returns the current ASCII key that is down;
  if no ASCII key is down, the null string is returned.  The
  page http://www.mactech.com/macintosh-c/chap02-1.html was
  very helpful in figuring out how to do this.
  """
  def __init__(self):
    import Carbon
    Carbon.Evt #see if it has this (in Unix, it doesn't)
  def __call__(self):
    import Carbon
    if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask
      return ''
    else:
      #
      # The event contains the following info:
      # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
      #
      # The message (msg) contains the ASCII char which is
      # extracted with the 0x000000FF charCodeMask; this
      # number is converted to an ASCII character with chr() and
      # returned
      #
      (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
      return chr(msg & 0x000000FF)

# ---- character reading from console - END ----





class pandaInteractiveConsole( InteractiveConsole, DirectObject.DirectObject ):
  def __init__( self, locals, interfaces=GUI|CONSOLE ):
    print "I: pandaInteractiveConsole.__init__"
    
    InteractiveConsole.__init__( self, locals )
   
    self.guiEnabled = (interfaces & GUI)
    if self.guiEnabled:
      print "I:  - GUI enabled"
      
      top = 1/SCALE - (V_SPACE/SCALE)
      bottom = 1/SCALE - (V_SPACE/SCALE) - (V_SIZE/SCALE)
      # panda3d interface
      self.consoleFrame = DirectFrame( relief = DGG.GROOVE
        , frameColor = (200, 200, 200, 0.85), scale=SCALE
        , frameSize = (-1/SCALE, 1/SCALE, top, bottom))
      self.accept( "f1", self.toggleConsole )
      # text entry line
      self.consoleEntry = DirectEntry( self.consoleFrame, text = "", command=self.setText, width = 1/SCALE*2-2
        , pos =(-1/SCALE+1,0,bottom+1), initialText="", numLines = 1, focus=1)
      # output lines
      self.consoleOutputList = list()
      for i in xrange( NUMLINES ):
        label = OnscreenText( parent = self.consoleFrame, text = "", pos = (-1/SCALE+1, bottom+1+NUMLINES-i) #-1-i)
          , align=TextNode.ALeft, mayChange=1, scale=1.0)
        self.consoleOutputList.append( label )
        self.toggleConsole()
        
    self.consoleEnabled = (interfaces & CONSOLE)
    self.stdout = sys.stdout
    if self.consoleEnabled:
      print "I:  - Console enabled"
      # function to read the keyboard inputs
      self.readKey = _Getch()
      
      # redirect input and output
      self.stdout = sys.stdout
      sys.stdout = self
      if sys.platform == 'linux2' or sys.platform == 'darwin':
        self.setConsoleNonBlocking( self.stdout )
    
    self.write( "D: myInteractiveConsole.__init__\n" )
    # buffer of current text
    self.linebuffer = ''
    self.linebufferPos = 0
  
  def setConsoleBlocking( self, fd_in ):
    fd = fd_in.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    try:
    	fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.FNDELAY)
    except:
      pass
  
  def setConsoleNonBlocking( self, fd_in ):
    fd = fd_in.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    try:
      fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    except:
      self.write( "D: console init failed\n" )
      self.setConsoleBlocking( fd )
    
    '''
    # set read non blocking
    fd = sys.stdin.fileno()
    self.oldterm = termios.tcgetattr(fd)
    newattr = self.oldterm
    self.oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
    newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
    termios.tcsetattr(fd, termios.TCSANOW, newattr)
    fcntl.fcntl(fd, fcntl.F_SETFL, self.oldflags | os.O_NONBLOCK)
    '''
  
  def __del__( self ):
    if self.consoleEnabled:
      sys.stdout = self.stdout
      if sys.platform == 'linux2' or sys.platform == 'darwin':
        self.write( "setting old terminal settings\n" )
        fd = sys.stdin.fileno()
        termios.tcsetattr(fd, termios.TCSAFLUSH, self.oldterm)
        fcntl.fcntl(fd, fcntl.F_SETFL, self.oldflags)
  
  def toggleConsole( self ):
    self.consoleFrame.toggleVis()
    self.consoleEntry['focus'] != self.consoleFrame.isHidden
  
  def setText(self, textEntered):
    self.write( ">%s" % textEntered )
    output = self.push( textEntered )
    # clear line
    self.consoleEntry.enterText('')
    self.consoleEntry['focus'] = 1
  
  def writeP3dConsole( self, printString ):
    # remove carriage returns (causes disort in output)
    printString = printString.strip()
    # remove not printable characters (which can be input by console input)
    printString = re.sub( r"[^%s]" % string.printable, "", printString )
    # only write lines which contain something
    if printString.strip() != '':
      while len(printString) > LINELENGTH:
        writeString = printString[0:LINELENGTH]
        printString = printString[LINELENGTH:]
        # output on panda3d interface
        for i in xrange( NUMLINES-1 ):
          self.consoleOutputList[i].setText( self.consoleOutputList[i+1].getText() )
        self.consoleOutputList[NUMLINES-1].setText( writeString )
      # output on panda3d interface
      for i in xrange( NUMLINES-1 ):
        self.consoleOutputList[i].setText( self.consoleOutputList[i+1].getText() )
      self.consoleOutputList[NUMLINES-1].setText( printString )
  
  def write( self, string ):
    # write a text the console
    try:
      if self.consoleEnabled:
        # output on console
        self.stdout.write( string )
Ejemplo n.º 3
0
class GuiConsole(DirectObject.DirectObject):
    def __init__(self, class_parent, parent, h_size, v_size, aspect, hugpos):
        self.h_size = h_size
        self.v_size = v_size
        self.scale = 0.04
        self.parent = class_parent
        self.numlines = int(v_size / self.scale - 2)

        self.pos_min_x = 0
        self.pos_min_y = 0
        self.pos_max_x = self.h_size
        self.pos_max_y = 0.7

        if aspect > 0:
            self.pos_max_x /= aspect
        else:
            self.pos_max_y *= aspect

        self.consoleFrame = DirectFrame(relief=DGG.RIDGE,
                                        frameColor=(0, 0, 0, 0),
                                        scale=self.scale,
                                        frameSize=(0, self.h_size / self.scale,
                                                   0,
                                                   self.v_size / self.scale))

        if parent == base.a2dBottomLeft:  #@UndefinedVariable
            self.pos_min_x -= 1
            self.pos_min_y -= 1
            self.pos_max_x -= 1
            self.pos_max_y -= 1

        if hugpos == "bottom":
            self.consoleFrame.setPos(0, 0, GUI_BOTTOM_OFFSET - 0.085)
            self.pos_min_x = 0
            self.pos_min_y = GUI_BOTTOM_OFFSET - 0.085 - 0.07
            self.pos_max_x = self.h_size
            self.pos_max_y = GUI_BOTTOM_OFFSET - 0.085

        fixedWidthFont = loader.loadFont(GUI_FONT)  #@UndefinedVariable
        #fixedWidthFont.setPixelsPerUnit(60)
        #fixedWidthFont.setRenderMode(fixedWidthFont.RMSolid)
        if not fixedWidthFont.isValid():
            print "pandaInteractiveConsole.py :: could not load the defined font %s" % str(
                self.font)
            fixedWidthFont = DGG.getDefaultFont()

        #fixedWidthFont.setPageSize(512,512)
        #fixedWidthFont.setPixelsPerUnit(60)

        self.consoleEntry = DirectEntry(
            self.consoleFrame,
            text="",
            command=self.onEnterPress
            #, width       = self.h_size/self.scale -2
            ,
            pos=(0.01, 0, 0.02),
            initialText="Enter text...",
            numLines=1,
            focus=0,
            entryFont=fixedWidthFont,
            scale=1,
            frameColor=(0, 0, 0, 0.2),
            text_fg=(0, 1, 0, 1),
            text_shadow=(0, 0, 0, 1))

        #self.consoleEntry = DirectEntry(self.consoleFrame)

        self.consoleEntry["frameSize"] = (0, self.h_size / self.scale, 0, 1)
        self.consoleEntry["width"] = self.h_size / self.scale
        self.consoleEntry["focusInCommand"] = self.focusInCallback
        self.consoleEntry["focusOutCommand"] = self.focusOutCallback

        self.consoleFrame.reparentTo(parent)

        self.textBuffer = list()
        self.textBufferLength = 100
        for i in xrange(self.textBufferLength):
            self.textBuffer.append(['', (100, 100, 100, 1)])
        self.textBufferPos = self.textBufferLength - self.numlines

        # output lines
        self.consoleOutputList = list()
        for i in xrange(self.numlines):
            label = OnscreenText(parent=self.consoleFrame,
                                 text="",
                                 pos=(0, i + 1.5),
                                 align=TextNode.ALeft,
                                 mayChange=1,
                                 scale=1.0,
                                 fg=(100, 100, 100, 1),
                                 shadow=(0, 0, 0, 1))
            # , frame = (200,0,0,1) )
            label.setFont(fixedWidthFont)
            self.consoleOutputList.append(label)

        self.linelength = 57
        self.linewrap = textwrap.TextWrapper()
        self.linewrap.width = self.linelength
        self.toggleConsole()

    def focusInCallback(self):
        self.parent.parent.camera_manager.disableKeyMovement()

    def focusOutCallback(self):
        self.parent.parent.camera_manager.enableKeyMovement()

    def show(self):
        if self.consoleFrame.isHidden():
            self.consoleFrame.toggleVis()

    def hide(self):
        pass
        #if not self.consoleFrame.isHidden():
        #    self.consoleFrame.toggleVis()

    def toggleConsole(self):
        self.consoleFrame.toggleVis()
        hidden = self.consoleFrame.isHidden()
        #self.consoleEntry['focus'] != hidden
        if hidden:
            #self.ignoreAll()
            self.accept('control', self.toggleConsole)
            self.accept('enter', self.manageFocus)
            self.accept('escape', self.unfocus)
        else:
            #self.ignoreAll()
            #self.accept( 'page_up', self.scroll, [-5] )
            #self.accept( 'page_up-repeat', self.scroll, [-5] )
            #self.accept( 'page_down', self.scroll, [5] )
            #self.accept( 'page_down-repeat', self.scroll, [5] )
            #self.accept( 'window-event', self.windowEvent)

            #self.accept( 'arrow_up'  , self.scrollCmd, [ 1] )
            #self.accept( 'arrow_down', self.scrollCmd, [-1] )

            self.accept('control', self.toggleConsole)
            self.accept('enter', self.manageFocus)
            self.accept('escape', self.unfocus)
            #self.accept( self.autocomplete_key, self.autocomplete )
            #self.accept( self.autohelp_key, self.autohelp )

            # accept v, c and x, where c & x copy's the whole console text
            #messenger.toggleVerbose()
            #for osx use ('meta')
            #if sys.platform == 'darwin':
            #  self.accept( 'meta', self.unfocus )
            #  self.accept( 'meta-up', self.focus )
            #  self.accept( 'meta-c', self.copy )
            #  self.accept( 'meta-x', self.cut )
            #  self.accept( 'meta-v', self.paste )
            #for windows use ('control')
            #if sys.platform == 'win32' or sys.platform == 'linux2':
            #  self.accept( 'control', self.unfocus )
            #  self.accept( 'control-up', self.focus )
            #  self.accept( 'control-c', self.copy )
            #  self.accept( 'control-x', self.cut )
            #  self.accept( 'control-v', self.paste )

    def onEnterPress(self, textEntered):
        # set to last message
        self.textBufferPos = self.textBufferLength - self.numlines
        # clear line
        self.consoleEntry.enterText('')
        self.consoleOutput(textEntered, utils.CONSOLE_PLAYER1_TEXT)
        ClientMsg.chat(textEntered)
        self.focus()

    def manageFocus(self):
        if self.consoleFrame.isHidden():
            self.consoleFrame.toggleVis()

        if self.consoleEntry["focus"] == 0:
            self.focus()

    def consoleOutput(self, printString, msgType):
        if msgType == utils.CONSOLE_SYSTEM_ERROR:
            self.write(printString, utils.CONSOLE_SYSTEM_ERROR_TEXT_COLOR)
        elif msgType == utils.CONSOLE_SYSTEM_MESSAGE:
            self.write(printString, utils.CONSOLE_SYSTEM_MESSAGE_TEXT_COLOR)
        elif msgType == utils.CONSOLE_PLAYER1_TEXT:
            self.write(printString, utils.CONSOLE_PLAYER1_TEXT_COLOR)
        else:
            self.write(printString, utils.CONSOLE_PLAYER2_TEXT_COLOR)

    def write(self, printString, color=(100, 100, 100, 0.5)):
        # remove not printable characters (which can be input by console input)
        printString = re.sub(r'[^%s]' % re.escape(string.printable[:95]), "",
                             printString)

        splitLines = self.linewrap.wrap(printString)
        for line in splitLines:
            self.textBuffer.append([line, color])
            self.textBuffer.pop(0)

        self.updateOutput()

    def updateOutput(self):
        for lineNumber in xrange(self.numlines):
            lineText, color = self.textBuffer[lineNumber + self.textBufferPos]
            self.consoleOutputList[self.numlines - lineNumber -
                                   1].setText(lineText)
            self.consoleOutputList[self.numlines - lineNumber -
                                   1]['fg'] = color

    def focus(self):
        self.consoleEntry['focus'] = 1

    def unfocus(self):
        self.consoleEntry['focus'] = 0

    def getTightBounds(self):
        l, r, b, t = self.consoleFrame.getBounds()
        print l, r, b, t
        bottom_left = Point3(l, 0, b)
        top_right = Point3(r, 0, t)
        return (bottom_left, top_right)
Ejemplo n.º 4
0
class panda3dIOClass(DirectObject.DirectObject):
    # set gui key to None if you want to call toggleConsole from outside this class
    gui_key = PANDA3D_CONSOLE_TOGGLE_KEY
    autocomplete_key = PANDA3D_CONSOLE_AUTOCOMPLETE_KEY
    autohelp_key = PANDA3d_CONSOLE_AUTOHELP_KEY
    # change size of text and number of characters on one line
    # scale of frame ( must be small (0.0x)
    scale = PANDA3D_CONSOLE_SCALE
    # to define a special font, if loading fails the default font is used (without warning)
    font = PANDA3D_CONSOLE_FONT
    fontWidth = PANDA3D_CONSOLE_FONT_WIDTH
    # frame position and size (vertical & horizontal)
    h_pos = PANDA3D_CONSOLE_HORIZONTAL_POS
    h_size = PANDA3D_CONSOLE_HORIZONTAL_SIZE
    # v_size + v_pos should not exceed 2.0, else parts of the interface will not be visible
    # space above the frame ( must be below 2.0, best between 0.0 and 1.0 )
    v_pos = PANDA3D_CONSOLE_VERTICAL_POS
    # vertical size of the frame ( must be at max 2.0, best between 0.5 and 2.0 )
    v_size = PANDA3D_CONSOLE_VERTICAL_SIZE
    linelength = int((h_size / scale - 5) / fontWidth)
    print "max number of characters on a length:", linelength
    numlines = int(v_size / scale - 5)
    defaultTextColor = (0.0, 0.0, 0.0, 1.0)
    autoCompleteColor = (0.9, 0.9, 0.9, 1.0)

    def __init__(self, parent):
        self.parent = parent

        # line wrapper
        self.linewrap = textwrap.TextWrapper()
        self.linewrap.width = self.linelength

        # calculate window size
        left = (self.h_pos) / self.scale
        right = (self.h_pos + self.h_size) / self.scale
        bottom = (self.v_pos) / self.scale
        top = (self.v_pos + self.v_size) / self.scale

        # panda3d interface
        self.consoleFrame = DirectFrame(
            relief=DGG.GROOVE,
            frameColor=(200, 200, 200, 0.5),
            scale=self.scale,
            frameSize=(0, self.h_size / self.scale, 0, self.v_size / self.scale),
        )
        self.windowEvent(base.win)

        # try to load the defined font
        fixedWidthFont = loader.loadFont(self.font)
        # if font is not valid use default font
        if not fixedWidthFont.isValid():
            if self.font is None:
                print "pandaInteractiveConsole.py :: could not load the defined font %s" % str(self.font)
            fixedWidthFont = DGG.getDefaultFont()

        # text entry line
        self.consoleEntry = DirectEntry(
            self.consoleFrame,
            text="",
            command=self.onEnterPress,
            width=self.h_size / self.scale - 2,
            pos=(1, 0, 1.5),
            initialText="",
            numLines=1,
            focus=1,
            entryFont=fixedWidthFont,
        )

        # output lines
        self.consoleOutputList = list()
        for i in xrange(self.numlines):
            label = OnscreenText(
                parent=self.consoleFrame,
                text="",
                pos=(1, -i + 3 + self.numlines),
                align=TextNode.ALeft,
                mayChange=1,
                scale=1.0,
                fg=self.defaultTextColor,
            )
            label.setFont(fixedWidthFont)
            self.consoleOutputList.append(label)

        # list of the last commands of the user
        self.userCommandList = list()
        self.userCommandListLength = 100
        for i in xrange(self.userCommandListLength):
            self.userCommandList.append("")
        self.userCommandPos = 0

        # buffer for data
        self.textBuffer = list()
        self.textBufferLength = 1000
        for i in xrange(self.textBufferLength):
            self.textBuffer.append(["", DEFAULT_COLOR])
        self.textBufferPos = self.textBufferLength - self.numlines

        # toggle the window at least once to activate the events
        self.toggleConsole()
        self.toggleConsole()

        self.help()

    def help(self):
        # output some info text about this module
        infoTxt = """ ------ Panda3dConsole ------
- press F10 to toggle it on/off
- use the usual copy, cut & paste keys
- page_up    : scrolls up
- page_down  : scrolls down
- arrow_up   : previous command
- arrow_down : next command
- BUGS       : if you paste a to long text, the entry blocks
         FIX : use cut to remove the whole line"""
        # for line in infoTxt.split('\n'):
        #  self.write( line, color=DEFAULT_COLOR )
        return infoTxt

    # write a string to the panda3d console
    def write(self, printString, color=defaultTextColor):
        # remove not printable characters (which can be input by console input)
        printString = re.sub(r"[^%s]" % re.escape(string.printable[:95]), "", printString)

        splitLines = self.linewrap.wrap(printString)
        for line in splitLines:
            self.textBuffer.append([line, color])
            self.textBuffer.pop(0)

        self.updateOutput()

    def updateOutput(self):
        for lineNumber in xrange(self.numlines):
            lineText, color = self.textBuffer[lineNumber + self.textBufferPos]
            self.consoleOutputList[lineNumber].setText(lineText)
            self.consoleOutputList[lineNumber]["fg"] = color

    # toggle the gui console
    def toggleConsole(self):
        self.consoleFrame.toggleVis()
        hidden = self.consoleFrame.isHidden()
        self.consoleEntry["focus"] != hidden
        if hidden:
            self.ignoreAll()
            self.accept(self.gui_key, self.toggleConsole)
        else:
            self.ignoreAll()
            self.accept("page_up", self.scroll, [-5])
            self.accept("page_up-repeat", self.scroll, [-5])
            self.accept("page_down", self.scroll, [5])
            self.accept("page_down-repeat", self.scroll, [5])
            self.accept("window-event", self.windowEvent)

            self.accept("arrow_up", self.scrollCmd, [1])
            self.accept("arrow_down", self.scrollCmd, [-1])

            self.accept(self.gui_key, self.toggleConsole)
            self.accept(self.autocomplete_key, self.autocomplete)
            self.accept(self.autohelp_key, self.autohelp)

            # accept v, c and x, where c & x copy's the whole console text
            # messenger.toggleVerbose()
            # for osx use ('meta')
            if sys.platform == "darwin":
                self.accept("meta", self.unfocus)
                self.accept("meta-up", self.focus)
                self.accept("meta-c", self.copy)
                self.accept("meta-x", self.cut)
                self.accept("meta-v", self.paste)
            # for windows use ('control')
            if sys.platform == "win32" or sys.platform == "linux2":
                self.accept("control", self.unfocus)
                self.accept("control-up", self.focus)
                self.accept("control-c", self.copy)
                self.accept("control-x", self.cut)
                self.accept("control-v", self.paste)

    def autocomplete(self):
        currentText = self.consoleEntry.get()
        currentPos = self.consoleEntry.guiItem.getCursorPosition()
        newText = self.parent.autocomplete(currentText, currentPos)
        if newText != currentText:
            self.consoleEntry.set(newText)
            self.consoleEntry.setCursorPosition(len(newText))

    def autohelp(self):
        currentText = self.consoleEntry.get()
        currentPos = self.consoleEntry.guiItem.getCursorPosition()
        self.parent.autohelp(currentText, currentPos)

    def focus(self):
        self.consoleEntry["focus"] = 1

    def unfocus(self):
        self.consoleEntry["focus"] = 0

    def copy(self):
        copy = self.consoleEntry.get()
        clipboard.setText(copy)

    def paste(self):
        oldCursorPos = self.consoleEntry.guiItem.getCursorPosition()
        clipboardText = clipboard.getText()

        # compose new text line
        oldText = self.consoleEntry.get()
        newText = oldText[0:oldCursorPos] + clipboardText + oldText[oldCursorPos:]

        clipboardTextLines = newText.split(os.linesep)

        for i in xrange(len(clipboardTextLines) - 1):
            currentLine = clipboardTextLines[i]
            # we only want printable characters
            currentLine = re.sub(r"[^" + re.escape(string.printable[:95]) + "]", "", currentLine)

            # set new text and position
            self.consoleEntry.set(currentLine)
            self.onEnterPress(currentLine)
        currentLine = clipboardTextLines[-1]
        currentLine = re.sub(r"[^" + re.escape(string.printable[:95]) + "]", "", currentLine)
        self.consoleEntry.set(currentLine)
        self.consoleEntry.setCursorPosition(len(self.consoleEntry.get()))
        self.focus()

    def cut(self):
        clipboard.setText(self.consoleEntry.get())
        self.consoleEntry.enterText("")
        self.focus()

    def scroll(self, step):
        self.textBufferPos += step
        self.textBufferPos = min(self.textBufferLength - self.numlines, max(0, self.textBufferPos))
        self.updateOutput()

    def scrollCmd(self, step):
        oldCmdPos = self.userCommandPos
        self.userCommandPos += step
        self.userCommandPos = min(self.userCommandListLength - 1, max(0, self.userCommandPos))
        self.userCommandList[oldCmdPos] = self.consoleEntry.get()
        newCmd = self.userCommandList[self.userCommandPos]
        self.consoleEntry.set(newCmd)
        self.consoleEntry.setCursorPosition(len(newCmd))

    def onEnterPress(self, textEntered):
        # set to last message
        self.textBufferPos = self.textBufferLength - self.numlines
        # clear line
        self.consoleEntry.enterText("")
        self.focus()
        # add text entered to user command list & remove oldest entry
        self.userCommandList.insert(1, textEntered)
        self.userCommandList[0] = ""
        self.userCommandList.pop(-1)
        self.userCommandPos = 0
        # call our parent
        self.parent.push(textEntered)

    def windowEvent(self, window):
        """
    This is a special callback.
    It is called when the panda window is modified.
    """
        wp = window.getProperties()
        width = wp.getXSize() / float(wp.getYSize())
        height = wp.getYSize() / float(wp.getXSize())
        if width > height:
            height = 1.0
        else:
            width = 1.0
        # aligned to center
        consolePos = Vec3(-self.h_size / 2, 0, -self.v_size / 2)
        # aligned to left bottom
        # consolePos = Vec3(-width+self.h_pos, 0, -height+self.v_pos)
        # aligned to right top
        # consolePos = Vec3(width-self.h_size, 0, height-self.v_size)
        # set position
        self.consoleFrame.setPos(consolePos)