class connectGUI(): def __init__(self, _client): # Ref self.client = _client # create a host button self.btnConnect = DirectButton( # Scale and position scale=0.25, pos=(0, 0, 0), # Text text="Connect", # Frame # Functionality command=self.connect) # create the IP input field self.txtIP = DirectEntry( # scale and position pos=(0, 0, -.35), scale=0.25, width=9, # Text text="", text_align=TextNode.ACenter, initialText="127.0.0.1", numLines=1, # Functionality command=self.connect, focusInCommand=self.clearText) self.hide() def show(self): self.btnConnect.show() self.txtIP.show() def hide(self): self.btnConnect.hide() self.txtIP.hide() def clearText(self): """Function to clear the text that was previously entered in the IP input field""" self.txtIP.enterText("") #TODO: Do something with the ip def connect(self, ip=None): """Function which will be called by pressing the connect button or hit enter while the focus is on the inut field""" if ip == None: ip = self.txtIP.get(True) if ip != "": if self.client.connectionMgr.connectToServer(ip, 5001): gui = lobbyGUI(self.client) self.client.gui.hide() gui.show() else: return
class ThiefHud(GameHud): def __init__(self, clientState): super().__init__(clientState) self.entryLabel = self.label(text='', mayChange=True) self.entry = DirectEntry( initialText='Type card name...', scale=0.05, focus=1, command=self.useThiefAbility, focusInCommand=lambda: self.entry.enterText('')) self.entry.hide() self.thiefAbilityButton = self.button( text="Faction Ability", scale=1, pos=(0, 0, -1), parent=self.endTurnButton, command=self.onThiefAbilityButton) def useThiefAbility(self, cardname): toDiscard = self.toDiscard.getPythonTag('card') toSteal = self.toSteal.getPythonTag('card') base.clientActions.useThiefAbility(toDiscard, toSteal, cardname) base.audioMaster.thiefAbilitySound.play() base.mouseHandler.targeting = False self.entry.hide() def onThiefAbilityButton(self): def chooseTarget(target): if target is None: base.mouseHandler.targeting = False showCard(self.toDiscard) return elif target.getPythonTag('zone') is not base.enemy.facedowns: return self.toSteal = target self.entry.show() def chooseDiscard(target): if target is None: base.mouseHandler.targeting = False return elif target.getPythonTag('zone') is not base.player.hand: return self.toDiscard = target hideCard(target) base.mouseHandler.startTargeting("Choose a target.", chooseTarget) base.mouseHandler.startTargeting("Choose a card to discard.", chooseDiscard) def redraw(self): super().redraw() # TODO: kludge if hasattr(self, 'thiefAbilityButton'): self.thiefAbilityButton.show()
class Entry(DirectObject): def __init__(self, placeholder, pos, on_enter, focus=False, sort_order=0): """ Object of a simple entry field @param placeholder: text to appear in textbox automatically @type placeholder: string @param pos: where to place the textbox @type pos: (float, float, float) @param on_enter: function to call upon them submitting a response @type on_enter: function @param focus: Should the entry auto-focus? @type focus: bool @param sort_order: Where should Entry display? (Alert is at 1000) @type sort_order: int """ DirectObject.__init__(self) self.accept('mouse1', self.click_out) self.placeholder = placeholder self.on_enter = on_enter self.entry = DirectEntry(initialText=self.placeholder, scale=0.05, focus=focus, focusOutCommand=self.focus_out, focusInCommand=self.focus_in, pos=pos, sortOrder=sort_order) def focus_out(self): if self.entry.get() == "": self.entry.enterText(self.placeholder) else: # they typed Something. # TODO validate self.on_enter(self.entry.get()) def focus_in(self): self.entry.set("") def click_out(self): self.entry.setFocus() def destroy(self): self.entry.destroy() self.ignoreAll()
class FileDialog(DirectObject): def __init__(self, title, initial_status, callback): self.frmLeft = -.7 self.frmBottom = -.12 self.frmRight = .7 self.frmTop = .12 self.frmWidth = self.frmRight - self.frmLeft self.frmHeight = self.frmTop - self.frmBottom self.title = title self.initial_status = initial_status self.callback = callback self.result = None self.done = 0 def activate(self): # create main dialog frame # Note: frame position references the CENTER of the frame self.frm = DirectFrame( pos = (0,0,0), frameSize = (self.frmLeft, self.frmRight,self.frmBottom, self.frmTop), relief = DGG.RIDGE, borderWidth = (0.01, 0.01), frameColor = (0.6, 0.6, 0.6, 1.0)) self.frm.reparentTo(base.aspect2d) # Note: wonderfully enough, label position on the other hand # references the CENTER of the LEFT border of the label! titleLabel = DirectLabel(text = self.title, scale = 0.04, frameColor = (0.5, 0.5, 0.5, 1), relief = DGG.RIDGE, borderWidth = (0.01, 0.01), text_align = TextNode.ALeft) titleLabel.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.045) titleLabel.reparentTo(self.frm) sc = .04 self.dir_edit_field = DirectEntry( text = '', # prompt text scale = sc, frameColor = (0.65, 0.65, 0.65, 1), command=self.setText, width = 32, numLines = 1, focus = 1, focusInCommand=self.efFocusIn, ) self.dir_edit_field.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.13) self.dir_edit_field.reparentTo(self.frm) self.statusLabel = DirectLabel(text = self.initial_status, scale = 0.04, frameColor = (0.6, 0.6, 0.6, 1), # relief = DGG.RIDGE, # borderWidth = (0.01, 0.01), text_align = TextNode.ALeft) self.statusLabel.setPos(-self.frmWidth/2+.02, 0, self.frmHeight/2-.2) self.statusLabel.reparentTo(self.frm) # call this if you need to run a form before your mainloop starts steppping the taskMgr def run(self): while self.done != 1: taskMgr.step() taskMgr.step() # once more to make the last output from the dialog visible # while we load a zone self.frm.removeNode() def end(self): self.frm.removeNode() # callback function to set text: called by the Panda3d taskMgr when the user # has entered something into the DirectEntry widget def setText(self, textEntered): print 'dir_edit_field::setText:', textEntered self.done = self.callback(textEntered) if self.done != 1: self.dir_edit_field['focus'] = 1 #clear the text def efFocusIn(self): print 'focus in' self.dir_edit_field.enterText('') def setStatus(self, status): self.statusLabel['text'] = status
class MainMenu(DirectObject): """This class represents the main menu as seen directly after the application has been started""" def __init__(self): # loading music self.menuMusic = loader.loadMusic("music/01Menu.mp3") self.menuMusic.setLoop(True) # create a main frame as big as the window self.frameMain = DirectFrame( # set framesize the same size as the window frameSize = (base.a2dLeft, base.a2dRight, base.a2dTop, base.a2dBottom), # position center pos = (0, 0, 0), # set tramsparent background color frameColor = (0.15, 0.15, 0.15, 1)) #self.frameMain.reparentTo(render2d) self.menuBackground = OnscreenImage( image = 'gui/Background.png', scale = (1.66, 1, 1), pos = (0, 0, 0)) self.menuBackground.reparentTo(self.frameMain) self.defaultBtnMap = base.loader.loadModel("gui/button_map") self.buttonGeom = ( self.defaultBtnMap.find("**/button_ready"), self.defaultBtnMap.find("**/button_click"), self.defaultBtnMap.find("**/button_rollover"), self.defaultBtnMap.find("**/button_disabled")) self.defaultTxtMap = base.loader.loadModel("gui/textbox_map") self.textboxGeom = self.defaultTxtMap.find("**/textbox") monospace = loader.loadFont('gui/DejaVuSansMono.ttf') defaultFont = loader.loadFont('gui/eufm10.ttf') # create the title self.textscale = 0.25 self.title = DirectLabel( # scale and position scale = self.textscale, pos = (0.0, 0.0, base.a2dTop - self.textscale), # frame frameColor = (0, 0, 0, 0), # Text text = "Dungeon Crawler", text_align = TextNode.ACenter, text_fg = (0.82,0.85,0.87,1), text_shadow = (0, 0, 0, 1), text_shadowOffset = (-0.02, -0.02), text_font = defaultFont) self.title.setTransparency(1) self.title.reparentTo(self.frameMain) # create a host button self.btnHostPos = Vec3(0, 0, .45) self.btnHostScale = 0.25 self.btnHost = DirectButton( # Scale and position scale = self.btnHostScale, pos = self.btnHostPos, # Text text = "Host", text_scale = 0.8, text_pos = (0, -0.2), text_fg = (0.82,0.85,0.87,1), text_shadow = (0, 0, 0, 1), text_shadowOffset = (-0.02, -0.02), text_font = defaultFont, # Frame geom = self.buttonGeom, frameColor = (0, 0, 0, 0), relief = 0, pressEffect = False, # Functionality command = self.host, rolloverSound = None, clickSound = None) self.btnHost.setTransparency(1) self.btnHost.reparentTo(self.frameMain) # create a join button self.btnJoinPos = Vec3(0, 0, 0) self.btnJoinScale = 0.25 self.btnJoin = DirectButton( scale = self.btnJoinScale, pos = self.btnJoinPos, text = "Join", text_scale = 0.8, text_pos = (0, -0.2), text_fg = (0.82,0.85,0.87,1), text_shadow = (0, 0, 0, 1), text_shadowOffset = (-0.02, -0.02), text_font = defaultFont, geom = self.buttonGeom, frameColor = (0, 0, 0, 0), relief = 0, pressEffect = False, command = self.join, rolloverSound = None, clickSound = None) self.btnJoin.setTransparency(1) self.btnJoin.reparentTo(self.frameMain) # create the IP input field self.txtIPPos = Vec3(0, 0, -.30) self.txtIPScale = 0.25 self.txtIPWidth = 9 self.txtIP = DirectEntry( # scale and position pos = self.txtIPPos, scale = self.txtIPScale, width = self.txtIPWidth, # Text entryFont = monospace, text_align = TextNode.ACenter, text = "", text_scale = 0.5, text_pos = (0, -0.2), text_fg = (0.82,0.85,0.87,1), text_shadow = (0, 0, 0, 1), text_shadowOffset = (0.04, 0.04), initialText = "127.0.0.1", numLines = 1, # Frame geom = self.textboxGeom, frameColor = (0, 0, 0, 0), relief = 0, # Functionality command = self.join, focusInCommand = self.clearText) self.txtIP.reparentTo(self.frameMain) # create an exit button self.btnExitPos = Vec3(0, 0, -.75) self.btnExitScale = 0.25 self.btnExit = DirectButton( scale = self.btnExitScale, pos = self.btnExitPos, text = "Exit", text_scale = 0.8, text_pos = (0, -0.2), text_fg = (0.82,0.85,0.87,1), text_shadow = (0, 0, 0, 1), text_shadowOffset = (-0.02, -0.02), text_font = defaultFont, geom = self.buttonGeom, frameColor = (0, 0, 0, 0), relief = 0, pressEffect = False, command = lambda: base.messenger.send("escape"), rolloverSound = None, clickSound = None) self.btnExit.setTransparency(1) self.btnExit.reparentTo(self.frameMain) # create a mute checkbox self.cbVolumeMute = DirectCheckBox( # set size scale = (0.1, 0.1, 0.1), frameSize = (-1, 1, 1, -1), # functionality and visuals command = self.cbVolumeMute_CheckedChanged, isChecked = True, checkedImage = "gui/SoundSwitch_off.png", uncheckedImage = "gui/SoundSwitch_on.png", # mouse behaviour relief = 0, pressEffect = False, rolloverSound = None, clickSound = None ) self.cbVolumeMute.setTransparency(1) self.cbVolumeMute.reparentTo(self.frameMain) self.cbVolumeMute.commandFunc(None) # catch window resizes and recalculate the aspectration self.accept("window-event", self.recalcAspectRatio) self.accept("showerror", self.showError) # show the menu right away self.show() def host(self): """Function which will be called by pressing the host button""" self.hide() base.messenger.send("start_server") def join(self, ip=None): """Function which will be called by pressing the join button""" if ip == None: ip = self.txtIP.get(True) if ip == "": return self.hide() base.messenger.send("start_client", [ip]) def showError(self, msg): self.show() self.dialog = OkDialog( dialogName="ErrorDialog", text="Error: {}".format(msg), command=self.closeDialog) def closeDialog(self, args): self.dialog.hide() def show(self): """Show the GUI""" self.frameMain.show() self.menuMusic.play() def hide(self): """Hide the GUI""" self.frameMain.hide() self.menuMusic.stop() def clearText(self): """Function to clear the text that was previously entered in the IP input field""" self.txtIP.enterText("") def cbVolumeMute_CheckedChanged(self, checked): if bool(checked): base.disableAllAudio() else: base.enableAllAudio() def recalcAspectRatio(self, window): """get the new aspect ratio to resize the mainframe""" # set the mainframe size to the window borders again self.frameMain["frameSize"] = ( base.a2dLeft, base.a2dRight, base.a2dTop, base.a2dBottom) # calculate new aspec tratio wp = window.getProperties() aspX = 1.0 aspY = 1.0 wpXSize = wp.getXSize() wpYSize = wp.getYSize() if wpXSize > wpYSize: aspX = wpXSize / float(wpYSize) else: aspY = wpYSize / float(wpXSize) # calculate new position/size/whatever of the gui items self.title.setPos(0.0, 0.0, base.a2dTop - self.textscale) self.menuBackground.setScale(1.0 * aspX, 1.0, 1.0 * aspY) self.cbVolumeMute.setPos(base.a2dRight - 0.15, 0, base.a2dBottom + 0.15)
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()
class DeveloperConsole(InteractiveInterpreter, DirectObject): """The name says it all.""" def __init__(self, xml): sys.stdout = PseudoFile(self.writeOut) sys.stderr = PseudoFile(self.writeErr) tpErr = TextProperties() tpErr.setTextColor(1, 0.5, 0.5, 1) TextPropertiesManager.getGlobalPtr().setProperties("err", tpErr) font = loader.loadFont("cmss12") self.frame = DirectFrame(parent=base.a2dTopCenter, text_align=TextNode.ALeft, text_pos=(-base.getAspectRatio() + TEXT_MARGIN[0], TEXT_MARGIN[1]), text_scale=0.05, text_fg=(1, 1, 1, 1), frameSize=(-2.0, 2.0, -0.5, 0.0), frameColor=(0, 0, 0, 0.5), text='', text_font=font) self.entry = DirectEntry(parent=base.a2dTopLeft, command=self.command, scale=0.05, width=1000.0, pos=(-0.02, 0, -0.48), relief=None, text_pos=(1.5, 0, 0), text_fg=(1, 1, 0.5, 1), rolloverSound=None, clickSound=None, text_font=font) self.otext = OnscreenText(parent=self.entry, scale=1, align=TextNode.ALeft, pos=(1, 0, 0), fg=(1, 1, 0.5, 1), text=':', font=font) self.lines = [''] * 9 self.commands = [] # All previously sent commands self.cscroll = None # Index of currently navigated command, None if current self.command = '' # Currently entered command self.block = '' # Temporarily stores a block of commands self.hide() self.initialized = False self.toggleKeys = manager.controls.registerKeyAll( "Toggle Console", "`", self.toggle, self) def reload(self, xml): pass def prevCommand(self): if self.hidden: return if len(self.commands) == 0: return if self.cscroll == None: self.cscroll = len(self.commands) self.command = self.entry.get() elif self.cscroll <= 0: return else: self.commands[self.cscroll] = self.entry.get() self.cscroll -= 1 self.entry.set(self.commands[self.cscroll]) self.entry.setCursorPosition(len(self.commands[self.cscroll])) def nextCommand(self): if self.hidden: return if len(self.commands) == 0: return if self.cscroll == None: return self.commands[self.cscroll] = self.entry.get() self.cscroll += 1 if self.cscroll >= len(self.commands): self.cscroll = None self.entry.set(self.command) self.entry.setCursorPosition(len(self.command)) else: self.entry.set(self.commands[self.cscroll]) self.entry.setCursorPosition(len(self.commands[self.cscroll])) def writeOut(self, line, copy=True): if copy: sys.__stdout__.write(line) lines = line.split('\n') firstline = lines.pop(0) self.lines[-1] += firstline self.lines += lines self.frame['text'] = '\n'.join(self.lines[-9:]) def writeErr(self, line, copy=True): if copy: sys.__stderr__.write(line) line = '\1err\1%s\2' % line lines = line.split('\n') firstline = lines.pop(0) self.lines[-1] += firstline self.lines += lines self.frame['text'] = '\n'.join(self.lines[-9:]) def command(self, text): if not self.hidden: self.cscroll = None self.command = '' self.entry.set('') self.entry['focus'] = True self.writeOut(self.otext['text'] + ' ' + text + '\n', False) if text != '' and (len(self.commands) == 0 or self.commands[-1] != text): self.commands.append(text) # Insert plugins into the local namespace locals = __main__.__dict__ for plugin in manager.getNamed().keys(): locals[plugin] = manager.getNamed()[plugin] locals['panda3d'] = panda3d #register custom commands locals['reload'] = manager.reload locals['load'] = manager.transition locals['wireframe'] = base.toggleWireframe locals['trigger'] = events.triggerEvent # Run it and print the output. if not self.initialized: InteractiveInterpreter.__init__(self, locals=locals) self.initialized = True try: if self.runsource(self.block + '\n' + text) and text != '': self.otext['text'] = '.' self.block += '\n' + text else: self.otext['text'] = ':' self.block = '' except Exception: # Not just "except", it will also catch SystemExit # Whoops! Print out a traceback. self.writeErr(traceback.format_exc()) def toggle(self): #remove toggle key from entry if self.entry['focus']: for gtype in self.toggleKeys.keys(): key = self.toggleKeys[gtype] entryLen = len(self.entry.get(True)) if self.entry.get(True)[entryLen - len(key):entryLen] == key: self.entry.enterText( self.entry.get(True)[:entryLen - len(key)]) if self.hidden: self.show() else: self.hide() def start(self): return #self.toggle() def stop(self): return #self.toggle() def show(self): self.accept('arrow_up', self.prevCommand) self.accept('arrow_up-repeat', self.prevCommand) self.accept('arrow_down', self.nextCommand) self.accept('arrow_down-repeat', self.nextCommand) self.hidden = False self.entry['focus'] = True self.frame.show() self.entry.show() self.otext.show() def hide(self): self.ignoreAll() self.hidden = True self.entry['focus'] = False self.frame.hide() self.entry.hide() self.otext.hide() def destroy(self): sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ self.ignoreAll() self.frame.destroy() self.entry.destroy() self.otext.destroy()
class PlacerToolSpinner(DirectFrame): def __init__(self, parent=render2d, pos=(0.0, 0.0, 0.0), scale=1.0, value=0, callback=None, increment=0.01): DirectFrame.__init__(self, parent, pos=pos, scale=1.0) self.increment = increment self.value = Decimal(value) self.callback = callback self.display = DirectEntry(parent=self, relief=None, initialText="%.2f" % value, scale=1, text_scale=0.055, text_align=TextNode.ACenter, pos=(0.0, 0.0, 0.0), frameColor=(0.8, 0.8, 0.5, 1), borderWidth=(0.1, 0.1), numLines=1, width=6, frameSize=(-0.1, 0.1, -0.1, 0.1), cursorKeys=1) self.display.bind(DGG.TYPE, self.typeCallback) # This allows the text box to handle mouse events self.display.guiItem.setActive(True) gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui.bam') image = (gui.find('**/tt_t_gui_mat_shuffleArrowUp'), gui.find('**/tt_t_gui_mat_shuffleArrowDown'), gui.find('**/tt_t_gui_mat_shuffleArrowUp'), gui.find('**/tt_t_gui_mat_shuffleArrowDisabled')) self.upArrow = DirectButton(self, relief=None, image=image, image_scale=(0.6, 0.6, 0.6), image1_scale=(0.7, 0.7, 0.7), image2_scale=(0.7, 0.7, 0.7), pos=(0.0, 0.0, 0.08), command=self.__handleUpClicked) self.upArrow.setR(90) self.downArrow = DirectButton(self, relief=None, image=image, image_scale=(0.6, 0.6, 0.6), image1_scale=(0.7, 0.7, 0.7), image2_scale=(0.7, 0.7, 0.7), pos=(0.0, 0.0, -0.05), command=self.__handleDownClicked) self.downArrow.setR(-90) def typeCallback(self, e): if self.display is None: return value = self.display.get() value = re.sub("[^0-9\.-]", "", value) if value == '': value = '000.00' elif value == '-': return if '.' not in value: try: value = int(value) except: return else: try: value = '%.2f' % float(value) except: return self.setValue(value) def setValue(self, value): getcontext().prec = 2 self.value = Decimal(value) self.display.enterText('%.2f' % float(value)) if self.callback: self.callback(self.value) def __handleUpClicked(self): getcontext().prec = 2 self.setValue(float(self.value) + float(self.increment)) def __handleDownClicked(self): getcontext().prec = 2 self.setValue(float(self.value) - float(self.increment))
class FileDialog(DirectObject): def __init__(self, title, initial_status, callback): self.frmLeft = -.7 self.frmBottom = -.12 self.frmRight = .7 self.frmTop = .12 self.frmWidth = self.frmRight - self.frmLeft self.frmHeight = self.frmTop - self.frmBottom self.title = title self.initial_status = initial_status self.callback = callback self.result = None self.done = 0 def activate(self): # create main dialog frame # Note: frame position references the CENTER of the frame self.frm = DirectFrame(pos=(0, 0, 0), frameSize=(self.frmLeft, self.frmRight, self.frmBottom, self.frmTop), relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameColor=(0.6, 0.6, 0.6, 1.0)) self.frm.reparentTo(base.aspect2d) # Note: wonderfully enough, label position on the other hand # references the CENTER of the LEFT border of the label! titleLabel = DirectLabel(text=self.title, scale=0.04, frameColor=(0.5, 0.5, 0.5, 1), relief=DGG.RIDGE, borderWidth=(0.01, 0.01), text_align=TextNode.ALeft) titleLabel.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .045) titleLabel.reparentTo(self.frm) sc = .04 self.dir_edit_field = DirectEntry( text='', # prompt text scale=sc, frameColor=(0.65, 0.65, 0.65, 1), command=self.setText, width=32, numLines=1, focus=1, focusInCommand=self.efFocusIn, ) self.dir_edit_field.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .13) self.dir_edit_field.reparentTo(self.frm) self.statusLabel = DirectLabel( text=self.initial_status, scale=0.04, frameColor=(0.6, 0.6, 0.6, 1), # relief = DGG.RIDGE, # borderWidth = (0.01, 0.01), text_align=TextNode.ALeft) self.statusLabel.setPos(-self.frmWidth / 2 + .02, 0, self.frmHeight / 2 - .2) self.statusLabel.reparentTo(self.frm) # call this if you need to run a form before your mainloop starts steppping the taskMgr def run(self): while self.done != 1: taskMgr.step() taskMgr.step( ) # once more to make the last output from the dialog visible # while we load a zone self.frm.removeNode() def end(self): self.frm.removeNode() # callback function to set text: called by the Panda3d taskMgr when the user # has entered something into the DirectEntry widget def setText(self, textEntered): print 'dir_edit_field::setText:', textEntered self.done = self.callback(textEntered) if self.done != 1: self.dir_edit_field['focus'] = 1 #clear the text def efFocusIn(self): print 'focus in' self.dir_edit_field.enterText('') def setStatus(self, status): self.statusLabel['text'] = status
class Chat(object): """Handles all chat.""" def __init__(self, game): self.game = game self.lines = [] self.whisperTarget = None props = base.win.getProperties() ratio = float(props.getXSize()) / props.getYSize() self.frame = DirectFrame(frameColor=(0, 0, 0, 0), frameSize=(0, 1, 0, 1), pos=(-ratio, 0, -1)) self.text = OnscreenText( text="", pos=(0.01, 0.45), scale=0.05, fg=(1, 1, 1, 1), bg=(0, 0, 0, 0.2), parent=self.frame, align=TextNode.ALeft, mayChange=True, ) self.entry = DirectEntry( parent=self.frame, text="", scale=0.05, pos=(0.01, 0, 0.02), initialText="", width=26, numLines=1 ) self.targetText = OnscreenText( text="", pos=(1.34, 0.025), scale=0.05, fg=(1, 1, 1, 1), bg=(0, 0, 0, 0.4), parent=self.frame, align=TextNode.ALeft, mayChange=True, ) base.accept("t", self.startChatting) base.accept("shift-t", self.startChatting) base.accept("y", self.startWhispering) base.accept("shift-y", self.startWhispering) base.accept("arrow_up", lambda: self.changeTarget(1)) base.accept("shift-arrow_up", lambda: self.changeTarget(1)) base.accept("arrow_down", lambda: self.changeTarget(-1)) base.accept("shift-arrow_down", lambda: self.changeTarget(-1)) base.accept("enter", self.sendChat) base.accept("shift-enter", self.sendChat) def startChatting(self): if not self.game.isChatting: self.game.isChatting = True # focus on the DirectEntry self.entry["focus"] = 1 # reset whisper target (just in case) self.whisperTarget = None # enable UI mode if self.game.camera is not None: self.game.camera.showMouse() def startWhispering(self): if not self.game.isChatting: if not self.game.characters: self.addLine("<<System>> Nobody to whisper!") else: self.startChatting() self.changeTarget(0) def changeTarget(self, amt): if self.game.isChatting: targets = self.game.characters.keys() targets.sort() # find target (or the closest id before target, or 0) index = 0 match = False for i in range(len(targets)): if targets[i] < self.whisperTarget: index = i elif targets[i] == self.whisperTarget: index = i match = True break else: break # if not found but going back 1 anyway, set amt to 0 if not match and amt == -1: amt = 0 # set new whisper target index = (index + amt) % len(targets) self.whisperTarget = targets[index] self.targetText.setText("to: " + str(self.game.characters[self.whisperTarget].name)) def sendChat(self): if self.game.isChatting: message = self.entry.get().strip() if len(message) > 0: # figure out target target = "" if self.whisperTarget in self.game.characters: target = self.game.characters[self.whisperTarget].name # submit message self.game.main.cManager.sendRequest(Constants.C_CHAT, {"message": message, "target": target}) # stop chatting self.stopChatting() def stopChatting(self): if self.game.isChatting: self.game.isChatting = False # disable chat entry self.entry["focus"] = 0 # clear text box self.entry.enterText("") # remove whisper target self.whisperTarget = None self.targetText.setText("") # disable UI mode if self.game.camera is not None: self.game.camera.hideMouse() def addLine(self, line): self.lines.append(line) self.text.setText("\n".join(self.lines[-8:]))
class MainMenu(): def __init__(self, showbase): self.showbase = showbase self.status = OnscreenText(text = "", pos = Vec3(0, -0.35, 0), scale = 0.05, fg = (1, 0, 0, 1), align = TextNode.ACenter, mayChange = True) self.background = DirectFrame( frameSize = (-1, 1, -1, 1), frameTexture = 'media/gui/mainmenu/menu.png', parent = self.showbase.render2d, ) self.title = OnscreenText( text = 'Main Menu', fg = (1, 1, 1, 1), parent = self.background, pos = (-0.6, 0.1), scale = 0.06 ) self.ip = '127.0.0.1' # Should make this write to file... so that the user can save ip's... # yep thats a good idea, there will be a few things i guess that need to be done like this # like settings and keys and whatnot # Buttons self.buttons = [] serverButtons = Vec3(-0.60, 0, -0.79) # Host self.params = ['3', '8'] p = serverButtons + Vec3(-0.25, 0, 0) self.hostButton = DirectButton(text = 'Host', pos = p, scale = 0.048, relief = DGG.GROOVE, command = self.showbase.hostGame, extraArgs = [self.params]) self.buttons.append(self.hostButton) # Join p = serverButtons + Vec3(0.0, 0.0, 0.0) self.joinButton = DirectButton(text = 'Join', pos = p, scale = 0.048, relief = DGG.GROOVE, command = self.joinServer) self.buttons.append(self.joinButton) # Refresh if self.showbase.online: p = serverButtons + Vec3(0.25, 0, 0) self.refreshButton = DirectButton(text = "Refresh", pos = p, scale = 0.048, relief = DGG.GROOVE, command = self.refreshStart) self.buttons.append(self.refreshButton) self.refreshStart() chatFrameCenter = (0.0, 0.325) chatFrameSize = (2.5, 1.2) self.chat = DirectFrame( frameColor = (0, 0, 0, 1), frameSize = (chatFrameSize[0] / 2, -chatFrameSize[0] / 2, chatFrameSize[1] / 2, -chatFrameSize[1] / 2), pos = (chatFrameCenter[0], 0, chatFrameCenter[1]) ) channelFrameSize = (chatFrameSize[0] / 4, chatFrameSize[1]) channelFrameCenter = (- chatFrameSize[0] / 2 + channelFrameSize[0] / 2, 0) numItemsVisible = 8 itemHeight = channelFrameSize[1] / (numItemsVisible + 1) self.channels = DirectScrolledList( parent = self.chat, pos = (channelFrameCenter[0], 0, channelFrameCenter[1]), frameSize = (-channelFrameSize[0] / 2, channelFrameSize[0] / 2, channelFrameSize[1] / 2, -channelFrameSize[1] / 2), frameColor = (1, 0, 0, 0.5), numItemsVisible = numItemsVisible, forceHeight = itemHeight, #itemFrame_frameSize = (-channelFrameSize[0] / 2.1, channelFrameSize[0] / 2.1, itemHeight, -channelFrameSize[1] + itemHeight), itemFrame_pos = (0, 0, channelFrameSize[1] / 2 - itemHeight), decButton_pos = (-0.2, 0, channelFrameCenter[1] - channelFrameSize[1] / 2 + itemHeight / 4), decButton_text = 'Prev', decButton_text_scale = 0.05, decButton_borderWidth = (0.005, 0.005), incButton_pos = (0.2, 0, channelFrameCenter[1] - channelFrameSize[1] / 2 + itemHeight / 4), incButton_text = 'Next', incButton_text_scale = 0.05, incButton_borderWidth = (0.005, 0.005), ) b1 = DirectButton(text = ("Button1", "click!", "roll", "disabled"), text_scale = 0.1, borderWidth = (0.01, 0.01), relief = 2) b2 = DirectButton(text = ("Button2", "click!", "roll", "disabled"), text_scale = 0.1, borderWidth = (0.01, 0.01), relief = 2) l1 = DirectLabel(text = "Test1", text_scale = 0.1) l2 = DirectLabel(text = "Test2", text_scale = 0.1) l3 = DirectLabel(text = "Test3", text_scale = 0.1) self.channels.addItem(b1) self.channels.addItem(b2) self.channels.addItem(l1) self.channels.addItem(l2) self.channels.addItem(l3) for fruit in ['apple', 'pear', 'banana', 'orange', 'cake', 'chocolate']: l = DirectLabel(text = fruit, text_scale = 0.1) self.channels.addItem(l) # need to add the chat stuff # i guess have like a chat manager which will hold an array of 'chat_instances' # and the chat manager can sort out which is displayed and which channel the text is sent from/received to # a bit more thinking needs to be done i guess # can discuss sometime (not really that important now :P) # also i guess the layout and shit will need to be sorted (for whoever is doing the designing) # current games list (need to implement this) # it should send a query to the master server to get the current list (just ip's atmo i guess) # options shit aswell needs to be sorted # maybe just an overlay or something # functionality for the host button (popup an overlay that will be able to set options and then 'start_game' button # then should auto connect and go to lobby (will be same as for all clients, except have a couple of different buttons i guess) # close game instead of disconnect, start game instead of ready (greyed until all others are ready), kick button i suppose # once the options are set the 'server_inst' should be started on the local computer (which will broadcast to master server, once host can successfully connect) # then game client will move to pregame state (connect to the server, and wait for others and ready) else: self.entry = DirectEntry( focusInCommand = self.clearText, frameSize = (-3, 3, -.5, 1), initialText = self.ip, parent = self.buttons[0], pos = (0, -0.6, -1.5), text_align = TextNode.ACenter, ) self.hide() def refreshStart(self): self.refreshButton["state"] = DGG.DISABLED if self.showbase.authCon.getConnected(): self.servers = [] self.showbase.authCon.sendData('serverQuery') self.showbase.taskMgr.doMethodLater(0.25, self.queryServers, 'Server Query') def queryServers(self, task): finished = False temp = self.showbase.authCon.getData() for package in temp: if len(package) == 2: # Handle Server Query if package[0] == 'server': self.servers.append(package[1]) print package[1] elif package[0] == 'final': self.refreshButton["state"] = DGG.NORMAL finished = True else: self.showbase.authCon.passData(package) if finished: return task.done return task.cont def joinChat(self): pass # Let the client mini auth with the lobby server(lobby_loop) by "Logging into the chat" # Since everything will be in the main menu? like a main chat window.. something similar to HoN i guess? # When the player opens the Join game by button, a list will be send from the lobby server telling it what games are active. # Then the player picks one and it connects via the host name/ip or whatever. # While this is busy happening the client stays connected to the lobby server. def joinServer(self): if self.showbase.online: self.ip = '127.0.0.1' else: self.ip = self.entry.get() self.status.setText('Attempting to join server @ ' + self.ip + '...') # attempt to connect to the game server self.showbase.client = Client(self.showbase, self.ip, 9099, compress = True) if self.showbase.client.getConnected(): print 'Connected to server, Awaiting authentication...' self.showbase.client.sendData(('username', self.showbase.username)) self.showbase.taskMgr.add(self.authorizationListener, 'Authorization Listener') else: self.status.setText('Could not Connect...') def authorizationListener(self, task): temp = self.showbase.client.getData() auth = False if temp != []: for package in temp: if len(package) == 2: if package[0] == 'auth': print 'Authentication Successful' auth = True elif package[0] == 'fail': self.status.setText('Authentication failed on server...') return task.done else: self.showbase.client.passData(package) if auth: self.showbase.startPregame() return task.done return task.again def clearText(self): self.entry.enterText('') def hide(self): self.showbase.taskMgr.remove('Server Query Timeout') self.background.hide() for b in self.buttons: b.hide() self.status.hide() if self.showbase.online: self.chat.hide() self.channels.hide() else: self.entry.hide() def show(self): self.background.show() for b in self.buttons: b.show() self.status.show() if self.showbase.online: self.chat.show() self.channels.show() else: self.entry.show()
class MainScreen(): def __init__(self): self.txtPlayerName = DirectEntry( text="", scale=0.08, pos=(-0.15, 0, 0.6), initialText="Name", numLines = 1, width = 4, focus=False, focusInCommand=self.__clearText, focusOutCommand=self.__checkText) self.btnStart = DirectButton( text = "Start", # size of the button scale = (0.25, 0.25, 0.25), # set no relief relief = 1, frameColor = (0,0,0,0), # No sink in when press pressEffect = False, # position on the window pos = (0, 0, .3), # the event which is thrown on clickSound command = self.btnStart_Click, # sounds that should be played rolloverSound = None, clickSound = None) self.btnStart.setTransparency(1) self.btnHighscore = DirectButton( text = "Highscore", # size of the button scale = (0.25, 0.25, 0.25), # set no relief relief = 1, frameColor = (0,0,0,0), # No sink in when press pressEffect = False, # position on the window pos = (0, 0, 0), # the event which is thrown on clickSound command = self.btnHighscore_Click, # sounds that should be played rolloverSound = None, clickSound = None) self.btnHighscore.setTransparency(1) self.btnQuit = DirectButton( text = "Quit", # size of the button scale = (0.25, 0.25, 0.25), # set no relief relief = 1, frameColor = (0,0,0,0), # No sink in when press pressEffect = False, # position on the window pos = (0, 0, -.3), # the event which is thrown on clickSound command = self.btnQuit_Click, # sounds that should be played rolloverSound = None, clickSound = None) self.btnQuit.setTransparency(1) def show(self): self.txtPlayerName.show() self.btnStart.show() self.btnHighscore.show() self.btnQuit.show() def hide(self): self.txtPlayerName.hide() self.btnStart.hide() self.btnHighscore.hide() self.btnQuit.hide() def __clearText(self): if self.txtPlayerName.get() == "" or \ self.txtPlayerName.get() == "Name": self.txtPlayerName.enterText("") def __checkText(self): if self.txtPlayerName.get() == "": self.txtPlayerName.enterText("Name") def btnStart_Click(self): self.hide() base.messenger.send("MainMenu_start") def btnHighscore_Click(self): self.hide() base.messenger.send("Highscore_show") def btnQuit_Click(self): base.messenger.send("MainMenu_quit") def getPlayername(self): return self.txtPlayerName.get()
class MainMenu(): def __init__(self, showbase): self.showbase = showbase self.status = OnscreenText(text="", pos=Vec3(0, -0.35, 0), scale=0.05, fg=(1, 0, 0, 1), align=TextNode.ACenter, mayChange=True) self.background = DirectFrame( frameSize=(-1, 1, -1, 1), frameTexture='media/gui/mainmenu/menu.png', parent=self.showbase.render2d, ) self.title = OnscreenText(text='Main Menu', fg=(1, 1, 1, 1), parent=self.background, pos=(-0.6, 0.1), scale=0.06) self.ip = '127.0.0.1' # Should make this write to file... so that the user can save ip's... # yep thats a good idea, there will be a few things i guess that need to be done like this # like settings and keys and whatnot # Buttons self.buttons = [] serverButtons = Vec3(-0.60, 0, -0.79) # Host self.params = ['3', '8'] p = serverButtons + Vec3(-0.25, 0, 0) self.hostButton = DirectButton(text='Host', pos=p, scale=0.048, relief=DGG.GROOVE, command=self.showbase.hostGame, extraArgs=[self.params]) self.buttons.append(self.hostButton) # Join p = serverButtons + Vec3(0.0, 0.0, 0.0) self.joinButton = DirectButton(text='Join', pos=p, scale=0.048, relief=DGG.GROOVE, command=self.joinServer) self.buttons.append(self.joinButton) # Refresh if self.showbase.online: p = serverButtons + Vec3(0.25, 0, 0) self.refreshButton = DirectButton(text="Refresh", pos=p, scale=0.048, relief=DGG.GROOVE, command=self.refreshStart) self.buttons.append(self.refreshButton) self.refreshStart() chatFrameCenter = (0.0, 0.325) chatFrameSize = (2.5, 1.2) self.chat = DirectFrame( frameColor=(0, 0, 0, 1), frameSize=(chatFrameSize[0] / 2, -chatFrameSize[0] / 2, chatFrameSize[1] / 2, -chatFrameSize[1] / 2), pos=(chatFrameCenter[0], 0, chatFrameCenter[1])) channelFrameSize = (chatFrameSize[0] / 4, chatFrameSize[1]) channelFrameCenter = (-chatFrameSize[0] / 2 + channelFrameSize[0] / 2, 0) numItemsVisible = 8 itemHeight = channelFrameSize[1] / (numItemsVisible + 1) self.channels = DirectScrolledList( parent=self.chat, pos=(channelFrameCenter[0], 0, channelFrameCenter[1]), frameSize=(-channelFrameSize[0] / 2, channelFrameSize[0] / 2, channelFrameSize[1] / 2, -channelFrameSize[1] / 2), frameColor=(1, 0, 0, 0.5), numItemsVisible=numItemsVisible, forceHeight=itemHeight, #itemFrame_frameSize = (-channelFrameSize[0] / 2.1, channelFrameSize[0] / 2.1, itemHeight, -channelFrameSize[1] + itemHeight), itemFrame_pos=(0, 0, channelFrameSize[1] / 2 - itemHeight), decButton_pos=(-0.2, 0, channelFrameCenter[1] - channelFrameSize[1] / 2 + itemHeight / 4), decButton_text='Prev', decButton_text_scale=0.05, decButton_borderWidth=(0.005, 0.005), incButton_pos=(0.2, 0, channelFrameCenter[1] - channelFrameSize[1] / 2 + itemHeight / 4), incButton_text='Next', incButton_text_scale=0.05, incButton_borderWidth=(0.005, 0.005), ) b1 = DirectButton(text=("Button1", "click!", "roll", "disabled"), text_scale=0.1, borderWidth=(0.01, 0.01), relief=2) b2 = DirectButton(text=("Button2", "click!", "roll", "disabled"), text_scale=0.1, borderWidth=(0.01, 0.01), relief=2) l1 = DirectLabel(text="Test1", text_scale=0.1) l2 = DirectLabel(text="Test2", text_scale=0.1) l3 = DirectLabel(text="Test3", text_scale=0.1) self.channels.addItem(b1) self.channels.addItem(b2) self.channels.addItem(l1) self.channels.addItem(l2) self.channels.addItem(l3) for fruit in [ 'apple', 'pear', 'banana', 'orange', 'cake', 'chocolate' ]: l = DirectLabel(text=fruit, text_scale=0.1) self.channels.addItem(l) # need to add the chat stuff # i guess have like a chat manager which will hold an array of 'chat_instances' # and the chat manager can sort out which is displayed and which channel the text is sent from/received to # a bit more thinking needs to be done i guess # can discuss sometime (not really that important now :P) # also i guess the layout and shit will need to be sorted (for whoever is doing the designing) # current games list (need to implement this) # it should send a query to the master server to get the current list (just ip's atmo i guess) # options shit aswell needs to be sorted # maybe just an overlay or something # functionality for the host button (popup an overlay that will be able to set options and then 'start_game' button # then should auto connect and go to lobby (will be same as for all clients, except have a couple of different buttons i guess) # close game instead of disconnect, start game instead of ready (greyed until all others are ready), kick button i suppose # once the options are set the 'server_inst' should be started on the local computer (which will broadcast to master server, once host can successfully connect) # then game client will move to pregame state (connect to the server, and wait for others and ready) else: self.entry = DirectEntry( focusInCommand=self.clearText, frameSize=(-3, 3, -.5, 1), initialText=self.ip, parent=self.buttons[0], pos=(0, -0.6, -1.5), text_align=TextNode.ACenter, ) self.hide() def refreshStart(self): self.refreshButton["state"] = DGG.DISABLED if self.showbase.authCon.getConnected(): self.servers = [] self.showbase.authCon.sendData('serverQuery') self.showbase.taskMgr.doMethodLater(0.25, self.queryServers, 'Server Query') def queryServers(self, task): finished = False temp = self.showbase.authCon.getData() for package in temp: if len(package) == 2: # Handle Server Query if package[0] == 'server': self.servers.append(package[1]) print package[1] elif package[0] == 'final': self.refreshButton["state"] = DGG.NORMAL finished = True else: self.showbase.authCon.passData(package) if finished: return task.done return task.cont def joinChat(self): pass # Let the client mini auth with the lobby server(lobby_loop) by "Logging into the chat" # Since everything will be in the main menu? like a main chat window.. something similar to HoN i guess? # When the player opens the Join game by button, a list will be send from the lobby server telling it what games are active. # Then the player picks one and it connects via the host name/ip or whatever. # While this is busy happening the client stays connected to the lobby server. def joinServer(self): if self.showbase.online: self.ip = '127.0.0.1' else: self.ip = self.entry.get() self.status.setText('Attempting to join server @ ' + self.ip + '...') # attempt to connect to the game server self.showbase.client = Client(self.showbase, self.ip, 9099, compress=True) if self.showbase.client.getConnected(): print 'Connected to server, Awaiting authentication...' self.showbase.client.sendData(('username', self.showbase.username)) self.showbase.taskMgr.add(self.authorizationListener, 'Authorization Listener') else: self.status.setText('Could not Connect...') def authorizationListener(self, task): temp = self.showbase.client.getData() auth = False if temp != []: for package in temp: if len(package) == 2: if package[0] == 'auth': print 'Authentication Successful' auth = True elif package[0] == 'fail': self.status.setText( 'Authentication failed on server...') return task.done else: self.showbase.client.passData(package) if auth: self.showbase.startPregame() return task.done return task.again def clearText(self): self.entry.enterText('') def hide(self): self.showbase.taskMgr.remove('Server Query Timeout') self.background.hide() for b in self.buttons: b.hide() self.status.hide() if self.showbase.online: self.chat.hide() self.channels.hide() else: self.entry.hide() def show(self): self.background.show() for b in self.buttons: b.show() self.status.show() if self.showbase.online: self.chat.show() self.channels.show() else: self.entry.show()
class ColourChannel(DirectObject): __slider_edit_callback: Callable[['ColourChannel', float], None] __entry_edit_callback: Callable[['ColourChannel', float], None] __frame: DirectFrame __label: DirectLabel __slider: DirectSlider __entry: DirectEntry __disable_frame_overlay: DirectButton __enabled: bool def __init__(self, parent: DirectFrame, text: str, value: float, slider_edit_callback: Callable[['ColourChannel', float], None], entry_edit_callback: Callable[['ColourChannel', float], None], mouse1_press_callbacks: List[Callable[[], None]]): self.__frame = DirectFrame(parent=parent) self.__slider_edit_callback = slider_edit_callback self.__entry_edit_callback = entry_edit_callback self.__label = DirectLabel(parent=self.__frame, text=text, text_fg=WHITE, text_bg=WINDOW_BG_COLOUR, pos=(-0.5, 0.0, 0.0), scale=(0.1, 1.0, 0.1)) self.__slider = DirectSlider(parent=self.__frame, orientation=DGG.HORIZONTAL, borderWidth=(0.0, 0.0), frameColor=WIDGET_BG_COLOUR, frameSize=(-1.0, 1.0, -0.4, 0.4), thumb_frameSize=(-0.075, 0.075, -0.2, 0.2), value=value, pos=(0.05, 0.0, 0.0255), scale=(0.45, 1.0, 0.5)) self.__entry_hovered = False mouse1_press_callbacks.append(self.__entry_mouse_click_callback) self.__entry = DirectEntry(parent=self.__frame, frameColor=WIDGET_BG_COLOUR, text_fg=WHITE, initialText=str(value), scale=0.1, width=3, suppressKeys=True, pos=(0.55, 0.0, -0.01105)) self.__entry.bind(DGG.EXIT, self.__entry_exit_callback) self.__entry.bind(DGG.ENTER, self.__entry_enter_callback) self.__entry.bind(DGG.B1PRESS, self.__entry_mouse_click_callback) self.accept("mouse1", self.__entry_mouse_click_callback) self.__disable_frame_overlay = DirectButton(parent=self.__frame, frameColor=TRANSPARENT, borderWidth=(0.0, 0.0), frameSize=(-0.6, 0.9, -0.2, 0.2), suppressMouse=True) self.__disable_frame_overlay.hide() self.__enabled = True self.__set_callbacks() def __entry_exit_callback(self, *discard) -> None: self.__entry_hovered = False def __entry_enter_callback(self, *discard) -> None: self.__entry_hovered = True def __entry_mouse_click_callback(self, *discard) -> None: if self.__entry_hovered: s = self.__entry.get() try: f = float(s) except: return self.__entry_edit_callback(self, f) else: self.__entry['focus'] = False def __unset_callbacks(self): self.__slider['command'] = None self.__entry['command'] = None def __set_callbacks(self): def slider_callback() -> None: self.__slider_edit_callback(self, self.__slider['value']) def entry_callback(s) -> None: try: f = float(s) except: return self.__entry_edit_callback(self, f) self.__slider['command'] = slider_callback self.__entry['command'] = entry_callback @property def frame(self) -> DirectFrame: return self.__frame def update_slider(self, value: float): if not self.__enabled: return self.__unset_callbacks() self.__slider['value'] = value self.__set_callbacks() def update_entry(self, value: float): if not self.__enabled: return self.__unset_callbacks() self.__entry.enterText(f'{value:.3f}') self.__set_callbacks() def update(self, value: float): self.update_slider(value) self.update_entry(value) @property def value(self) -> float: return self.__slider['value'] @property def enabled(self) -> str: return 'enabled' @enabled.setter def enabled(self, enabled: bool) -> None: if self.__enabled == enabled: return self.__enabled = enabled if enabled: self.__disable_frame_overlay.hide() else: self.__disable_frame_overlay.show() @enabled.getter def enabled(self) -> bool: return self.__enabled
class MyApp(ShowBase): def __init__(self): """ Initialize the MyApp class, including defining the GUI objects and layout. Inherits from ShowBase. """ # Because MyApp inherits from ShowBase, ShowBase needs to be initialized also (?) ShowBase.__init__(self) # doesn't disable the mouse, only disables the default setting of the mouse controlling the camera # can't import base and showbase at the same time. showbase is moreimportant right now. #base.disableMouse() # prints the coords of direct gui objects (DGO) when 'P' key is pressed or LMB is clicked self.accept("p", self.getDGOcoords) self.accept("mouse1", self.getDGOcoords) # only catches mouse when clicked on not gui elements >:| # add frmChatBox self.frmChatBox = DirectFrame(frameColor=(0, 255, 0, 1), frameSize=(-0.5, 0.5, -0.25, 0.25), pos=(0, 0, 0)) ### Add some gui objects and parent them to 'frmChatBox'. They move relative to 'frmChatBox', but are positioned relative to aspect2d. WHY?? ### Solution might be to create the frame at 0,0,0 and the DGOs relative to it (currently at 0,0,0) THEN move the frame. ### TODO: get mouse coords on screen to help with sizing DGOs? # add button btnClear = DirectButton(pos=(-0.15,0,-0.95),text = ("Clear", "CLEAR", "Clear", "disabled"), scale=.05, command=self.clearText, parent=self.frmChatBox) # add text entry box self.txtEntry = DirectEntry(text = "",pos=(-0.99,0,-0.95),scale=.05,command=self.setTextBox,initialText="Type Something", numLines = 2, focus=1, parent=self.frmChatBox) # add button btnCommit = DirectButton(pos=(-0.35,0,-0.95),text = ("Commit", "COMMIT", "Commit", "disabled"), scale=.05, command=self.changeText, parent=self.frmChatBox) # define some text to be used in bk_text = "This is my Demo" # add text label, not parented to 'frmChatBox' self.lblLabel = DirectLabel(text = bk_text, pos = (0.95, 0, -0.95), scale = 0.07, text_fg=(1,0.5,0.5,1), text_bg=(0,0,0,1), textMayChange=1) def changeText(self): """ Gets the text from input box "txtEntry" and sets it as the text of label "lblLabel" """ bk_text = self.txtEntry.get() self.lblLabel['text'] = bk_text def setTextBox(self, textEntered): """ Sets the text of label "textObject" to the value passed """ self.textObject['text'] = textEntered def clearText(self): """ Clears the text entry box """ self.txtEntry.enterText('') def getDGOcoords(self): """ Prints the coordinates of DirectGUI objects """ for child in self.frmChatBox.getChildren(): if "Direct" in str(child): print child, " position = ", child.getPos() print child, " scale = ", child.getScale() print "-" * 10
class ChatBox(DirectObject): MESSAGE_LIFE = 10 MAX_NUM_MESSAGES = 15 (TYPE_GLOBAL, TYPE_TEAM, TYPE_CONSOLE) = range(3) messageTypeToPrefix = { TYPE_GLOBAL : 'Global:', TYPE_TEAM : 'Team:', TYPE_CONSOLE : 'Console' } def __init__(self): self.textNodes = [] self.messageType = None self.rootFrame = DirectFrame(pos = (0.03, 0, 0.2), frameColor = (0, 0, 0, 0), frameSize = (0, 1, 0, 1), parent = base.a2dBottomLeft) self.rootFrame.setBin('fixed', GUIOrder.ORDER[GUIOrder.CHAT]) self.entryFrame = DirectFrame(pos = (0, 0, 0), frameColor = (0, 0, 0, 0.1), frameSize = (0, 1, 0, 0.1), parent = self.rootFrame) self.chatarea = DirectEntry(width = 27, scale = Settings.CHAT_HEIGHT, pos = (0, 0, 0), frameColor = (0, 0, 0, 0), text_fg = (1, 1, 1, 1), numLines = 1, cursorKeys = 1, rolloverSound = None, clickSound = None, focus = 0, command = self.OnChatEntered, parent = self.entryFrame) self.typeText = OnscreenText(text = '', pos = (0, Settings.CHAT_HEIGHT + 0.01), scale = Settings.CHAT_HEIGHT, fg = (1, 1, 1, 1), mayChange = True, align = TextNode.ALeft, parent = self.entryFrame) self.displayFrame = DirectFrame(pos = (0, 0, 0.1), frameColor = (0, 0, 0, 0), frameSize = (0, 1, 0, 0.42), parent = self.rootFrame) self.chatarea.enterText('') self.entryFrame.hide() self.chatarea['focus'] = 0 self.chatarea.setFocus() def OnChatEntered(self, enteredText): enteredText = enteredText.strip() self.Hide() if(len(enteredText) > 0): ChatEnteredEvent(self.messageType, enteredText).Fire() def AddMessage(self, prefix, prefixColor, message): parent = self.displayFrame.attachNewNode('messageParent') prefixTextNode = TextNode('prefixMessage') prefixTextNode.setText(prefix) prefixTextNode.setTextColor(prefixColor) prefixTextNode.setShadow(0.05, 0.05) prefixTextNode.setShadowColor(Globals.COLOR_BLACK) prefixTextNodePath = parent.attachNewNode(prefixTextNode) prefixTextNodePath.setScale(Settings.CHAT_HEIGHT) messageTextNode = TextNode('prefixMessage') messageTextNode.setText(message) messageTextNode.setTextColor(1, 1, 1, 1) messageTextNode.setShadow(0.05, 0.05) messageTextNode.setShadowColor(Globals.COLOR_BLACK) messageTextNodePath = parent.attachNewNode(messageTextNode) messageTextNodePath.setScale(Settings.CHAT_HEIGHT) messageTextNodePath.setPos(Vec3(prefixTextNode.calcWidth(prefix) * Settings.CHAT_HEIGHT, 0, 0)) taskMgr.remove('HideMessageLog') taskMgr.doMethodLater(ChatBox.MESSAGE_LIFE, self.HideMessageLog, 'HideMessageLog') self.ShowMessageLog() self.textNodes.append(parent) if(len(self.textNodes) > ChatBox.MAX_NUM_MESSAGES): self.RemoveMessage(self.textNodes[0]) self.RedrawMessages() def RedrawMessages(self): n = len(self.textNodes) for i, textNode in enumerate(self.textNodes): LerpPosInterval(textNode, 0.5, (0, 0, (n-i) * (Settings.CHAT_HEIGHT + 0.01))).start() def RemoveMessage(self, textNode): self.textNodes.remove(textNode) textNode.removeNode() def HideMessageLog(self, task = None): self.displayFrame.hide() def ShowMessageLog(self): self.displayFrame.show() def Hide(self): self.chatarea.enterText('') self.entryFrame.hide() self.chatarea['focus'] = 0 self.chatarea.setFocus() self.HideMessageLog() ChatCloseEvent().Fire() def Show(self, messageType): self.messageType = messageType self.entryFrame.show() self.chatarea['focus'] = 1 self.chatarea.setFocus() self.typeText.setText(ChatBox.GetPrefix(self.messageType)) self.ShowMessageLog() ChatOpenEvent().Fire() def EnableKeyboardListening(self): self.acceptOnce('escape', self.Hide) def DisableKeyboardListening(self): self.ignoreAll() @staticmethod def GetPrefix(messageType): return ChatBox.messageTypeToPrefix[messageType] def Destroy(self): taskMgr.remove('HideMessageLog') self.rootFrame.destroy() self.entryFrame.destroy() self.chatarea.destroy() self.typeText.destroy() self.displayFrame.destroy() self.ignoreAll()
def __call__(self): return self.impl() class _GetchUnix: def __init__(self): import tty, sys, termios # import termios now or else you'll get the Unix version on the Mac def __call__(self): import sys, tty, termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch class _GetchWindows: def __init__(self): import msvcrt def __call__(self): import msvcrt return msvcrt.getch() class _GetchMacCarbon: """ A function which returns the current ASCII key that is down; if no ASCII key is down, the null string is returned. The page http://www.mactech.com/macintosh-c/chap02-1.html was very helpful in figuring out how to do this. """ def __init__(self): import Carbon Carbon.Evt #see if it has this (in Unix, it doesn't) def __call__(self): import Carbon if Carbon.Evt.EventAvail(0x0008)[0]==0: # 0x0008 is the keyDownMask return '' else: # # The event contains the following info: # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1] # # The message (msg) contains the ASCII char which is # extracted with the 0x000000FF charCodeMask; this # number is converted to an ASCII character with chr() and # returned # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1] return chr(msg & 0x000000FF) # ---- character reading from console - END ---- class pandaInteractiveConsole( InteractiveConsole, DirectObject.DirectObject ): def __init__( self, locals, interfaces=GUI|CONSOLE ): print "I: pandaInteractiveConsole.__init__" InteractiveConsole.__init__( self, locals ) self.guiEnabled = (interfaces & GUI) if self.guiEnabled: print "I: - GUI enabled" top = 1/SCALE - (V_SPACE/SCALE) bottom = 1/SCALE - (V_SPACE/SCALE) - (V_SIZE/SCALE) # panda3d interface self.consoleFrame = DirectFrame( relief = DGG.GROOVE , frameColor = (200, 200, 200, 0.85), scale=SCALE , frameSize = (-1/SCALE, 1/SCALE, top, bottom)) self.accept( "f1", self.toggleConsole ) # text entry line self.consoleEntry = DirectEntry( self.consoleFrame, text = "", command=self.setText, width = 1/SCALE*2-2 , pos =(-1/SCALE+1,0,bottom+1), initialText="", numLines = 1, focus=1) # output lines self.consoleOutputList = list() for i in xrange( NUMLINES ): label = OnscreenText( parent = self.consoleFrame, text = "", pos = (-1/SCALE+1, bottom+1+NUMLINES-i) #-1-i) , align=TextNode.ALeft, mayChange=1, scale=1.0) self.consoleOutputList.append( label ) self.toggleConsole() self.consoleEnabled = (interfaces & CONSOLE) self.stdout = sys.stdout if self.consoleEnabled: print "I: - Console enabled" # function to read the keyboard inputs self.readKey = _Getch() # redirect input and output self.stdout = sys.stdout sys.stdout = self if sys.platform == 'linux2' or sys.platform == 'darwin': self.setConsoleNonBlocking( self.stdout ) self.write( "D: myInteractiveConsole.__init__\n" ) # buffer of current text self.linebuffer = '' self.linebufferPos = 0 def setConsoleBlocking( self, fd_in ): fd = fd_in.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) try: fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.FNDELAY) except: pass def setConsoleNonBlocking( self, fd_in ): fd = fd_in.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) try: fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) except: self.write( "D: console init failed\n" ) self.setConsoleBlocking( fd ) ''' # set read non blocking fd = sys.stdin.fileno() self.oldterm = termios.tcgetattr(fd) newattr = self.oldterm self.oldflags = fcntl.fcntl(fd, fcntl.F_GETFL) newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO termios.tcsetattr(fd, termios.TCSANOW, newattr) fcntl.fcntl(fd, fcntl.F_SETFL, self.oldflags | os.O_NONBLOCK) ''' def __del__( self ): if self.consoleEnabled: sys.stdout = self.stdout if sys.platform == 'linux2' or sys.platform == 'darwin': self.write( "setting old terminal settings\n" ) fd = sys.stdin.fileno() termios.tcsetattr(fd, termios.TCSAFLUSH, self.oldterm) fcntl.fcntl(fd, fcntl.F_SETFL, self.oldflags) def toggleConsole( self ): self.consoleFrame.toggleVis() self.consoleEntry['focus'] != self.consoleFrame.isHidden def setText(self, textEntered): self.write( ">%s" % textEntered ) output = self.push( textEntered ) # clear line self.consoleEntry.enterText('') self.consoleEntry['focus'] = 1 def writeP3dConsole( self, printString ): # remove carriage returns (causes disort in output) printString = printString.strip() # remove not printable characters (which can be input by console input) printString = re.sub( r"[^%s]" % string.printable, "", printString ) # only write lines which contain something if printString.strip() != '': while len(printString) > LINELENGTH: writeString = printString[0:LINELENGTH] printString = printString[LINELENGTH:] # output on panda3d interface for i in xrange( NUMLINES-1 ): self.consoleOutputList[i].setText( self.consoleOutputList[i+1].getText() ) self.consoleOutputList[NUMLINES-1].setText( writeString ) # output on panda3d interface for i in xrange( NUMLINES-1 ): self.consoleOutputList[i].setText( self.consoleOutputList[i+1].getText() ) self.consoleOutputList[NUMLINES-1].setText( printString ) def write( self, string ): # write a text the console try: if self.consoleEnabled: # output on console self.stdout.write( string )
class DeveloperConsole(InteractiveInterpreter, DirectObject): """The name says it all.""" def __init__(self, xml): sys.stdout = PseudoFile(self.writeOut) sys.stderr = PseudoFile(self.writeErr) tpErr = TextProperties() tpErr.setTextColor(1, 0.5, 0.5, 1) TextPropertiesManager.getGlobalPtr().setProperties("err", tpErr) font = loader.loadFont("cmss12") self.frame = DirectFrame(parent=base.a2dTopCenter, text_align=TextNode.ALeft, text_pos=(-base.getAspectRatio() + TEXT_MARGIN[0], TEXT_MARGIN[1]), text_scale=0.05, text_fg=(1, 1, 1, 1), frameSize=(-2.0, 2.0, -0.5, 0.0), frameColor=(0, 0, 0, 0.5), text='', text_font=font) self.entry = DirectEntry(parent=base.a2dTopLeft, command=self.command, scale=0.05, width=1000.0, pos=(-0.02, 0, -0.48), relief=None, text_pos=(1.5, 0, 0), text_fg=(1, 1, 0.5, 1), rolloverSound=None, clickSound=None, text_font=font) self.otext = OnscreenText(parent=self.entry, scale=1, align=TextNode.ALeft, pos=(1, 0, 0), fg=(1, 1, 0.5, 1), text=':', font=font) self.lines = [''] * 9 self.commands = [] # All previously sent commands self.cscroll = None # Index of currently navigated command, None if current self.command = '' # Currently entered command self.block = '' # Temporarily stores a block of commands self.hide() self.initialized = False self.toggleKeys = manager.controls.registerKeyAll("Toggle Console", "`", self.toggle, self) def reload(self, xml): pass def prevCommand(self): if self.hidden: return if len(self.commands) == 0: return if self.cscroll == None: self.cscroll = len(self.commands) self.command = self.entry.get() elif self.cscroll <= 0: return else: self.commands[self.cscroll] = self.entry.get() self.cscroll -= 1 self.entry.set(self.commands[self.cscroll]) self.entry.setCursorPosition(len(self.commands[self.cscroll])) def nextCommand(self): if self.hidden: return if len(self.commands) == 0: return if self.cscroll == None: return self.commands[self.cscroll] = self.entry.get() self.cscroll += 1 if self.cscroll >= len(self.commands): self.cscroll = None self.entry.set(self.command) self.entry.setCursorPosition(len(self.command)) else: self.entry.set(self.commands[self.cscroll]) self.entry.setCursorPosition(len(self.commands[self.cscroll])) def writeOut(self, line, copy=True): if copy: sys.__stdout__.write(line) lines = line.split('\n') firstline = lines.pop(0) self.lines[-1] += firstline self.lines += lines self.frame['text'] = '\n'.join(self.lines[-9:]) def writeErr(self, line, copy=True): if copy: sys.__stderr__.write(line) line = '\1err\1%s\2' % line lines = line.split('\n') firstline = lines.pop(0) self.lines[-1] += firstline self.lines += lines self.frame['text'] = '\n'.join(self.lines[-9:]) def command(self, text): if not self.hidden: self.cscroll = None self.command = '' self.entry.set('') self.entry['focus'] = True self.writeOut(self.otext['text'] + ' ' + text + '\n', False) if text != '' and (len(self.commands) == 0 or self.commands[-1] != text): self.commands.append(text) # Insert plugins into the local namespace locals = __main__.__dict__ for plugin in manager.getNamed().keys(): locals[plugin] = manager.getNamed()[plugin] locals['panda3d'] = panda3d #register custom commands locals['reload'] = manager.reload locals['load'] = manager.transition locals['wireframe'] = base.toggleWireframe locals['trigger'] = events.triggerEvent # Run it and print the output. if not self.initialized: InteractiveInterpreter.__init__(self, locals=locals) self.initialized = True try: if self.runsource(self.block + '\n' + text) and text != '': self.otext['text'] = '.' self.block += '\n' + text else: self.otext['text'] = ':' self.block = '' except Exception: # Not just "except", it will also catch SystemExit # Whoops! Print out a traceback. self.writeErr(traceback.format_exc()) def toggle(self): #remove toggle key from entry if self.entry['focus']: for gtype in self.toggleKeys.keys(): key = self.toggleKeys[gtype] entryLen = len(self.entry.get(True)) if self.entry.get(True)[entryLen - len(key):entryLen] == key: self.entry.enterText(self.entry.get(True)[:entryLen - len(key)]) if self.hidden: self.show() else: self.hide() def start(self): return #self.toggle() def stop(self): return #self.toggle() def show(self): self.accept('arrow_up', self.prevCommand) self.accept('arrow_up-repeat', self.prevCommand) self.accept('arrow_down', self.nextCommand) self.accept('arrow_down-repeat', self.nextCommand) self.hidden = False self.entry['focus'] = True self.frame.show() self.entry.show() self.otext.show() def hide(self): self.ignoreAll() self.hidden = True self.entry['focus'] = False self.frame.hide() self.entry.hide() self.otext.hide() def destroy(self): sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ self.ignoreAll() self.frame.destroy() self.entry.destroy() self.otext.destroy()
class Login(object): def __init__(self, main): self.main = main self.frame = None self.title = None self.error = None self.username = None self.userTextbox = None self.password = None self.passTextbox = None self.cpassword = None self.cpassTextbox = None self.submitBtn = None self.registerBtn = None self.cancelBtn = None # login screen def createLoginWindow(self): self.frame = DirectFrame(frameColor = (0, 0, 0, 1), frameSize = (-1.35, 1.35, -1, 1), pos = (0, 0, 0)) self.title = OnscreenText(text = 'Login', pos = (0, 0.3), scale = 0.15, fg = (1, 0.5, 0.5, 1), align = TextNode.ACenter, parent = self.frame, mayChange = False) # username self.username = OnscreenText(text = 'Username:'******'', scale = 0.05, pos = (0.1, 0, 0), initialText = '[username]', numLines = 1, focus = 1, focusInCommand = self.userTextFocus, focusOutCommand = self.userTextBlur) # password self.password = OnscreenText(text = 'Password:'******'', scale = 0.05, pos = (0.1, 0, -0.2), initialText = '[password]', numLines = 1, focus = 0, focusInCommand = self.passTextFocus, focusOutCommand = self.passTextBlur) # buttons self.submitBtn = DirectButton(self.frame, # parent text = 'Login', scale = 0.08, command = self.clickedSubmit, pos = (0.8, 0.0, -0.90), text_bg = (1, 0.5, 0.5, 1), relief = None) self.registerBtn = DirectButton(self.frame, # parent text = 'Register', scale = 0.08, command = self.clickedRegister, pos = (0.5, 0.0, -0.90), text_bg = (1, 0.5, 0.5, 1), relief = None) self.cancelBtn = DirectButton(self.frame, # parent text = 'Cancel', scale = 0.08, command = self.clickedCancel, pos = (0.2, 0.0, -0.90), text_bg = (1, 0.5, 0.5, 1), relief = None) def destroyLoginWindow(self): self.frame.destroy() self.frame = None self.title = None self.error = None self.username = None self.userTextbox = None self.password = None self.passTextbox = None self.submitBtn = None self.registerBtn = None self.cancelBtn = None # register screen def createRegisterWindow(self): self.frame = DirectFrame(frameColor = (0, 0, 0, 1), frameSize = (-1.35, 1.35, -1, 1), pos = (0, 0, 0)) self.title = OnscreenText(text = 'Register', pos = (0, 0.3), scale = 0.15, fg = (1, 0.5, 0.5, 1), align = TextNode.ACenter, parent = self.frame, mayChange = False) # username self.username = OnscreenText(text = 'Username:'******'', scale = 0.05, pos = (0.1, 0, 0), initialText = '[username]', numLines = 1, focus = 1, focusInCommand = self.userTextFocus, focusOutCommand = self.userTextBlur) # password self.password = OnscreenText(text = 'Password:'******'', scale = 0.05, pos = (0.1, 0, -0.2), initialText = '[password]', numLines = 1, focus = 0, focusInCommand = self.passTextFocus, focusOutCommand = self.passTextBlur) # confirm password self.cpassword = OnscreenText(text = 'Confirm Password:'******'', scale = 0.05, pos = (0.1, 0, -0.4), initialText = '[confirm password]', numLines = 1, focus = 0, focusInCommand = self.cpassTextFocus, focusOutCommand = self.cpassTextBlur) # buttons self.registerBtn = DirectButton(self.frame, # parent text = 'Register', scale = 0.08, command = self.clickedRegRegister, pos = (0.8, 0.0, -0.90), text_bg = (1, 0.5, 0.5, 1), relief = None) self.cancelBtn = DirectButton(self.frame, # parent text = 'Cancel', scale = 0.08, command = self.clickedRegCancel, pos=(0.2, 0.0, -0.90), text_bg = (1, 0.5, 0.5, 1), relief = None) def destroyRegisterWindow(self): self.frame.destroy() self.frame = None self.title = None self.error = None self.username = None self.userTextbox = None self.password = None self.passTextbox = None self.cpassword = None self.cpassTextbox = None self.registerBtn = None self.cancelBtn = None # error message helper def displayError(self, msg): if self.frame is None: return if self.error is not None: self.error.destroy() self.error = OnscreenText(text = msg, pos = (0, -0.65), scale = 0.06, fg = (1, 0.5, 0.5, 1), align = TextNode.ACenter, parent = self.frame, mayChange = False) # userTextbox handlers def userTextFocus(self): if self.userTextbox.get() == '[username]': self.userTextbox.enterText('') def userTextBlur(self): if self.userTextbox.get().strip() == '': self.userTextbox.enterText('[username]') # passTextbox handlers def passTextFocus(self): if self.passTextbox.get() == '[password]': self.passTextbox.enterText('') def passTextBlur(self): if self.passTextbox.get().strip() == '': self.passTextbox.enterText('[password]') # cpassTextbox handlers def cpassTextFocus(self): if self.cpassTextbox.get() == '[confirm password]': self.cpassTextbox.enterText('') def cpassTextBlur(self): if self.cpassTextbox.get().strip() == '': self.cpassTextbox.enterText('[confirm password]') # login button handlers def clickedSubmit(self): username = self.userTextbox.get().strip() password = self.passTextbox.get().strip() if username != '' and password != '': self.main.cManager.sendRequest(Constants.C_AUTH, {'username': username, 'password': password}) else: self.displayError('Please enter a username and password.') def clickedRegister(self): self.destroyLoginWindow() self.createRegisterWindow() def clickedCancel(self): exit() # register button handlers def clickedRegRegister(self): username = self.userTextbox.get().strip() password = self.passTextbox.get().strip() cpass = self.cpassTextbox.get().strip() if username != '': if password != '': if cpass == password: self.main.cManager.sendRequest(Constants.C_REGISTER, {'username': username, 'password': password}) self.destroyRegisterWindow() self.createLoginWindow() else: self.displayError('Your passwords do not match.') else: self.displayError('Please enter a password.') else: self.displayError('Please enter a username.') def clickedRegCancel(self): self.destroyRegisterWindow() self.createLoginWindow()
class MyApp(ShowBase): def __init__(self): """ Initialize the MyApp class, including defining the GUI objects and layout. Inherits from ShowBase. """ # Because MyApp inherits from ShowBase, ShowBase needs to be initialized also (?) ShowBase.__init__(self) # doesn't disable the mouse, only disables the default setting of the mouse controlling the camera # can't import base and showbase at the same time. showbase is moreimportant right now. #base.disableMouse() # prints the coords of direct gui objects (DGO) when 'P' key is pressed or LMB is clicked self.accept("p", self.getDGOcoords) self.accept( "mouse1", self.getDGOcoords ) # only catches mouse when clicked on not gui elements >:| # add frmChatBox self.frmChatBox = DirectFrame(frameColor=(0, 255, 0, 1), frameSize=(-0.5, 0.5, -0.25, 0.25), pos=(0, 0, 0)) ### Add some gui objects and parent them to 'frmChatBox'. They move relative to 'frmChatBox', but are positioned relative to aspect2d. WHY?? ### Solution might be to create the frame at 0,0,0 and the DGOs relative to it (currently at 0,0,0) THEN move the frame. ### TODO: get mouse coords on screen to help with sizing DGOs? # add button btnClear = DirectButton(pos=(-0.15, 0, -0.95), text=("Clear", "CLEAR", "Clear", "disabled"), scale=.05, command=self.clearText, parent=self.frmChatBox) # add text entry box self.txtEntry = DirectEntry(text="", pos=(-0.99, 0, -0.95), scale=.05, command=self.setTextBox, initialText="Type Something", numLines=2, focus=1, parent=self.frmChatBox) # add button btnCommit = DirectButton(pos=(-0.35, 0, -0.95), text=("Commit", "COMMIT", "Commit", "disabled"), scale=.05, command=self.changeText, parent=self.frmChatBox) # define some text to be used in bk_text = "This is my Demo" # add text label, not parented to 'frmChatBox' self.lblLabel = DirectLabel(text=bk_text, pos=(0.95, 0, -0.95), scale=0.07, text_fg=(1, 0.5, 0.5, 1), text_bg=(0, 0, 0, 1), textMayChange=1) def changeText(self): """ Gets the text from input box "txtEntry" and sets it as the text of label "lblLabel" """ bk_text = self.txtEntry.get() self.lblLabel['text'] = bk_text def setTextBox(self, textEntered): """ Sets the text of label "textObject" to the value passed """ self.textObject['text'] = textEntered def clearText(self): """ Clears the text entry box """ self.txtEntry.enterText('') def getDGOcoords(self): """ Prints the coordinates of DirectGUI objects """ for child in self.frmChatBox.getChildren(): if "Direct" in str(child): print child, " position = ", child.getPos() print child, " scale = ", child.getScale() print "-" * 10
class GuiConsole(DirectObject.DirectObject): def __init__(self, class_parent, parent, h_size, v_size, aspect, hugpos): self.h_size = h_size self.v_size = v_size self.scale = 0.04 self.parent = class_parent self.numlines = int(v_size / self.scale - 2) self.pos_min_x = 0 self.pos_min_y = 0 self.pos_max_x = self.h_size self.pos_max_y = 0.7 if aspect > 0: self.pos_max_x /= aspect else: self.pos_max_y *= aspect self.consoleFrame = DirectFrame(relief=DGG.RIDGE, frameColor=(0, 0, 0, 0), scale=self.scale, frameSize=(0, self.h_size / self.scale, 0, self.v_size / self.scale)) if parent == base.a2dBottomLeft: #@UndefinedVariable self.pos_min_x -= 1 self.pos_min_y -= 1 self.pos_max_x -= 1 self.pos_max_y -= 1 if hugpos == "bottom": self.consoleFrame.setPos(0, 0, GUI_BOTTOM_OFFSET - 0.085) self.pos_min_x = 0 self.pos_min_y = GUI_BOTTOM_OFFSET - 0.085 - 0.07 self.pos_max_x = self.h_size self.pos_max_y = GUI_BOTTOM_OFFSET - 0.085 fixedWidthFont = loader.loadFont(GUI_FONT) #@UndefinedVariable #fixedWidthFont.setPixelsPerUnit(60) #fixedWidthFont.setRenderMode(fixedWidthFont.RMSolid) if not fixedWidthFont.isValid(): print "pandaInteractiveConsole.py :: could not load the defined font %s" % str( self.font) fixedWidthFont = DGG.getDefaultFont() #fixedWidthFont.setPageSize(512,512) #fixedWidthFont.setPixelsPerUnit(60) self.consoleEntry = DirectEntry( self.consoleFrame, text="", command=self.onEnterPress #, width = self.h_size/self.scale -2 , pos=(0.01, 0, 0.02), initialText="Enter text...", numLines=1, focus=0, entryFont=fixedWidthFont, scale=1, frameColor=(0, 0, 0, 0.2), text_fg=(0, 1, 0, 1), text_shadow=(0, 0, 0, 1)) #self.consoleEntry = DirectEntry(self.consoleFrame) self.consoleEntry["frameSize"] = (0, self.h_size / self.scale, 0, 1) self.consoleEntry["width"] = self.h_size / self.scale self.consoleEntry["focusInCommand"] = self.focusInCallback self.consoleEntry["focusOutCommand"] = self.focusOutCallback self.consoleFrame.reparentTo(parent) self.textBuffer = list() self.textBufferLength = 100 for i in xrange(self.textBufferLength): self.textBuffer.append(['', (100, 100, 100, 1)]) self.textBufferPos = self.textBufferLength - self.numlines # output lines self.consoleOutputList = list() for i in xrange(self.numlines): label = OnscreenText(parent=self.consoleFrame, text="", pos=(0, i + 1.5), align=TextNode.ALeft, mayChange=1, scale=1.0, fg=(100, 100, 100, 1), shadow=(0, 0, 0, 1)) # , frame = (200,0,0,1) ) label.setFont(fixedWidthFont) self.consoleOutputList.append(label) self.linelength = 57 self.linewrap = textwrap.TextWrapper() self.linewrap.width = self.linelength self.toggleConsole() def focusInCallback(self): self.parent.parent.camera_manager.disableKeyMovement() def focusOutCallback(self): self.parent.parent.camera_manager.enableKeyMovement() def show(self): if self.consoleFrame.isHidden(): self.consoleFrame.toggleVis() def hide(self): pass #if not self.consoleFrame.isHidden(): # self.consoleFrame.toggleVis() def toggleConsole(self): self.consoleFrame.toggleVis() hidden = self.consoleFrame.isHidden() #self.consoleEntry['focus'] != hidden if hidden: #self.ignoreAll() self.accept('control', self.toggleConsole) self.accept('enter', self.manageFocus) self.accept('escape', self.unfocus) else: #self.ignoreAll() #self.accept( 'page_up', self.scroll, [-5] ) #self.accept( 'page_up-repeat', self.scroll, [-5] ) #self.accept( 'page_down', self.scroll, [5] ) #self.accept( 'page_down-repeat', self.scroll, [5] ) #self.accept( 'window-event', self.windowEvent) #self.accept( 'arrow_up' , self.scrollCmd, [ 1] ) #self.accept( 'arrow_down', self.scrollCmd, [-1] ) self.accept('control', self.toggleConsole) self.accept('enter', self.manageFocus) self.accept('escape', self.unfocus) #self.accept( self.autocomplete_key, self.autocomplete ) #self.accept( self.autohelp_key, self.autohelp ) # accept v, c and x, where c & x copy's the whole console text #messenger.toggleVerbose() #for osx use ('meta') #if sys.platform == 'darwin': # self.accept( 'meta', self.unfocus ) # self.accept( 'meta-up', self.focus ) # self.accept( 'meta-c', self.copy ) # self.accept( 'meta-x', self.cut ) # self.accept( 'meta-v', self.paste ) #for windows use ('control') #if sys.platform == 'win32' or sys.platform == 'linux2': # self.accept( 'control', self.unfocus ) # self.accept( 'control-up', self.focus ) # self.accept( 'control-c', self.copy ) # self.accept( 'control-x', self.cut ) # self.accept( 'control-v', self.paste ) def onEnterPress(self, textEntered): # set to last message self.textBufferPos = self.textBufferLength - self.numlines # clear line self.consoleEntry.enterText('') self.consoleOutput(textEntered, utils.CONSOLE_PLAYER1_TEXT) ClientMsg.chat(textEntered) self.focus() def manageFocus(self): if self.consoleFrame.isHidden(): self.consoleFrame.toggleVis() if self.consoleEntry["focus"] == 0: self.focus() def consoleOutput(self, printString, msgType): if msgType == utils.CONSOLE_SYSTEM_ERROR: self.write(printString, utils.CONSOLE_SYSTEM_ERROR_TEXT_COLOR) elif msgType == utils.CONSOLE_SYSTEM_MESSAGE: self.write(printString, utils.CONSOLE_SYSTEM_MESSAGE_TEXT_COLOR) elif msgType == utils.CONSOLE_PLAYER1_TEXT: self.write(printString, utils.CONSOLE_PLAYER1_TEXT_COLOR) else: self.write(printString, utils.CONSOLE_PLAYER2_TEXT_COLOR) def write(self, printString, color=(100, 100, 100, 0.5)): # remove not printable characters (which can be input by console input) printString = re.sub(r'[^%s]' % re.escape(string.printable[:95]), "", printString) splitLines = self.linewrap.wrap(printString) for line in splitLines: self.textBuffer.append([line, color]) self.textBuffer.pop(0) self.updateOutput() def updateOutput(self): for lineNumber in xrange(self.numlines): lineText, color = self.textBuffer[lineNumber + self.textBufferPos] self.consoleOutputList[self.numlines - lineNumber - 1].setText(lineText) self.consoleOutputList[self.numlines - lineNumber - 1]['fg'] = color def focus(self): self.consoleEntry['focus'] = 1 def unfocus(self): self.consoleEntry['focus'] = 0 def getTightBounds(self): l, r, b, t = self.consoleFrame.getBounds() print l, r, b, t bottom_left = Point3(l, 0, b) top_right = Point3(r, 0, t) return (bottom_left, top_right)
class ThiefHud(GameHud): def __init__(self): super().__init__() self.entryLabel = self.label(text='', mayChange=True) self.entry = DirectEntry( initialText='Type card name...', scale=0.05, focus=1, command=self.useThiefAbility, focusInCommand=lambda: self.entry.enterText('')) self.entry.hide() self.thiefAbilityButton = self.button( text="Faction Ability", scale=1, pos=(0, 0, -1), parent=self.endPhaseButton, command=self.onThiefAbilityButton) def useThiefAbility(self, cardname): try: cardId = next(c for c in base.enemy.deck if c.name == cardname).cardId except StopIteration: print("That is not the name of an enemy card.") else: toDiscardIndex = base.player.hand.index( self.toDiscard.getPythonTag('card')) toStealIndex = base.enemy.facedowns.index( self.toSteal.getPythonTag('card')) base.networkManager.useThiefAbility(toDiscardIndex, cardId, toStealIndex) base.mouseHandler.targeting = False self.entry.hide() def onThiefAbilityButton(self): def chooseTarget(target): if target is None: base.mouseHandler.targeting = False showCard(self.toDiscard) return elif target.getPythonTag('zone') is not base.enemy.facedowns: return self.toSteal = target self.entry.show() def chooseDiscard(target): if target is None: base.mouseHandler.targeting = False return elif target.getPythonTag('zone') is not base.player.hand: return self.toDiscard = target hideCard(target) base.mouseHandler.startTargeting("Choose a target.", chooseTarget) base.mouseHandler.startTargeting("Choose a card to discard.", chooseDiscard) def redraw(self): super().redraw() # TODO: kludge if hasattr(self, 'thiefAbilityButton'): if base.game.phase == Phase.startOfTurn: self.thiefAbilityButton.show() else: self.thiefAbilityButton.hide()
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 )
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)