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 )
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 )
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)
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)