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
class GagSelectionGui(DirectFrame, FSM):
    InactivityTime = 5.0

    AmmoZSelect = -0.15
    AmmoZIdle = 0.035

    def __init__(self):
        DirectFrame.__init__(self,
                             parent=aspect2d,
                             pos=(0, 0, 0.93),
                             scale=0.7)
        FSM.__init__(self, 'GagSelectionGui')
        self.setTransparency(TransparencyAttrib.MDual)
        self.tracks = []
        self.currentTrack = 0
        self.currentGag = None
        self.fwdShakeIval = None
        self.revShakeIval = None
        self.newTrackSound = None
        self.keyScrollSound = None
        self.selectSound = None
        self.selectDenySound = None
        self.lastActivityTime = 0.0
        self.activityTask = None
        self.midpoint = 0.0

        self.ammoFrame = DirectFrame(parent=self,
                                     pos=(0, 0, -0.2),
                                     image='phase_14/maps/status_bar.png',
                                     image_scale=(0.461 * 0.7, 0, 0.098),
                                     relief=None)
        self.ammoFrame.hide()
        self.ammoTitle = OnscreenText(parent=self.ammoFrame,
                                      text='SUPPLY',
                                      fg=(0, 0, 0, 0.65),
                                      align=TextNode.ALeft,
                                      pos=(-0.37 * 0.7, -0.015, 0))
        self.ammoText = OnscreenText(parent=self.ammoFrame,
                                     text='',
                                     fg=(1, 1, 1, 1),
                                     shadow=(0, 0, 0, 1),
                                     align=TextNode.ARight,
                                     pos=(0.37 * 0.7, -0.015, 0))

    def update(self):
        plyr = base.localAvatar

        if not base.localAvatar.hasAttacks():
            return

        gagId = -1
        if self.getCurrentOrNextState() == 'Idle':
            gagId = plyr.getEquippedAttack()
        elif self.getCurrentOrNextState() == 'Select' and self.currentGag:
            gagId = self.currentGag.gagId

        if gagId != -1:
            self.ammoFrame.showThrough()
            if plyr.hasAttackId(gagId):
                self.ammoText.setText(
                    '%i/%i' %
                    (plyr.getAttackAmmo(gagId), plyr.getAttackMaxAmmo(gagId)))
            else:
                self.ammoText.setText('')
            col = GagGlobals.TrackColorByName[GagGlobals.getTrackOfGag(gagId)]
            self.ammoFrame['image_color'] = (col[0], col[1], col[2], 1.0)
        else:
            self.ammoFrame.hide()

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def hide(self):
        showAmmo = False
        if not self.ammoFrame.isHidden():
            showAmmo = True
        DirectFrame.hide(self)
        if showAmmo:
            self.ammoFrame.showThrough()

    def enterSelect(self):
        base.localAvatar.disableGagKeys()
        self.ammoFrame.setZ(self.AmmoZSelect)
        self.show()
        self.update()
        self.acceptSelectionClick()
        self.resetTimeout()
        self.activityTask = taskMgr.add(self.__activityTask, "activityTask")

    def acceptSelectionClick(self):
        self.accept('mouse1-up', self.selectCurrentGag)

    def ignoreSelectionClick(self):
        self.ignore('mouse1-up')

    def resetTimeout(self):
        self.lastActivityTime = globalClock.getFrameTime()

    def __activityTask(self, task):
        time = globalClock.getFrameTime()
        if time - self.lastActivityTime >= self.InactivityTime:
            self.request('Idle')
            return task.done
        return task.cont

    def exitSelect(self):
        self.activityTask.remove()
        self.activityTask = None
        self.hide()
        self.ignoreSelectionClick()

    def enterIdle(self):
        self.ammoFrame.setZ(self.AmmoZIdle)
        self.hide()
        self.update()
        if base.localAvatar.avatarMovementEnabled:
            base.localAvatar.enableGagKeys()

    def exitIdle(self):
        pass

    def disable(self):
        self.disableControls()
        self.hide()

    def enable(self):
        self.enableControls()
        self.show()

    def cleanup(self):
        self.request('Off')

        self.disableControls()

        self.newTrackSound = None
        self.keyScrollSound = None
        self.selectSound = None
        self.selectDenySound = None

        if self.fwdShakeIval:
            self.fwdShakeIval.finish()
            self.fwdShakeIval = None
        if self.revShakeIval:
            self.revShakeIval.finish()
            self.revShakeIval = None
        self.currentTrack = None
        self.currentGag = None
        if self.tracks:
            for track in self.tracks:
                track.cleanup()
        self.tracks = None

        self.destroy()

    def __accumulateTracks(self):
        tracks = []
        for gagId in base.localAvatar.attacks.keys():
            trackId = GagGlobals.getTrackOfGag(gagId, getId=True)
            if trackId not in tracks:
                tracks.append(trackId)
        tracks.sort()
        return tracks

    def load(self):
        tracks = self.__accumulateTracks()
        for i in xrange(len(tracks)):
            track = GagTrack(self, tracks[i])
            track.load()
            track.reparentTo(self)
            track.setX(FRAME_OFFSET * i)
            self.tracks.append(track)

        self.midpoint = (len(self.tracks) / 2.0) * -FRAME_OFFSET
        # Center the gui horizontally
        self.setX(self.midpoint)
        self.ammoFrame.setX(-self.midpoint)

        if base.config.GetBool('gsg-want-hlsounds', False):
            self.newTrackSound = base.loadSfx(
                "phase_14/audio/sfx/wpn_hudon.ogg")
            self.keyScrollSound = base.loadSfx(
                'phase_14/audio/sfx/wpn_moveselect.ogg')
            self.selectSound = base.loadSfx(
                'phase_14/audio/sfx/wpn_select.ogg')
            self.selectDenySound = base.loadSfx(
                'phase_14/audio/sfx/wpn_denyselect.ogg')
        else:
            self.newTrackSound = base.loadSfx(
                "phase_3/audio/sfx/GUI_create_toon_back.ogg")
            self.keyScrollSound = base.loadSfx(
                'phase_3/audio/sfx/GUI_rollover.ogg')
            self.selectSound = base.loadSfx(
                'phase_3/audio/sfx/GUI_create_toon_fwd.ogg')
            self.selectDenySound = base.loadSfx(
                'phase_4/audio/sfx/ring_miss.ogg')

        self.fwdShakeIval = Effects.createXBounce(self, 1,
                                                  Vec3(self.midpoint, 0, 0.93),
                                                  0.05, 0.05)
        self.revShakeIval = Effects.createXBounce(self, 1,
                                                  Vec3(self.midpoint, 0, 0.93),
                                                  0.05, -0.05)

        if base.localAvatar.hasAttacks():
            self.updateCurrentTrack(0)

    def enableControls(self):
        self.accept('wheel_up', self.__handleScrollUp)
        self.accept('wheel_down', self.__handleScrollDown)

        for i in xrange(len(self.tracks)):
            self.accept(str(i + 1), self.__handleTrackChoose, [i])

        self.request('Idle')

    def selectCurrentGag(self):
        selected = False

        self.newTrackSound.stop()
        self.keyScrollSound.stop()

        if self.currentGag is not None:
            if base.localAvatar.getEquippedAttack() == self.currentGag.gagId:
                selected = True
            elif (not self.currentGag.locked and self.currentGag.hasAmmo()):
                gagId = self.currentGag.gagId
                base.localAvatar.needsToSwitchToGag = gagId
                if base.localAvatar.gagsTimedOut == False:
                    base.localAvatar.selectGag(gagId)
                    selected = True

        if not selected:
            # Denied!
            self.selectDenySound.play()
            self.resetTimeout()
        else:
            self.selectSound.play()
            self.request('Idle')

    def disableControls(self):
        self.ignore('wheel_up')
        self.ignore('wheel_down')

        for i in xrange(len(self.tracks)):
            self.ignore(str(i + 1))

        self.request('Idle')

    def __maybeDoSelect(self):
        if self.getCurrentOrNextState() == 'Idle':
            self.request('Select')

    def __handleTrackChoose(self, idx):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        if self.currentTrack == idx:
            # Scroll through the current track.
            self.tracks[self.currentTrack].selectNextGag()

            self.newTrackSound.stop()
            self.keyScrollSound.play()
        else:
            # Always start from the beginning when using the keys to choose a track.
            self.updateCurrentTrack(idx, 0)
            self.newTrackSound.play()

    def __handleScrollUp(self):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        track = self.tracks[self.currentTrack]
        if track.isOnFirstGag():
            self.prevTrack()
        else:
            track.selectPrevGag()

        self.newTrackSound.stop()
        self.keyScrollSound.play()

    def __handleScrollDown(self):
        if not base.localAvatar.hasAttacks():
            return

        self.__maybeDoSelect()
        self.resetTimeout()

        track = self.tracks[self.currentTrack]
        if track.isOnLastGag():
            self.nextTrack()
        else:
            track.selectNextGag()

        self.newTrackSound.stop()
        self.keyScrollSound.play()

    def nextTrack(self):
        newIdx = self.currentTrack + 1
        if newIdx > len(self.tracks) - 1:
            newIdx = 0

        self.updateCurrentTrack(newIdx)

    def prevTrack(self):
        newIdx = self.currentTrack - 1
        if newIdx < 0:
            newIdx = len(self.tracks) - 1

        self.updateCurrentTrack(newIdx)

    def updateCurrentTrack(self, idx, startLoc=None):
        oldTrack = self.tracks[self.currentTrack]
        oldTrack.deselectAll()
        oldTrack.stashContents()

        if idx - self.currentTrack < 0:
            direction = 1
        else:
            direction = 0

        if startLoc is None:
            startLoc = direction

        if direction == 0:
            self.fwdShakeIval.start()
        else:
            self.revShakeIval.start()

        self.currentTrack = idx

        # Resort the tracks
        numTracks = len(self.tracks)
        maxTrack = numTracks - 1
        for i in xrange(len(self.tracks)):
            track = self.tracks[i]

            if i == idx:
                sort = FRAME_FRONT_SORT
            elif i > idx:
                sort = FRAME_SORT_BEGIN + (maxTrack - i) * FRAME_SORT_DISTANCE
            elif i < idx:
                sort = FRAME_SORT_BEGIN + (i * FRAME_SORT_DISTANCE)
            track.setBin('gsg-popup', sort)

            if i == idx:
                track.unstashContents()
                if startLoc == 0:
                    track.selectFirstGag()
                else:
                    track.selectLastGag()
            else:
                track.stashContents()
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 GuiFrame(DirectObject, HasKeybinds):
    #should be able to show/hide, do conditional show hide
    #position where you want
    #parent to other frames
    TEXT_MAGIC_NUMBER = .833333333334  #5/6 ?!?
    DRAW_ORDER = {
        'frame': ('unsorted', 0),
        'frame_bg': ('unsorted', 0),
        'items': ('unsorted', 0),
        'title': ('unsorted', 0),
        'border': ('unsorted', 0),
    }

    def __init__(
            self,
            title,
            shortcut=None,  # XXX obsolete, but needs a non deco replacement
            x=0,
            y=.1,
            width=.2,
            height=.8,
            #scale = .05,  # there is some black magic here :/
            bdr_thickness=2,
            bdr_color=(.1, .1, .1, 1),
            bg_color=(.7, .7, .7, .5),
            text_color=(0, 0, 0, 1),
            text_font=TextNode.getDefaultFont(),
            #text_h = .05,  # do not use directly
            text_height_mm=4,
            items=tuple(),
    ):
        #item_w_pad = 1
        #item_h_pad = 1

        self.title = title
        self.do_xywh(x, y, width, height)
        self.bdr_thickness = bdr_thickness  # FIXME ??
        self.bdr_color = bdr_color
        self.bg_color = bg_color
        self.text_color = text_color
        self.text_font = text_font
        self.text_height_mm = text_height_mm

        #set up variables
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        self.__ar__ = base.camLens.getAspectRatio()
        self.__was_dragging__ = False
        self.__first_item__ = None
        self.__add_head__ = None
        self.items = OrderedDict()  # ordered dict to allow sequential addition

        #self.BT = buttonThrower if buttonThrower else base.buttonThrowers[0].node()
        self.BT = base.buttonThrowers[0].node()

        # get our aspect ratio, and pixels per mm
        self.pixels_per_mm = render.getPythonTag('system_data')['max_ppmm']
        self.getWindowData()
        self.accept('window-event', self.getWindowData)

        #set the text height using the above data
        self.setTextHeight()

        # get the root for all frames in the scene
        self.frameRoot = aspect2d.find('frameRoot')
        if not self.frameRoot:
            self.frameRoot = aspect2d.attachNewNode('frameRoot')

        # create the parent node for this frame
        #parent = self.frameRoot.find('frame-*')
        #if not parent:
        #parent = self.frameRoot
        self.frame = self.frameRoot.attachNewNode('frame-%s-%s' %
                                                  (title, id(self)))
        self.frame.setBin(*self.DRAW_ORDER['frame'])

        # background
        l, r, b, t = 0, self.width, 0, self.height
        self.frame_bg = DirectFrame(
            parent=self.frame,
            frameColor=self.bg_color,
            pos=LVecBase3f(self.x, 0, self.y),
            frameSize=(l, r, b, t),
            state=DGG.NORMAL,  # FIXME framesize is >_<
            suppressMouse=1)
        self.frame_bg.setBin(*self.DRAW_ORDER['frame_bg'])

        # border
        self.__make_border__(self.frame_bg, self.bdr_thickness, self.bdr_color,
                             l, r, b, t)

        # setup for items
        self.itemsParent = self.frame_bg.attachNewNode('items parent')

        # title
        self.title_button = self.__create_item__(title, self.title_toggle_vis)

        # add any items that we got
        for item in items:
            self.__create_item__(
                *item
            )  # FIXME when we call frame adjust we will loose the record of any data items

        # dragging
        self.title_button.bind(DGG.B1PRESS, self.__startDrag)
        self.title_button.bind(DGG.B1RELEASE, self.__stopDrag)

        # raise if we click the frame background
        self.frame_bg.bind(DGG.B1PRESS, self.raise_)
        #self.frame_bg.bind(DGG.B1RELEASE, self.__stopDrag)  # this can cause problems w/ was dragging

        # toggle vis
        if shortcut:
            self.accept(shortcut, self.toggle_vis)

        # adjust the frame
        self.frame_adjust()

    @property
    def text_s(self):
        return self.text_h * self.TEXT_MAGIC_NUMBER

    def setTextHeight(self):
        h_units = 2 * base.a2dTop
        units_per_pixel = h_units / self.__winy__
        text_h = self.text_height_mm * self.pixels_per_mm * units_per_pixel
        self.text_h = text_h

    def do_xywh(self, x, y, w, h):
        """ makes negative wneg xidths and heights work
            as well as negative x and y (bottom right is 0)
        """
        if x < 0:
            x = 1 + x
        if y < 0:
            y = 1 + y
        if w < 0:
            x, w = x + w, -w
        if h < 0:
            y, h = y + h, -h

        self.x = self.fix_x(x)  # for top left
        self.y = self.fix_y(y)  # for top left
        self.width = self.fix_w(w)
        self.height = self.fix_h(h)

    def getWindowData(self, window=None):
        x = base.win.getXSize()
        y = base.win.getYSize()
        if x != self.__winx__ or y != self.__winy__:
            self.__ar__ = base.camLens.getAspectRatio()  # w/h
            self.__winx__ = x
            self.__winy__ = y
            self.frame_adjust()

    def raise_(self, *args):
        """ function that raises windows
            call FIRST inside any function that should raise
        """
        self.frame.reparentTo(
            self.frameRoot)  # self.frame doesn't move so no wrt

    def frame_adjust(
        self
    ):  # FIXME sometimes this fails to call, also calls too often at startup
        self.setTextHeight()
        MI = self.getMaxItems()  # does not count title >_<
        LI = len(self.items)
        DI = MI - LI
        if DI >= 0:
            for i in range(DI + 1):
                self.__create_item__(' blank')
        else:
            for i in range(-(DI + 1)):
                k, v = self.items.popitem()  # remove the last nodes in order
                v.removeNode()  # FIXME consider keeping these around?

        for k, b in self.items.items():
            if k == 'title':
                if self.frame_bg.isHidden():
                    x, y, z = self.frame_bg.getPos()
                    self.title_button.setPos(LVecBase3f(x, y, z - self.text_h))
                else:
                    self.title_button.setPos(LVecBase3f(0, 0, -self.text_h))
            elif k == self.__first_item__:
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
            else:
                b.setPos(LVecBase3f(0, 0, -self.text_h))
            b['frameSize'] = 0, self.width, 0, self.text_h
            b['text_scale'] = self.text_s, self.text_s
            b['text_pos'] = 0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s

    def getWindowSize(self, event=None):  # TODO see if we really need this
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        m = max(self.__winx__, self.__winy__)
        self.__xscale__ = self.__winx__ / m
        self.__yscale__ = self.__winy__ / m

    # put origin in top left and positive down and right
    @staticmethod
    def fix_x(x):
        return (x - .5) * 2  # TODO * base.a2dLeft?

    @staticmethod
    def fix_y(y):
        return (y - .5) * -2  # TODO * base.a2dTop?

    @staticmethod
    def fix_w(n):
        return n * 2

    @staticmethod
    def fix_h(n):
        return -n * 2

    def add_item(self, text, command=None, args=tuple()):
        args = list(args)
        if text[0] != ' ':
            text = ' ' + text
        items = list(self.items)
        last_slot = len(self.items)
        if self.__add_head__ == last_slot:
            print('all slots are full, cannot add item to %s' % self)
            return None
        button = self.items[items[self.__add_head__]]
        button['text'] = text
        button['command'] = command
        button['extraArgs'] = args + button[
            'extraArgs']  # blank buttons always have [self,id]
        self.__add_head__ += 1

    def __create_item__(self, text, command=None, args=tuple()):
        args = list(args)

        #if not len(self.items):
        #parent = self.frame
        if len(self.items) <= 1:
            parent = self.itemsParent  #everyone else parents off 2nd text
        else:
            parent = list(self.items.values())[-1]

        if command != None:

            def cmd(*args):
                """ any item should raise
                """
                self.raise_()
                command(*args)
        else:
            cmd = self.raise_

        b = DirectButton(
            parent=parent,
            frameColor=(1, 1, 1, .0),  # a = 0 => no border overlap
            frameSize=(0, self.width, 0, self.text_h),
            text=' ' + text,  # hack to keep spacing from border
            text_font=self.text_font,
            text_fg=self.text_color,
            text_scale=self.text_s,
            text_pos=(0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s),
            command=cmd,
            relief=DGG.FLAT,
            text_align=TextNode.ALeft,
        )

        b.setPos(LVecBase3f(0, 0, -self.text_h))
        b.setName('DirectButton-' + text)
        if not len(self.items):
            self.items['title'] = b
            b.setBin(*self.DRAW_ORDER['title'])
        else:
            b['extraArgs'] = args + [self, id(b)]
            b.node().setPythonTag('id', id(b))
            b.setBin(*self.DRAW_ORDER['items'])
            if len(self.items) is 1:  # the first item that is not the title
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
                self.__first_item__ = id(b)

            self.items[id(b)] = b

        if text == ' blank':
            if self.__add_head__ is None:
                self.__add_head__ = 1

        return b

    def del_all(self):
        if self.__first_item__ != None:
            for id_, button in self.items.items():
                if id_ != 'title':
                    button['text'] = ' blank'
                    button['command'] = None
                    button['extraArgs'] = [self, id_]
            self.__add_head__ = 1

    def del_item(self, text):  # FIXME uniqueness problems
        #d = self.itemsParent.find('*%s*'%text)
        if text[0] != ' ':
            text = ' ' + text
        d = [i for i in self.items.values() if i.getName().count(text)]
        try:
            self.__del_item__(d[0].getPythonTag('id'))
        except IndexError:
            print('that item does not seem to exist')
            # if we have a name then there shouldn't be key errors

    def __del_item__(self, index):
        """ I have no idea how this is going to work """
        out = self.items[index]
        p = out.getParent()
        if out.getNumChildren():  # avoid the printing of the AssertionError :/
            c = out.getChild(0)
            c.reparentTo(p)
            if index == self.__first_item__:  # XXX is fails, ints from id !=
                c.setPos(LVecBase3f(out.getPos()))
                id_ = c.getPythonTag('id')
                self.__first_item__ = id_
                out.setPos(LVecBase3f(0, 0, -self.text_h))
        self.items.pop(index)
        parent = list(self.items.values())[-1]
        out['text'] = ' del blank'
        #out['command'] = None
        out['extraArgs'] = [self, index]
        out.reparentTo(parent)
        self.items[index] = out
        if self.__add_head__ > 1:  # title is always at 0
            self.__add_head__ -= 1

    @classmethod
    def __make_border__(cls, parent, thickness, color, l, r, b, t):
        moveto_drawto = (
            ((l, 0, t), (l, 0, b)),
            ((r, 0, t), (r, 0, b)),
            ((l, 0, b), (r, 0, b)),
            ((l, 0, t), (r, 0, t)),
        )
        for moveto, drawto in moveto_drawto:
            Border = LineSegs()
            Border.setThickness(thickness)
            Border.setColor(*color)
            Border.moveTo(*moveto)
            Border.drawTo(*drawto)
            b = parent.attachNewNode(Border.create())
            b.setBin(*cls.DRAW_ORDER['border'])

    def getMaxItems(self):
        return int(abs(self.height / self.text_h) - 1)

    @event_callback
    def toggle_vis(self):
        if self.frame_bg.isHidden():
            self.frame_bg.show()
            self.raise_()
        else:
            self.frame_bg.hide()

    def title_toggle_vis(self):
        if not self.__was_dragging__:
            self.toggle_vis()
            if self.frame_bg.isHidden():
                self.title_button.wrtReparentTo(self.frame)
                self.title_button['frameColor'] = (1, 1, 1, .5)  # TODO
            else:
                self.title_button.wrtReparentTo(self.frame_bg)
                self.title_button['frameColor'] = (1, 1, 1, 0)  # TODO
        else:
            self.__was_dragging__ = False

    def __startDrag(self, crap):
        self.raise_()
        self._ox, self._oy = base.mouseWatcherNode.getMouse()
        taskMgr.add(self.__drag, 'dragging %s' % self.title)
        self.origBTprefix = self.BT.getPrefix()
        self.BT.setPrefix('dragging frame')

    def __drag(self, task):
        if base.mouseWatcherNode.hasMouse():
            x, y = base.mouseWatcherNode.getMouse()
            if x != self._ox or y != self._oy:
                m_old = aspect2d.getRelativePoint(
                    render2d, Point3(self._ox, self._oy, 0))
                m_new = aspect2d.getRelativePoint(render2d, Point3(x, y, 0))
                dx, dy, _ = m_new - m_old
                self.setPos(self.x + dx, self.y + dy)
                self._ox = x
                self._oy = y
                self.__was_dragging__ = True
        return task.cont

    def __stopDrag(self, crap):
        taskMgr.remove('dragging %s' % self.title)
        self.BT.setPrefix(self.origBTprefix)

    def setPos(self, x, y):
        """ actually sets the title button position
            since it is really the parent node
        """
        self.x = x
        self.y = y  #- self.text_h  # FIXME is hard :/
        self.frame_bg.setPos(LVecBase3f(x, 0, y))
        if self.frame_bg.isHidden():
            self.title_button.setPos(LVecBase3f(x, 0, y - self.text_h))

    def __enter__(self):
        #load the position
        #load other saved state
        pass

    def __exit__(self):
        #save the position!
        #save other state
        pass
Ejemplo n.º 5
0
Archivo: ui.py Proyecto: tgbugs/desc
class GuiFrame(DirectObject, HasKeybinds):
    #should be able to show/hide, do conditional show hide
    #position where you want
    #parent to other frames
    TEXT_MAGIC_NUMBER = .833333333334  #5/6 ?!?
    DRAW_ORDER={
        'frame':('unsorted',0),
        'frame_bg':('unsorted', 0),
        'items':('unsorted', 0),
        'title':('unsorted', 0),
        'border':('unsorted', 0),
    }

    def __init__(self, title,
                 shortcut = None,  # XXX obsolete, but needs a non deco replacement
                 x = 0,
                 y = .1,
                 width = .2,
                 height = .8,
                 #scale = .05,  # there is some black magic here :/
                 bdr_thickness = 2,
                 bdr_color = (.1, .1, .1, 1),
                 bg_color = (.7, .7, .7, .5),
                 text_color = (0, 0, 0, 1),
                 text_font = TextNode.getDefaultFont(),
                 #text_h = .05,  # do not use directly
                 text_height_mm = 4,
                 items = tuple(),
                ):
        #item_w_pad = 1
        #item_h_pad = 1

        self.title = title
        self.do_xywh(x, y, width, height)
        self.bdr_thickness = bdr_thickness  # FIXME ??
        self.bdr_color = bdr_color
        self.bg_color = bg_color
        self.text_color = text_color
        self.text_font = text_font
        self.text_height_mm = text_height_mm

        #set up variables
        self.__winx__ = base.win.getXSize() 
        self.__winy__ = base.win.getYSize()
        self.__ar__ = base.camLens.getAspectRatio()
        self.__was_dragging__ = False
        self.__first_item__ = None
        self.__add_head__ = None
        self.items = OrderedDict()  # ordered dict to allow sequential addition

        #self.BT = buttonThrower if buttonThrower else base.buttonThrowers[0].node()
        self.BT = base.buttonThrowers[0].node()

        # get our aspect ratio, and pixels per mm
        self.pixels_per_mm = render.getPythonTag('system_data')['max_ppmm']
        self.getWindowData()
        self.accept('window-event', self.getWindowData)

        #set the text height using the above data
        self.setTextHeight()

        # get the root for all frames in the scene
        self.frameRoot = aspect2d.find('frameRoot')
        if not self.frameRoot:
            self.frameRoot = aspect2d.attachNewNode('frameRoot')

        # create the parent node for this frame
        #parent = self.frameRoot.find('frame-*')
        #if not parent:
            #parent = self.frameRoot
        self.frame = self.frameRoot.attachNewNode('frame-%s-%s'%(title, id(self)))
        self.frame.setBin(*self.DRAW_ORDER['frame'])

        # background
        l,r,b,t = 0, self.width, 0, self.height
        self.frame_bg = DirectFrame(parent=self.frame,
                                    frameColor=self.bg_color,
                                    pos=LVecBase3f(self.x, 0, self.y),
                                    frameSize=(l,r,b,t),
                                    state=DGG.NORMAL,  # FIXME framesize is >_<
                                    suppressMouse=1)
        self.frame_bg.setBin(*self.DRAW_ORDER['frame_bg'])

        # border
        self.__make_border__(self.frame_bg, self.bdr_thickness, self.bdr_color, l, r, b, t)

        # setup for items
        self.itemsParent = self.frame_bg.attachNewNode('items parent')

        # title
        self.title_button = self.__create_item__(title, self.title_toggle_vis)
        
        # add any items that we got
        for item in items:
            self.__create_item__(*item)  # FIXME when we call frame adjust we will loose the record of any data items

        # dragging
        self.title_button.bind(DGG.B1PRESS, self.__startDrag)
        self.title_button.bind(DGG.B1RELEASE, self.__stopDrag)

        # raise if we click the frame background
        self.frame_bg.bind(DGG.B1PRESS, self.raise_)
        #self.frame_bg.bind(DGG.B1RELEASE, self.__stopDrag)  # this can cause problems w/ was dragging


        # toggle vis
        if shortcut:
            self.accept(shortcut, self.toggle_vis)

        # adjust the frame
        self.frame_adjust()

    @property
    def text_s(self):
        return self.text_h * self.TEXT_MAGIC_NUMBER

    def setTextHeight(self):
        h_units = 2 * base.a2dTop
        units_per_pixel = h_units / self.__winy__
        text_h = self.text_height_mm * self.pixels_per_mm * units_per_pixel
        self.text_h = text_h

    def do_xywh(self, x, y, w, h):
        """ makes negative wneg xidths and heights work
            as well as negative x and y (bottom right is 0)
        """
        if x < 0:
            x = 1 + x
        if y < 0:
            y = 1 + y
        if w < 0:
            x, w = x + w, -w
        if h < 0:
            y, h = y + h, -h

        self.x = self.fix_x(x)  # for top left
        self.y = self.fix_y(y)  # for top left
        self.width = self.fix_w(w)
        self.height = self.fix_h(h)

    def getWindowData(self, window=None):
        x = base.win.getXSize() 
        y = base.win.getYSize()
        if x != self.__winx__ or y != self.__winy__:
            self.__ar__ = base.camLens.getAspectRatio()  # w/h
            self.__winx__ = x
            self.__winy__ = y
            self.frame_adjust()

    def raise_(self, *args):
        """ function that raises windows
            call FIRST inside any function that should raise
        """
        self.frame.reparentTo(self.frameRoot)  # self.frame doesn't move so no wrt

    def frame_adjust(self):  # FIXME sometimes this fails to call, also calls too often at startup
        self.setTextHeight()
        MI = self.getMaxItems()  # does not count title >_<
        LI = len(self.items)
        DI = MI - LI
        if DI >= 0:
            for i in range(DI+1):
                self.__create_item__(' blank')
        else:
            for i in range(-(DI+1)):
                k,v = self.items.popitem()  # remove the last nodes in order
                v.removeNode()  # FIXME consider keeping these around?

        for k,b in self.items.items():
            if k == 'title':
                if self.frame_bg.isHidden():
                    x, y, z = self.frame_bg.getPos()
                    self.title_button.setPos(LVecBase3f(x, y , z-self.text_h))
                else:
                    self.title_button.setPos(LVecBase3f(0, 0, -self.text_h))
            elif k == self.__first_item__:
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
            else:
                b.setPos(LVecBase3f(0, 0, -self.text_h))
            b['frameSize'] = 0, self.width, 0, self.text_h
            b['text_scale'] = self.text_s, self.text_s
            b['text_pos'] = 0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s
        
    def getWindowSize(self, event=None):  # TODO see if we really need this
        self.__winx__ = base.win.getXSize()
        self.__winy__ = base.win.getYSize()
        m = max(self.__winx__, self.__winy__)
        self.__xscale__ = self.__winx__ / m
        self.__yscale__ = self.__winy__ / m

    # put origin in top left and positive down and right
    @staticmethod
    def fix_x(x): return (x - .5) *  2  # TODO * base.a2dLeft?
    @staticmethod
    def fix_y(y): return (y - .5) * -2  # TODO * base.a2dTop?
    @staticmethod
    def fix_w(n): return  n * 2
    @staticmethod
    def fix_h(n): return -n * 2

    def add_item(self, text, command = None, args = tuple()): 
        args = list(args)
        if text[0] != ' ':
            text = ' '+text
        items = list(self.items)
        last_slot = len(self.items)
        if self.__add_head__ == last_slot:
            print('all slots are full, cannot add item to %s'%self)
            return None
        button = self.items[items[self.__add_head__]]
        button['text'] = text
        button['command'] = command
        button['extraArgs'] = args + button['extraArgs']  # blank buttons always have [self,id]
        self.__add_head__ += 1


    def __create_item__(self, text, command = None, args = tuple()): 
        args = list(args)

        #if not len(self.items):
            #parent = self.frame
        if len(self.items) <= 1:
            parent = self.itemsParent  #everyone else parents off 2nd text
        else:
            parent = list(self.items.values())[-1]

        if command != None:
            def cmd(*args):
                """ any item should raise
                """
                self.raise_()
                command(*args)
        else:
            cmd = self.raise_


        b = DirectButton(
            parent=parent,
            frameColor=(1,1,1,.0),  # a = 0 => no border overlap
            frameSize=(0, self.width, 0, self.text_h),
            text=' '+text,  # hack to keep spacing from border
            text_font=self.text_font,
            text_fg=self.text_color,
            text_scale=self.text_s,
            text_pos=(0, self.text_h - self.TEXT_MAGIC_NUMBER * self.text_s),
            command=cmd,
            relief=DGG.FLAT,
            text_align=TextNode.ALeft,
        )

        b.setPos(LVecBase3f(0, 0, -self.text_h))
        b.setName('DirectButton-'+text)
        if not len(self.items):
            self.items['title'] = b
            b.setBin(*self.DRAW_ORDER['title'])
        else:
            b['extraArgs'] = args+[self, id(b)]
            b.node().setPythonTag('id', id(b))
            b.setBin(*self.DRAW_ORDER['items'])
            if len(self.items) is 1:  # the first item that is not the title
                b.setPos(LVecBase3f(0, 0, -(self.text_h * 2)))
                self.__first_item__ = id(b)

            self.items[id(b)] = b

        if text == ' blank':
            if self.__add_head__ is None:
                self.__add_head__ = 1

        return b

    def del_all(self):
        if self.__first_item__ != None:
            for id_, button in self.items.items():
                if id_ != 'title':
                    button['text'] = ' blank'
                    button['command'] = None
                    button['extraArgs'] = [self, id_]
            self.__add_head__ = 1

    def del_item(self, text):  # FIXME uniqueness problems
        #d = self.itemsParent.find('*%s*'%text)
        if text[0] != ' ':
            text = ' '+text
        d = [i for i in self.items.values() if i.getName().count(text)]
        try:
            self.__del_item__(d[0].getPythonTag('id'))
        except IndexError:
            print('that item does not seem to exist')
            # if we have a name then there shouldn't be key errors

    def __del_item__(self, index):
        """ I have no idea how this is going to work """
        out = self.items[index]
        p = out.getParent()
        if out.getNumChildren():  # avoid the printing of the AssertionError :/
            c = out.getChild(0)
            c.reparentTo(p)
            if index == self.__first_item__:  # XXX is fails, ints from id !=
                c.setPos(LVecBase3f(out.getPos()))
                id_ = c.getPythonTag('id')
                self.__first_item__ = id_
                out.setPos(LVecBase3f(0, 0, -self.text_h))
        self.items.pop(index)
        parent = list(self.items.values())[-1]
        out['text'] = ' del blank'
        #out['command'] = None
        out['extraArgs'] = [self, index]
        out.reparentTo(parent)
        self.items[index] = out
        if self.__add_head__ > 1:  # title is always at 0
            self.__add_head__ -= 1

    @classmethod
    def __make_border__(cls, parent, thickness, color, l, r , b, t):
        moveto_drawto = (
           ((l,0,t), (l,0,b)),
           ((r,0,t), (r,0,b)),
           ((l,0,b), (r,0,b)),
           ((l,0,t), (r,0,t)),
        )
        for moveto, drawto in moveto_drawto:
            Border = LineSegs()
            Border.setThickness(thickness)
            Border.setColor(*color)
            Border.moveTo(*moveto)
            Border.drawTo(*drawto)
            b = parent.attachNewNode(Border.create())
            b.setBin(*cls.DRAW_ORDER['border'])

    def getMaxItems(self):
        return int(abs(self.height / self.text_h) - 1)

    @event_callback
    def toggle_vis(self):
        if self.frame_bg.isHidden():
            self.frame_bg.show()
            self.raise_()
        else:
            self.frame_bg.hide()

    def title_toggle_vis(self):
        if not self.__was_dragging__:
            self.toggle_vis()
            if self.frame_bg.isHidden():
                self.title_button.wrtReparentTo(self.frame)
                self.title_button['frameColor'] = (1, 1, 1, .5)  # TODO
            else:
                self.title_button.wrtReparentTo(self.frame_bg)
                self.title_button['frameColor'] = (1, 1, 1, 0)  # TODO
        else:
            self.__was_dragging__ = False

    def __startDrag(self, crap):
        self.raise_()
        self._ox, self._oy = base.mouseWatcherNode.getMouse()
        taskMgr.add(self.__drag,'dragging %s'%self.title)
        self.origBTprefix=self.BT.getPrefix()
        self.BT.setPrefix('dragging frame')

    def __drag(self, task):
        if base.mouseWatcherNode.hasMouse():
            x, y = base.mouseWatcherNode.getMouse()
            if x != self._ox or y != self._oy:
                m_old = aspect2d.getRelativePoint(render2d, Point3(self._ox, self._oy, 0))
                m_new = aspect2d.getRelativePoint(render2d, Point3(x, y, 0))
                dx, dy, _ = m_new - m_old
                self.setPos(self.x + dx, self.y + dy)
                self._ox = x
                self._oy = y
                self.__was_dragging__ = True
        return task.cont

    def __stopDrag(self,crap):
        taskMgr.remove('dragging %s'%self.title)
        self.BT.setPrefix(self.origBTprefix)

    def setPos(self, x, y):
        """ actually sets the title button position
            since it is really the parent node
        """
        self.x = x
        self.y = y #- self.text_h  # FIXME is hard :/
        self.frame_bg.setPos(LVecBase3f(x, 0, y))
        if self.frame_bg.isHidden():
            self.title_button.setPos(LVecBase3f(x, 0, y - self.text_h))


    def __enter__(self):
        #load the position
        #load other saved state
        pass

    def __exit__(self):
        #save the position!
        #save other state
        pass
Ejemplo n.º 6
0
class ConsoleWindow(DirectObject.DirectObject):
    console_output = None
    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)
    textBuffer = list()
    MAX_BUFFER_LINES = 5000
    commandPos = 0
    _iconsole = None
    _commandBuffer = ''
    logger.debug("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)
    consoleFrame = None
    commandList = []
    maxCommandHistory = 10000
    textBufferPos = -1
    clipboardTextLines = None
    clipboardTextRaw = None

    def __init__(self, parent):
        global base
        if not logger.isEnabledFor(logging.DEBUG):
            global CONSOLE_MESSAGE
            CONSOLE_MESSAGE = '''
----------------- Ship's Interface version 3.0.9_749 -------------------
Direct Ship Interface Enabled.
Please use caution.  Irresponsible use of this console may result in the ship's AI refusing access to this interface.

type 'help' for basic commands.
-------------------------------------------------------------------------'''

        # change up from parent/IC
        self.parent = parent
        localenv = globals()
        localenv['gameroot'] = self.parent
        self._iconsole = customConsoleClass(localsEnv=localenv)

        # 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)
        fixedWidthFont = None
        try:
            # try to load the defined font
            fixedWidthFont = parent.loader.loadFont(self.font)
        except Exception:
            traceback.print_exc()
            # if font is not valid use default font
            logger.warn('could not load the defined font %s" % str(self.font')
            fixedWidthFont = DGG.getDefaultFont()

        # text lines
        self._visibleLines = list(
            OnscreenText(parent=self.consoleFrame,
                         text="",
                         pos=(1, -i + 3 + self.numlines),
                         align=TextNode.ALeft,
                         mayChange=1,
                         scale=1.0,
                         fg=self.defaultTextColor)
            for i in range(self.numlines))
        map(lambda x: x.setFont(fixedWidthFont), self._visibleLines)

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

        # self.console_output = ConsoleOutput(testme=True)
        self.echo(CONSOLE_MESSAGE)
        self.clipboard = pyperclip

    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 submitTrigger(self, cmdtext):
        # set to last message
        # clear line
        self.consoleEntry.enterText('')
        self.focus()
        # add text entered to user command list & remove oldest entry
        self.commandList.append(cmdtext)
        self.commandPos += 1
        self._commandBuffer = ''
        logger.debug('CP {}'.format(self.commandPos))
        # push to interp
        for text, pre, color in self._iconsole.push(cmdtext):
            self.echo(text, pre, color)

    # set up console controls
    def mapControls(self):
        hidden = self.consoleFrame.isHidden()
        self.consoleEntry['focus'] != hidden
        if hidden:
            self.ignoreAll()
        else:
            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('escape', 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)

    # toggle the gui console
    def toggleConsole(self, hide=False):
        if hide:
            self.consoleFrame.hide()
            self.ignoreAll()
            # express hide, don't call setControls()
            return

        if self.consoleFrame.is_hidden():
            self.consoleFrame.show()
            self.parent.setControls(self)
        else:
            self.consoleFrame.hide()
            self.ignoreAll()
            self.parent.setControls()

    def scroll(self, step):
        newpos = self.textBufferPos + step
        if newpos < 0 or newpos >= len(self.textBuffer):
            # no... no... I no think so
            return
        self.textBufferPos = newpos
        self.redrawConsole()

    def redrawConsole(self):
        windowstart = max(self.textBufferPos - len(self._visibleLines) + 1, 0)
        windowend = min(
            len(self._visibleLines) + windowstart, len(self.textBuffer))
        logger.debug('windowS: {} WindowE: {}'.format(windowstart, windowend))
        for lineNumber, (lineText, color) in \
                enumerate(self.textBuffer[
                    windowstart:
                    windowend]):
            logger.debug("LN {}, LEN {}".format(lineNumber,
                                                len(self.textBuffer)))
            self._visibleLines[lineNumber].setText(lineText)
            self._visibleLines[lineNumber]['fg'] = color

    def scrollCmd(self, step):
        if not self.commandList:  # 0 or null - nothing to scroll
            return
        # should we update a temp buffer?
        if self.commandPos == len(self.commandList):
            if step > 0:
                self.consoleEntry.set(self._commandBuffer)
                return
            else:
                tmp = self.consoleEntry.get()
                if self.commandList[-1] != tmp:
                    self._commandBuffer = tmp
        self.commandPos += step
        if self.commandPos >= len(self.commandList):
            self.commandPos = len(self.commandList) - 1
            self.consoleEntry.set(self._commandBuffer)
            self.consoleEntry.setCursorPosition(
                len(self.commandList[self.commandPos]))
        elif self.commandPos < 0:
            self.commandPos = -1
            # No need to change anything, can't go past the beginning
            return
        # finally, just set it
        self.consoleEntry.set(self.commandList[self.commandPos])
        self.consoleEntry.setCursorPosition(
            len(self.commandList[self.commandPos]))

    def autocomplete(self):
        currentText = self.consoleEntry.get()
        currentPos = self.consoleEntry.guiItem.getCursorPosition()
        newText = self._iconsole.autocomplete(currentText, currentPos)
        if newText[-1] and newText[-1] != currentText:
            self.consoleEntry.set(newText[-1])
            self.consoleEntry.setCursorPosition(len(newText))

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

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

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

    def copy(self):
        copy = self.consoleEntry.get()
        pyperclip.copy(copy)

    def paste(self):
        oldCursorPos = self.consoleEntry.guiItem.getCursorPosition()
        self.clipboardTextRaw = pyperclip.paste()

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

        self.clipboardTextLines = newText.split(os.linesep)

        for i in range(len(self.clipboardTextLines) - 1):
            currentLine = self.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.submitTrigger(currentLine)
        currentLine = self.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):
        pyperclip.copy(self.consoleEntry.get())
        self.consoleEntry.enterText('')
        self.focus()

    def echo(self, output, pre='*', color=defaultTextColor):
        if logger.isEnabledFor(logging.DEBUG):
            logger.debug('output: {}'.format(pprint.pformat(output)))
        for line in output.split('\n'):
            fmtline = "{}{}".format(pre, line)
            logger.debug(fmtline)
            if len(line) > 0:
                self.write_to_panel(fmtline, color)

    def write_to_panel(self, output, color=defaultTextColor):
        # remove not printable characters (which can be input by console input)
        output = re.sub(r'[^%s]' % re.escape(string.printable[:95]), "",
                        output)
        logger.debug('write_to_panel: output="{}"'.format(output))
        splitLines = self.linewrap.wrap(output)
        logger.debug('write_to_panel: splitLines="{}"'.format(splitLines))
        for line in splitLines:
            self.textBuffer.append([line, color])
            if len(self.textBuffer) > self.MAX_BUFFER_LINES:
                self.textBuffer.pop(0)
            else:
                self.textBufferPos += 1

        self.redrawConsole()
Ejemplo n.º 7
0
class OnScreenInterface():
	
	# Time users have for each level will be listed here
	LEVEL_1_TIME = (3,59)
	LEVEL_2_TIME = (6,59)
	
	'''
	    Constructor takes a reference of the current game, creates a direct frame that everything
	    will be parented to and also sets the sound effect that will be used for aything on the 
	    onscreen interface class.
	    @param game - pointer to current game
	'''
	def __init__(self,game):
		self.previous = -1
		self.min = 0
		self.sec = 0
		self.__game = game
		self.create_main_frame()
		self.set_sound()

	'''
	    Creates instance variables for all sounds effects that will be used for this
	    instance of the class. Volume and playrate is also set.
	'''
	def set_sound(self):
		self.intro = base.loader.loadMusic("sfx/not_seems.mp3")
		self.hover = base.loader.loadSfx("sfx/hover.mp3")
		self.click = base.loader.loadSfx("sfx/click.wav")
		self.pause = base.loader.loadSfx("sfx/pause.wav")
		self.blocked = base.loader.loadSfx("sfx/blocked.wav")
		self.blocked.setVolume(.05)
		self.hover.setPlayRate(5)
		self.hover.setVolume(.05)
		self.intro.setVolume(.5)
		self.intro.setLoop(True)

	'''
	    Creates instance variable that decides how much time user will have to complete the current level
	'''
	def set_timer(self,level):
		if level == 'L1':
			self.min = OnScreenInterface.LEVEL_1_TIME[0]
			self.sec = OnScreenInterface.LEVEL_1_TIME[1]
		elif level == 'L2':
			self.min = OnScreenInterface.LEVEL_2_TIME[0]
			self.sec = OnScreenInterface.LEVEL_2_TIME[1]
	
	'''
	   Creates the initial two frames that are needed in the game before the gameplay starts
	'''
	def load_initial_interface(self):
		self.create_player()
		self.create_start()
		self.create_stage_selector(button=False)
	
	'''
	   Creates the rest of the frames that are needed once gameplay has started
	'''
	def load_essentials(self):
		self.create_health_bar()
		self.create_token_counter()
		self.create_coordinate_view()	
		self.create_help_menu()
		self.create_control_guide()
		self.create_stage_selector()
		self.create_leaderboard_selector()
		self.create_leaderboard(self.__game.current_level)

	#------------------------------------------------------------------------- CREATE METHODS -----------------------------------------------------------------#

	'''
	    Creates frame that will be the parent of all the other frames created for this game.
	    This is done so that when all gui interfaces need to be destroyed they can be destroyed with 
	    on call to destroy.
	'''
	def create_main_frame(self):
		self.main_frame = DirectFrame(frameColor = (0,0,0,0),frameSize=(-2,2,-1,1), pos=(0,0,0))

	
	'''
	    Creates game start frame. This frame is not hidden because it is the first thing the user sees.	
	'''
	def create_start(self):
		# Direct frame to hold contents of start frame
		self.start_frame = DirectFrame(parent = self.main_frame,frameColor = (0,0,0,1),frameSize=(-2,2,-1,1), pos=(0,0,0))
		
		# Onscreen image of kyklops
		kyklops = OnscreenImage(parent=self.start_frame,image = 'img/kyklops.png', pos = (.9, 0, .3), scale = (.3,0,.3))
		kyklops.setTransparency(TransparencyAttrib.MAlpha)
		
		# Onscreen image of game title
		title = OnscreenImage(parent=self.start_frame,image = 'img/title.png', pos = (0, 0, 0), scale = (.8,0,.3))
		title.setTransparency(TransparencyAttrib.MAlpha)
		
		# Onscreen image of eve rolling
		rolling_eve = OnscreenImage(parent=self.start_frame,image = 'img/rolling-eve.png', pos = (-.95, 0, -.1), scale = (.5,0,.5))
		rolling_eve.setTransparency(TransparencyAttrib.MAlpha)

		# Create button to start game
		self.create_menu_button(self.start_frame,'START',LVecBase3f(0,0,-.5),self.show_input)

		# Play intro music		
		self.intro.play()

	'''
	    Creates a fields so the player can input his/her name at the beginning of the game. This is used
	    to store the name and user score at the end of each stage in the game.
	'''
	def create_player(self):
		# Direct frame to hold input field
		self.input_frame = DirectFrame(parent=self.main_frame,frameColor = (0,0,0,1),frameSize=(-2,2,-1,1), pos=(0,0,0))
		
		# Instructions for user
		instructions = OnscreenText(parent=self.input_frame, text = 'Enter your name: ', pos = (0,.2), scale = 0.1, fg=(0,.2,.2,1),shadow=(1,1,1,.7))
		
		# Name input field
		self.entry = DirectEntry(parent=self.input_frame,text = "",scale=(.1,1,.08),pos=(-.5,0,0),command=self.set_player,numLines=1,focus=1)

		# Hide frame
		self.input_frame.hide()

	'''
	    Creates help menu frame with buttons for each option in the menu. This frame is hidden.
	'''
	def create_help_menu(self):
		# Direct frame to hold buttons and text
		self.help_frame = DirectFrame(parent=self.main_frame,frameColor = (.6,.6,.6,.7),frameSize=(-2,2,-1,1), pos=(0,0,0))
		
		# Title for frame
		title = OnscreenText(parent=self.help_frame, text = 'MENU', pos = (0, .4), scale = 0.2, fg=(0,.2,.2,1),shadow=(.5,.5,.5,1) )
		
		# Buttons for menu frame
		self.create_menu_button(self.help_frame,'Controls',LVecBase3f(0,0,.1),self.show_controls)
		self.create_menu_button(self.help_frame,'Level Select',LVecBase3f(0,0,-.1),self.show_level_select)
		self.create_menu_button(self.help_frame,'Leaderboard',LVecBase3f(0,0,-.3),self.show_lb_selection)
		self.create_menu_button(self.help_frame,'Quit',LVecBase3f(0,0,-.5),sys.exit)
		
		# Hide frame 
		self.help_frame.hide()

	'''
	    Creates control instruction frame. This frame hidden. 
	'''
	def create_control_guide(self):
		# Direct frame to hold player control instructions
		self.control_frame = DirectFrame(frameColor = (.9,.9,.9,.9),frameSize=(-2,2,-1,1), pos=(0,0,0))

		# Title for frame
		frame_title = OnscreenText(parent=self.control_frame, text = 'CONTROL KEYS', pos = (0, .5), scale = 0.1, fg=(0,.2,.2,1),shadow=(.5,.5,.5,1))

		OnscreenText(parent=self.control_frame, text = 'PLAYER CONTROLS', pos = (-.5, .3), scale = 0.08, fg=(0,0,0,1),shadow=(.5,.5,.5,1))
		OnscreenText(parent=self.control_frame, text = 'OTHER CONTROLS', pos = (.5, .3), scale = 0.08, fg=(0,0,0,1),shadow=(.5,.5,.5,1))

		# Player control instructions
		OnscreenText(parent=self.control_frame, text = '[w] - Forward', pos = (-.5, .2), scale = 0.07, fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[a] - Left', pos = (-.5, .1), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[d] - Right', pos = (-.5, 0), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[s] - Back', pos = (-.5, -.1), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[space] - Jump', pos = (-.5, -.2), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[m] - Toggle Modes', pos = (-.5, -.3), scale = 0.07,fg=(0,0,0,1))

		OnscreenText(parent=self.control_frame, text = '[h] - Help Menu', pos = (.5, .2), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[c] - Toggle Camera Modes', pos = (.5, .1), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[q] - Toggle Music On/Off', pos = (.5, 0), scale = 0.07,fg=(0,0,0,1))
		OnscreenText(parent=self.control_frame, text = '[x] - Toggle SFX On/Off', pos = (.5, -.1), scale = 0.07,fg=(0,0,0,1))

		# Create button to go back to main menu
		self.create_menu_button(self.control_frame,'Back',LVecBase3f(0,0,-.7),self.show_menu)

		# Hide frame
		self.control_frame.hide()
	'''
	    Creates stage selection frame. This frame is hidden.
	'''
	def create_stage_selector(self,button=True):
		# Direct frame to hold stage selection buttons
		self.stage_select_frame = DirectFrame(parent=self.main_frame,frameColor = (.8,.8,.8,.9),frameSize=(-2,2,-1,1), pos=(0,0,0))
		
		title = OnscreenText(parent=self.stage_select_frame, text = 'Stage Selection', pos = (0, .7), scale = .15,fg=(0,.2,.2,1))

		# Stage select buttons with title for each button 
		t1 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 1', pos = (-.9, .5), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/stage1.png','',LVecBase3f(-.9,0,.2),self.__game.clean_and_set,'L1')
		# Stage 2 will be unlocked when stage 2 is made		
		t2 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 2', pos = (-.3, .5), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/stage2.png','',LVecBase3f(-.3,0,.2),self.__game.clean_and_set,'L2')
		
		t3 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 3', pos = (.3, .5), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(.3,0,.2),self.__game.clean_and_set,'L3')
		
		t4 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 4', pos = (.9, .5), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(.9,0,.2),self.__game.clean_and_set,'L4')
		
		t5 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 5', pos = (-.9, -.1), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(-.9,0,-.4),self.__game.clean_and_set,'L5')
		
		t6 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 6', pos = (-.3, -.1), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(-.3,0,-.4),self.__game.clean_and_set,'L6')
		
		t7 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 7', pos = (.3, -.1), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(.3,0,-.4),self.__game.clean_and_set,'L7')
			
		t8 = OnscreenText(parent=self.stage_select_frame, text = 'STAGE 8', pos = (.9, -.1), scale = 0.07,fg=(0,.2,.2,1))
		self.create_stage_button(self.stage_select_frame,'img/locked.jpg','',LVecBase3f(.9,0,-.4),self.__game.clean_and_set,'L8')
		
		if button is True:		
			# Create button to go back to main menu
			self.create_menu_button(self.stage_select_frame,'Back',LVecBase3f(0,0,-.8),self.show_menu)
		
		# Hide frame
		self.stage_select_frame.hide()

	
	'''
	    Creates leaderboard selection frame which will contain links to the leaderboard for the different game stages. This frame is hidden.
	'''
	def create_leaderboard_selector(self):
		# Direct frame to hold links to the leaderboards for different levels
		self.leaderboard_selection_frame = DirectFrame(parent=self.main_frame,frameColor = (.8,.8,.8,.9),frameSize=(-2,2,-1,1), pos=(0,0,0))

		# Frame title
		title = OnscreenText(parent=self.leaderboard_selection_frame, text = 'LEADERBOARD SELECTION', pos = (0, .6), scale = 0.15, fg=(0,.2,.2,1),shadow=(.5,.5,.5,1))
		
		# Links to leaderboards for the stages that are currently made
		self.link(self.leaderboard_selection_frame,'STAGE 1',LVecBase3f(0,0,.0),self.show_leaderboard,'L1')
		self.link(self.leaderboard_selection_frame,'STAGE 2',LVecBase3f(0,0,-.1),self.show_leaderboard,'L2')
		
		# Create button to go back to main menu
		self.create_menu_button(self.leaderboard_selection_frame,'Back',LVecBase3f(0,0,-.4),self.show_menu)

		# Hide frame 
		self.leaderboard_selection_frame.hide()

	'''
	    Leaderboard is created based on the level that was clicked on the leaderboard selector frame. This information is gathered from the stored information in
	    .leaderboard.txt. This method will look for the correct section in the file and output the information for the stage choosen onto a direct frame.
	'''
	def create_leaderboard(self,level):
		# Direct frame to hold all contents of the leaderboard
		self.leaderboard_frame = DirectFrame(parent=self.main_frame,frameColor = (.8,.8,.8,.9),frameSize=(-2,2,-1,1), pos=(0,0,0))

		# Create a scroll_frame to hold contents of leaderboard file
		scroll_frame = DirectScrolledFrame(parent = self.leaderboard_frame,
						   canvasSize=(-1,1,-4,4),
					           frameColor = (1,1,1,.9),
						   frameSize=(-1,1,-.5,.5), 
						   pos=(0,0,0),
						   manageScrollBars=True,
						   scrollBarWidth = .04, 
					           autoHideScrollBars = True)

		# Frame title
		title = OnscreenText(parent=self.leaderboard_frame, text = 'LEADERBOARD', pos = (0, .6), scale = 0.15, fg=(0,.2,.2,1),shadow=(.5,.5,.5,1))
		
		# Open .leaderboard.txt file as read only
		leaderboard_file = open('files/.leaderboard.txt','r')
		start_read = False					# Boolean that will used to notify loop when to start generating text on the scroll canvas
		start = leaderboard_file.read(1)			# Reads only the first byte of the file, equivalent to a character

		# Guide for leaderboard
		name = OnscreenText(parent=scroll_frame.getCanvas(), text = 'NAME', pos = (-.1, 3.9), scale = 0.075,fg=(0,.2,.2,1))
		score = OnscreenText(parent=scroll_frame.getCanvas(), text = 'SCORE', pos = (.5, 3.9), scale = 0.075,fg=(0,.2,.2,1))
		
		index = 1
		v_pos = 3.8

		# Loop will iterate through the contents of the file
		while len(start) != 0:
			if start == '#': leaderboard_file.readline()			# Comments in text file start with an octothorpe and are ignored
			elif start == '@'and start_read is False:			# Line with @ signify the beginning of a set of level info
				if leaderboard_file.readline().split()[0] == level:	# This checks if the info that follows is the information we need
					start_read = True
			elif start == '@'and start_read is True:			# If this condition is true then we read through all the information we need
				break
			elif start_read is True:
				coord = leaderboard_file.readline().split(',')
				if len(coord) == 1:
					name = 'Unknown'				
					score = coord[0]
				else:
					name = start + coord[0]					
					score = coord[1]					
				entry_text = str(index) + ".\t " + name + '\t\t' + score
				# Create onscreen text for each entry
				entry = OnscreenText(parent=scroll_frame.getCanvas(), text = entry_text, pos = (0,v_pos), scale = 0.07,fg=(0,0,0,1))
				# Increase index and decrease vertical position
				index += 1
				v_pos -= .1
			# Read the first byte of the next line
			start = leaderboard_file.read(1)

		leaderboard_file.close()

		# Create button to go back to the leaderboard selection frame
		self.create_menu_button(self.leaderboard_frame,'Back',LVecBase3f(0,0,-.7),self.show_lb_selection)

		# Hide frame
		self.leaderboard_frame.hide()
	
	'''
	    Creates a frame in the top of the window that shows player health and timer. Frame is hidden.
	'''
	def create_health_bar(self):
		# Direct frame that holds all contents such as timer and health bar
		self.health_frame = DirectFrame(parent=self.main_frame,frameColor=(0, 0, 0, .4),frameSize=(-2, 3, -.1, 1),pos=(-.6, 0, .9))
		
		# Create a direct wait bar that will be used as the health gauge.
		self.bar = DirectWaitBar(parent=self.health_frame,
					 text = "HEALTH",
					 text_fg=(1,1,1,1), 
					 value = 100, 
					 range=100, 
					 pos = (.15,0,-.02),
					 barColor=VBase4(0,.2,.2,1),
					 scale=.6)

		# Onscreen image for eve face icon on health gauge
		eve_icon = OnscreenImage(parent=self.bar,image = 'img/eve_face.png', pos = (-1.15, 0, -.075), scale = (.25,1,.25))
		eve_icon.setTransparency(TransparencyAttrib.MAlpha)
		
		# Create a node for timer
		timer_txt = str(self.min) + ' min ' + str(self.sec) + ' seconds'
		self.timer = OnscreenText(parent=self.health_frame, text = timer_txt, pos = (1.5, -.02), scale = 0.07, fg=(1,1,1,1))

		# Hide frame
		self.health_frame.hide()
	
	'''
	    Creates an a token counter in the right bottom corner of screen. This image is hidden.
	'''
	def create_token_counter(self):
		# Set onscreen image and set transparency
		self.shape = OnscreenImage(image = 'img/tire_score.png', pos = (1, 0, -.85), scale = (.3,1,.1))
		self.shape.setTransparency(TransparencyAttrib.MAlpha)
		
		# Set another onscreen image and set transparency
		tire = OnscreenImage(parent=self.shape,image = 'img/tire.png', pos = (-1, 0, 0), scale = (.4,1,1))
		tire.setTransparency(TransparencyAttrib.MAlpha)

		# Set text displaying the number of token collected
		self.score = OnscreenText(parent=self.shape, text = str(self.__game.eve.tiresCollected), pos = (0, -.1), scale = (.3,.8), fg=(255,255,255,1))

		# Hide token counter
		self.shape.hide()

	'''
	    Creates a frame that displays the name of the stage at the beginning of every stage in the game. Only level 1 and 2 are set.
	'''
	def create_stage_title(self,level):
		s_text = ""
		t_text = ""

		# The following if statement set the text that will be displayed
		if level == 'L1':
			s_text = "Stage 1:"
			t_text = "The Journey Begins"
		elif level == 'L2':
			s_text = "Stage 2:"
			t_text = "The Dark Place"
		
		# Direct frame to hold stage title
		self.stage_frame = DirectFrame(parent=self.main_frame,frameColor=(0, 0, 0, .6),frameSize=(-2, 3, -.1, .4),pos=(0, 0, 0))
		
		stage_name = OnscreenText(parent=self.stage_frame, text = s_text, pos = (0, .2), scale = 0.16, fg=(1,.84,0,1),shadow=(1,1,1,.2))
		stage_title = OnscreenText(parent=self.stage_frame, text = t_text, pos = (0, 0), scale = 0.12, fg=(1,.84,0,1),shadow=(1,1,1,.2))

	'''
	    Creates onscreen text showing current position of the character
	'''
	def create_coordinate_view(self):
		self.coord = OnscreenText(parent=self.main_frame,text='1', style = 1, fg= (1,1,1,1),  pos=(0,-0.95), align=TextNode.A_right, scale=0.08)
		self.coord.hide()

	#------------------------------------------------------------------------- SHOW METHODS -----------------------------------------------------------------#

	'''
	    Shows input frame so user can enter his/her name. 	
	'''	
	def show_input(self):
		self.__game.accept('h', self.do_nothing)
		self.start_frame.destroy()
		self.input_frame.show()	
	
	'''
	    Shows menu frame and hides anything else that may be showing	
	'''
	def show_menu(self):
		if self.control_frame.isHidden() is False:
			self.control_frame.hide()
		elif self.leaderboard_selection_frame.isHidden() is False:
			self.leaderboard_selection_frame.hide()
		elif self.stage_select_frame.isHidden() is False:
			self.stage_select_frame.hide()
		self.__game.accept('h', self.toggleHelp)
		self.help_frame.show()
	
	'''
	    Shows frame that contains the game instructions
	'''
	def show_controls(self):
		self.__game.accept('h', self.do_nothing)
		self.help_frame.hide()
		self.control_frame.show()

	'''
	    Shows stage select frame
	'''
	def show_level_select(self):
		self.__game.accept('h', self.do_nothing)
		self.help_frame.hide()
		self.stage_select_frame.show()

	'''
	    Shows leaderboard selector frame
	'''
	def show_lb_selection(self):
		if self.leaderboard_frame.isHidden() is False:
			self.leaderboard_frame.hide()
		elif self.help_frame.isHidden() is False:
			self.__game.accept('h', self.do_nothing)
			self.help_frame.hide()
		self.leaderboard_selection_frame.show()
	
	'''
	    Shows leaderboard frame for a specific level
	    @param level - stage whose leaderboard will be displayed
	'''
	def show_leaderboard(self,level):
		self.leaderboard_selection_frame.hide()
		self.create_leaderboard(level)
		self.leaderboard_frame.show()

	'''
	    Shows in game stats, which is health gauge, timer and token counter
	'''
	def show_game_interface(self):
		self.health_frame.show()
		self.shape.show()

	'''
	    Hides the game interface. This is used when the help menu us shown.
	'''
	def hide_game_interface(self):
		self.health_frame.hide()
		self.shape.hide()


	#------------------------------------------------------------------------- CREATE BTN METHODS -----------------------------------------------------------------#

	'''
	    Creates a button with an= simple button image as the background. This is the default btn.
	    @param parent - who the button will be parented to
	    @param btn_text - text that will be displayed on button
	    @param btn_pos - position of the button
	    @param cmd - command that will be executed when button is clicked
	    @param level - each link created is based on a stage, this is the stage this btn represents
	'''
	def create_menu_button(self,parent,btn_text,btn_pos,cmd):
		start_btn = DirectButton(parent=parent,
					 text=btn_text,
					 pos=btn_pos,
					 scale=(.2,1,.15),
					 command=cmd,
					 pressEffect=1,
					 text_scale=(.4,.4),
					 text_pos=(.1,.1),
					 text_fg=(.1,.1,.1,1),
					 text_shadow=(1,1,1,1),
					 image='img/btn2.png',
					 image_scale=(2.50,1,.7),
					 image_pos=(0,1,.25),
					 relief=None,
					 rolloverSound = self.hover,
					 clickSound=self.click)
		start_btn.setTransparency(TransparencyAttrib.MAlpha)

	'''
	    Creates a button that contains an image of a stage or if it was not yet created it contains an image of a lock
	    @param parent - who the button will be parented to
	    @param btn_text - text that will be displayed on button
	    @param btn_pos - position of the button
	    @param cmd - command that will be executed when button is clicked
	    @param level - each link created is based on a stage, this is the stage this btn represents
	'''
	def create_stage_button(self,parent,img,btn_text,btn_pos,cmd,level):

		click_sound = self.blocked
		hover_sound = self.blocked
		
		# This if statement sets the sound that each button will have. 
		# Everything that is not level 1 or level 2 will have the blocked sound effect and will do nothing
		if level == 'L1' or level == 'L2':
			click_sound = self.click
			hover_sound = self.hover

		btn = DirectButton(parent=parent,
					 text=btn_text,
					 pos=btn_pos,
					 scale=(.2,1,.15),
					 command=cmd,
					 pressEffect=1,
					 text_scale=(.4,.4),
					 text_pos=(.1,.1),
					 text_fg=(.1,.1,.1,1),
					 text_shadow=(1,1,1,1),
					 image=img,
					 image_scale=(1,1,1),
					 image_pos=(0,1,.25),
					 relief=None,
					 rolloverSound = hover_sound,
					 clickSound=click_sound,
					 extraArgs=[level])
		btn.setTransparency(TransparencyAttrib.MAlpha)

	'''
	    Creates a button with no relief and no background image or color.
	    @param parent - who the button will be parented to
	    @param btn_text - text that will be displayed on button
	    @param btn_pos - position of the button
	    @param cmd - command that will be executed when button is clicked
	    @param level - each link created is based on a stage, this is the stage this btn represents
	'''
	def link(self,parent,btn_text,btn_pos,cmd,level):
		btn = DirectButton(parent=parent,
					 text=btn_text,
					 pos=btn_pos,
					 scale=(.2,1,.15),
					 command=cmd,
					 pressEffect=1,
					 text_scale=(.4,.4),
					 text_pos=(.1,.1),
					 text_fg=(.1,.1,.1,1),
					 relief=None,
					 rolloverSound = self.hover,
					 clickSound=self.click,
					 extraArgs = [level])

	def link2(self,parent,btn_text,btn_pos,cmd):
		btn = DirectButton(parent=parent,
					 text=btn_text,
					 pos=btn_pos,
					 scale=(.2,1,.15),
					 command=cmd,
					 pressEffect=1,
					 text_scale=(.4,.4),
					 text_pos=(.1,.1),
					 text_fg=(.1,.1,.1,1),
					 relief=None,
					 rolloverSound = self.hover,
					 clickSound=self.click)


	#------------------------------------------------------------------------- TASK METHODS -----------------------------------------------------------------#
	
	'''
	    At the beginning of every stage the title is show. This task only runs for the first 2 seconds of each stage. It shows
	    the stage title for 2 seconds and then destroys the frame containing the title and the task ends.
	'''	
	def show_title(self,task):
		if globalClock.getRealTime() - self.__game.actual_start > 2:	# Wait two seconds
			self.stage_frame.destroy()
			self.show_game_interface()
			self.__game.eve.enable_character_controls()		# Enable character controls only after the two seconds have passed
			self.__game.accept('h', self.toggleHelp)
			self.__game.accept('f1', self.__game.toggleDebug)
			return Task.done
		return Task.cont
	
	'''
	    This task runs continuously throughout each stage. It updates the time remaining in the current level.
	    This task ends when the minutes and seconds reach 0.
	'''
	def update_timer(self,task):
		elapsed_time = globalClock.getRealTime() - self.__game.actual_start
		change_time = int(elapsed_time) % 60
		if change_time == 0 and self.previous != int(elapsed_time):
			self.min = self.min - int(elapsed_time) / 60
			self.sec = 59
		else:
			if self.previous != int(elapsed_time):
				self.sec = 59 - change_time 
	
		self.timer['text'] = str(self.min) + ' min ' + str(self.sec) + ' seconds'
		self.previous = int(elapsed_time)
		if self.min == 0 and self.sec == 0:
			self.__game.game_over = True
			return Task.done
		return Task.cont 

	'''
	    In debug mode the user can view the players current position in the left bottom corner. This task
	    takes care of updating that position onscreen text.
	'''
	def updateCoord(self, task):
        	x = self.__game.eve.currentNP.getX()
       		y = self.__game.eve.currentNP.getY()
        	z = self.__game.eve.currentNP.getZ()
        	self.coord.setText(str(x) + " , " + (str(y)) + " , "  + str(z))
        	return Task.cont


	def game_over(self):
		self.game_over_frame = DirectFrame(parent = self.main_frame,frameColor=(1, 1, 1, .7),frameSize=(-2, 2, 1, -1),pos=(0, 0, 0))
		OnscreenText(parent = self.game_over_frame,text = 'GAME OVER', pos = (0, 0), scale = .1, fg=(1,0,0,1))
		OnscreenText(parent = self.game_over_frame,text = 'RETRY?', pos = (0, -.2), scale = .07, fg=(1,0,0,1))
		self.link(self.game_over_frame,'YES',LVecBase3f(.3,0,-.3),self.__game.clean_and_set,self.__game.current_level)
		self.link2(self.game_over_frame,'NO',LVecBase3f(-.3,0,-.3),sys.exit)

		
	
	def level_passed(self):
		self.level_passed_frame = DirectFrame(parent = self.main_frame,frameColor=(1, 1, 1, .9),frameSize=(-2, 2, 1, -1),pos=(0, 0, 0))
		if self.__game.current_level == 'L1':
			t1 = OnscreenText(parent = self.level_passed_frame,text = 'STAGE 1 COMPLETE', pos = (0, 0), scale = .1, fg=(1,0,0,1))
		elif self.__game.current_level == 'L2':
			t1 = OnscreenText(parent = self.level_passed_frame,text = 'STAGE 2 COMPLETE', pos = (0, 0), scale = .1, fg=(1,0,0,1))
		t2 = OnscreenText(parent = self.level_passed_frame,text = 'Wheels Collected : ' + str(self.__game.eve.tiresCollected) + ' / ' + str(self.__game.e.total_tokens) , pos = (0, -.2), scale = .1, fg=(1,0,0,1))
		t3 = OnscreenText(parent = self.level_passed_frame,text = 'Score : ' + str(self.__game.user.score) , pos = (0, -.3), scale = .1, fg=(1,0,0,1))

		OnscreenText(parent = self.level_passed_frame,text = 'CONTINUE?', pos = (0, -.5), scale = .07, fg=(1,0,0,1))
		if self.__game.current_level == 'L1':
			self.link(self.level_passed_frame,'YES',LVecBase3f(.3,0,-.6),self.__game.clean_and_set,'L2')
		else:
			self.link(self.level_passed_frame,'YES',LVecBase3f(.3,0,-.6),self.__game.clean_and_set,'L1')
		self.link2(self.level_passed_frame,'NO',LVecBase3f(-.3,0,-.6),sys.exit)

	'''
	    After user inputs his/her name in the beginning of the game, this method is execute and a new user is instantiated and the game is setup.
	    Aside from this two things are added to the task manager...the timer and the stage title.
	'''
	def set_player(self,entry):
		print '\tWELCOME ' + entry + ' ...'
		print '\tSETTING UP USER ...'
		self.input_frame.destroy()				# Destroy the input frame
		self.__game.user = User(entry)		# Frame title

		self.stage_select_frame.show()

	
	'''
	   Method used to toggle between the help menu and the game.
	'''
	def toggleHelp(self):
		if self.help_frame.isHidden():
			# Stop update task and disable character controls when help is activated
			self.pause.play()
			self.__game.taskMgr.remove('update')
			self.hide_game_interface()
			self.__game.eve.disable_character_controls()
			self.help_frame.show()
		else:
			# Restart update task and enable character controls when help is deactivated
			self.pause.play()
			self.__game.taskMgr.add(self.__game.update,'update')            # Add task to task manager
			self.show_game_interface()
			self.__game.eve.enable_character_controls()
			self.help_frame.hide()
	
	'''
	    Method does nothing but is important to stop the h key from working when help menu is in a sub menu or frame
	'''
	def do_nothing(self):
		pass
Ejemplo n.º 8
0
class Menu(UIItem):
    def __init__(self, list=[]):
        self.__menuContainer = DirectFrame(
            #text_pos=(0,1),
            pos=(0, 0, 0),
            text_scale=1.5,
            text_fg=(0, 0, 0, 0),
            relief=DGG.FLAT,
            frameColor=(1, 1, 1, 0.5))
        self.__menuContainer.hide()
        self.__menuItems = []
        self.selectedItem = 0
        self.addItems(list)

    def addItem(self, text, action):
        self.__menuItems.append(
            DirectButton(
                text=text,
                command=self.pressEnter,
                extraArgs=[len(self.__menuItems)],
                text_align=TextNode.ALeft,
                scale=0.1,
                #left/right, forward/back, up/down
                pos=Vec3(-0.38, 0, 0.5 - (len(self.__menuItems) * 0.15)),
                text_fg=(1, 1, 1, 1),
                rolloverSound=None,
                clickSound=None,
                pressEffect=0,
                relief=None,
                textMayChange=True
                #text_font=base.fontLoader.load('Arial Bold.ttf')
            ))
        self.__menuItems[-1].reparentTo(self.__menuContainer)
        self.__menuItems[-1].setPythonTag('action', action)
        self.__menuItems[-1].setPythonTag('text', text)

    def addItems(self, list):
        for k, v in list:
            self.addItem(k, v)

    def hasItem(self, text):
        for i in self.__menuItems:
            if (i['text'] == text): return True
        return False

    def __del__(self):
        self.ignoreAll()

    def pressEnter(self, item=None):
        if (item != None):
            self.selectedItem = item

        logging.info("pressEnter:: selectedItem = %s" % str(self.selectedItem))

        action = self.__menuItems[self.selectedItem].getPythonTag('action')
        text = self.__menuItems[self.selectedItem].getPythonTag('text')

        logging.info("pressEnter:: action = %s" % str(action))

        if (action != None):  #function
            #self.hide()
            action(text)  #call the function in 'action'

    def enter(self):
        """ Press the enter key to select the current menu item. """
        self.pressEnter()
        UIItem.enter(self)

    def up(self):
        """Move one item up in the menu."""
        newItem = self.selectedItem - 1
        if (newItem < 0):
            newItem = len(self.__menuItems) - 1
        self.select(newItem)

        UIItem.up(self)

    def down(self):
        """Move one item down in the menu."""
        newItem = self.selectedItem + 1
        if (newItem >= len(self.__menuItems)):
            newItem = 0
        self.select(newItem)

        UIItem.down(self)

    def select(self, item):
        self.__menuItems[self.selectedItem]['text_fg'] = (1, 1, 1, 1)
        self.__menuItems[self.selectedItem]['text_bg'] = (0, 0, 0, 0)
        self.selectedItem = item
        self.__menuItems[self.selectedItem]['text_fg'] = (0, 0, 0.5, 1)
        self.__menuItems[self.selectedItem]['text_bg'] = (1, 1, 1, 1)

    def show(self):
        assert len(self.__menuItems) > 0
        self.select(0)  #make the first item selected
        if (self.__menuContainer.isHidden()):
            Sequence(
                Func(self.__menuContainer.setAlphaScale, 0.0),
                Func(self.__menuContainer.show),
                LerpFunctionInterval(self.__menuContainer.setAlphaScale,
                                     toData=1.0,
                                     fromData=0.0,
                                     duration=1.0)).start()

    def hide(self):
        self.ignoreAll()
        if (not self.__menuContainer.isHidden()):
            Sequence(
                LerpFunctionInterval(self.__menuContainer.setAlphaScale,
                                     toData=0.0,
                                     fromData=1.0,
                                     duration=1.0),
                Func(self.__menuContainer.hide),
                Func(self.__menuContainer.setAlphaScale, 1.0)).start()
        #now, hide all dialogs referred to:
        for i in self.__menuItems:
            a = i.getPythonTag('action')
            if (isinstance(a, Menu)):
                a.hide()

    def setParent(self, parent):
        self.__menuContainer.reparentTo(parent)

    def setPos(self, x, y, z):
        self.__menuContainer.setPos(x, y, z)
Ejemplo n.º 9
0
class NameValueList(UIItem):
    """
    A NameValueList is a list of name and value pairs. This is commonly used in the settings screen
    where the user has a list of items and can edit them at any point in time.
    This is just a generic UI element to keep all of the data and logic within the game subsystem
    """
    def __init__(self, items=[], lines_to_show=5):
        # items is a tuple consisting of (text name of item, selection values, current selection)
        self._items = []
        self._lines_to_show = lines_to_show

        self.__menuContainer = DirectFrame(
            #text_pos=(0,1),
            pos=(-0.6, 0, 0.3),
            text_scale=1.5,
            text_align=TextNode.ALeft,
            text_fg=(0, 0, 0, 0),
            relief=DGG.FLAT,
            frameColor=(1, 1, 1, 0),
            #left,right,bottom,top
            frameSize=(-0.5, 0.5, -0.5, 0.5))
        self.__menuContainer.hide()

        self.selectedItem = 0

        self.editing = False
        self.blink = False
        self.blinkTask = None

        for item in items:
            self.addItem(item[0], item[1], item[2])

    def setPos(self, x, y, z):
        self.__menuContainer.setPos(x, y, z)

    def addItem(self, text_name, selection_values, current_selection_idx=-1):
        """
        Adds an item to the list display.
        text_name - The string representation of the list item
        selection_values - A list of available values for selection in this field
        current_selection - The currently selected item
        """

        pos_name = Vec3(-0.47, 0, 0.4 - (len(self._items) * 0.15))
        pos_value = Vec3(1.0, 0, 0.4 - (len(self._items) * 0.15))

        length = len(self._items)

        selection_text = ""
        if current_selection_idx != -1:
            selection_text = str(selection_values[current_selection_idx])

        name_display = DirectButton(
            text=text_name,
            command=None,
            extraArgs=[len(self._items)],
            text_align=TextNode.ALeft,
            scale=0.1,
            #left/right, forward/back, up/down
            pos=pos_name,
            text_fg=(1, 1, 1, 1),
            rolloverSound=None,
            clickSound=None,
            pressEffect=0,
            relief=None,
            textMayChange=True
            #text_font=base.fontLoader.load('Arial Bold.ttf')
        )
        name_display.reparentTo(self.__menuContainer)

        value_display = DirectButton(
            text=str(selection_text),
            command=None,
            extraArgs=[len(self._items)],
            text_align=TextNode.ALeft,
            scale=0.1,
            #left/right, forward/back, up/down
            pos=pos_value,
            text_fg=(1, 1, 1, 1),
            rolloverSound=None,
            clickSound=None,
            pressEffect=0,
            relief=None,
            textMayChange=True
            #text_font=base.fontLoader.load('Arial Bold.ttf')
        )

        value_display.reparentTo(self.__menuContainer)

        v = {}
        v['name'] = text_name
        v['name_display'] = name_display
        v['value_display'] = value_display
        v['selection_values'] = selection_values
        v['current_selection_idx'] = current_selection_idx
        self._items.append(v)

    def up(self):
        """Move one item up in the menu."""

        if self.editing:
            current_item = self._items[self.selectedItem]
            current_item['current_selection_idx'] = (
                current_item['current_selection_idx'] + 1) % len(
                    current_item['selection_values'])
            current_item['value_display']['text'] = current_item[
                'selection_values'][current_item['current_selection_idx']]
        else:

            newItem = self.selectedItem - 1
            if (newItem < 0):
                newItem = len(self._items) - 1
            self.select(newItem)

        UIItem.up(self)

    def down(self):
        """Move one item down in the menu."""
        if self.editing:
            current_item = self._items[self.selectedItem]
            current_item['current_selection_idx'] = (
                current_item['current_selection_idx'] - 1) % len(
                    current_item['selection_values'])
            current_item['value_display']['text'] = current_item[
                'selection_values'][current_item['current_selection_idx']]
        else:

            newItem = self.selectedItem + 1
            if (newItem >= len(self._items)):
                newItem = 0
            self.select(newItem)

        UIItem.down(self)

    def show(self):
        if (self.__menuContainer.isHidden()):
            Sequence(
                Func(self.__menuContainer.setAlphaScale, 0.0),
                Func(self.__menuContainer.show),
                LerpFunctionInterval(self.__menuContainer.setAlphaScale,
                                     toData=1.0,
                                     fromData=0.0,
                                     duration=1.0)).start()

    def hide(self):
        self.ignoreAll()
        if (not self.__menuContainer.isHidden()):
            Sequence(
                LerpFunctionInterval(self.__menuContainer.setAlphaScale,
                                     toData=0.0,
                                     fromData=1.0,
                                     duration=1.0),
                Func(self.__menuContainer.hide),
                Func(self.__menuContainer.setAlphaScale, 1.0)).start()

    def setParent(self, parent):
        self.__menuContainer.reparentTo(parent)

    def select(self, item):
        self._items[self.selectedItem]['name_display']['text_fg'] = (1, 1, 1,
                                                                     1)
        self._items[self.selectedItem]['name_display']['text_bg'] = (0, 0, 0,
                                                                     0)
        self._items[self.selectedItem]['value_display']['text_fg'] = (1, 1, 1,
                                                                      1)
        self._items[self.selectedItem]['value_display']['text_bg'] = (0, 0, 0,
                                                                      0)
        self.selectedItem = item
        self._items[self.selectedItem]['name_display']['text_fg'] = (0, 0, 0.5,
                                                                     1)
        self._items[self.selectedItem]['name_display']['text_bg'] = (1, 1, 1,
                                                                     1)
        self._items[self.selectedItem]['value_display']['text_fg'] = (0, 0,
                                                                      0.5, 1)
        self._items[self.selectedItem]['value_display']['text_bg'] = (1, 1, 1,
                                                                      1)

    def toggleEdit(self):
        self.editing = not self.editing
        if self.editing:
            # Start blinking
            self.blinkTask = base.taskMgr.doMethodLater(
                0.4, self._doBlink, 'nvBlinkTask')
        else:
            # Stop blinking
            if self.blinkTask != None:
                base.taskMgr.remove(self.blinkTask)

    def _doBlink(self, task):
        self.blink = not self.blink
        if self.blink:
            self._items[self.selectedItem]['value_display']['text_fg'] = (0, 0,
                                                                          0, 1)
            self._items[self.selectedItem]['value_display']['text_bg'] = (1, 1,
                                                                          1, 1)
        else:
            self._items[self.selectedItem]['value_display']['text_fg'] = (1, 1,
                                                                          1, 1)
            self._items[self.selectedItem]['value_display']['text_bg'] = (0, 0,
                                                                          0, 0)
        return Task.again
Ejemplo n.º 10
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)