Python port of Falagard demo (console window with history)

Projects using CEGUI or any kind of implementation revolving around CEGUI - Post it here!
Showing off screenshots and giving a short description is highly encouraged!

Moderators: CEGUI MVP, CEGUI Team

User avatar
Leftium
Just popping in
Just popping in
Posts: 12
Joined: Sat Feb 05, 2011 19:48
Contact:

Python port of Falagard demo (console window with history)

Postby Leftium » Sat Feb 12, 2011 18:28

I have completed a Python port of the Falagard demo, a basic console window with history. The added ease of rapid development with PyCEGUI is more than worth the minor 10% performance hit! It is nearly identical to the native version, with some enhancements:
  • Fixed keyboard input so it behaves as expected. (UP/DOWN arrow keys work when edit box is active; F12 always toggles the console regardless of input focus).
  • KeyDown + KeyUp events are always injected in addition to Char events whenever possible.
  • Added some utility functions to easily inspect PyCEGUI objects and conditionally log to console.
  • Some other minor bugs fixed.
Coming soon:
  • Immediate mode Python from (PyCEGUI) console, so PyCEGUI functions can be tested interactively
  • Allegro5 + D3D/OpenGL renderer
About the script:
  • Based on C++ source code of native samples. I tried to maintain the same variable names and comments for easy diff comparisons.
  • Released as one large file for convenience, but logically broken into 4 or 5 files (one file per class + utilities).
  • It is simple to start creating your own PyCEGUI apps in no time by subclassing one of the base classes!
  • Currently only works with Python 2.6 due to an issue with the PyCEGUI bindings.
The code:

Code: Select all

# based on sample from http://www.cegui.org.uk/wiki/index.php/PyCEGUI
import os, sys
import new

# you must have PyCEGUI python package installed (see pypi repository)
# or you must make it work yourself using binary bindings from SDK
from PyCEGUI import *

class PyCeguiBaseApplication(object):
    def __init__(self):
        # TODO: figure out smart default
        self.CEGUI_SAMPLE_DATAPATH = 'C:/Python26/Lib/site-packages/PyCEGUI'
        self.DATAPATH_VAR_NAME = 'CEGUI_SAMPLE_DATAPATH'

    def getDataPathPrefix(self):
        # set data path prefix / base directory.  This will
        # be either from an environment variable, or based on
        # the PyCEGUI library path
        if self.DATAPATH_VAR_NAME in os.environ:
            return os.environ[self.DATAPATH_VAR_NAME]
        else:
            return self.CEGUI_SAMPLE_DATAPATH

    def initialiseResourceGroupDirectories(self):
        # initialise the required dirs for the DefaultResourceProvider
        rp = System.getSingleton().getResourceProvider()

        dataPathPrefix = self.getDataPathPrefix()

        # for each resource type, set a resource group directory
        rp.setResourceGroupDirectory('schemes'
                ,dataPathPrefix + '/datafiles/schemes')
        rp.setResourceGroupDirectory('imagesets'
                ,dataPathPrefix + '/datafiles/imagesets')
        rp.setResourceGroupDirectory('fonts'
                ,dataPathPrefix + '/datafiles/fonts')
        rp.setResourceGroupDirectory('layouts'
                ,dataPathPrefix + '/datafiles/layouts')
        rp.setResourceGroupDirectory('looknfeels'
                ,dataPathPrefix + '/datafiles/looknfeel')
        rp.setResourceGroupDirectory('schemas'
                ,dataPathPrefix + '/datafiles/xml_schemas')
        rp.setResourceGroupDirectory('animations'
                ,dataPathPrefix + '/datafiles/animations')

    def initialiseDefaultResourceGroups(self):
        # set the default resource groups to be used
        Imageset.setDefaultResourceGroup('imagesets')
        Font.setDefaultResourceGroup('fonts')
        Scheme.setDefaultResourceGroup('schemes')
        WidgetLookManager.setDefaultResourceGroup('looknfeels')
        WindowManager.setDefaultResourceGroup('layouts')
        AnimationManager.setDefaultResourceGroup('animations');

        # setup default group for validation schemas
        parser = System.getSingleton().getXMLParser()
        if parser.isPropertyPresent('SchemaDefaultResourceGroup'):
            parser.setProperty('SchemaDefaultResourceGroup', 'schemas')


import string

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import PyCEGUIOpenGLRenderer
class PyCeguiGlutBaseApplication(PyCeguiBaseApplication):
    specialKeyMap = {
            GLUT_KEY_F1: Key.F1
           ,GLUT_KEY_F2: Key.F2
           ,GLUT_KEY_F3: Key.F3
           ,GLUT_KEY_F4: Key.F4
           ,GLUT_KEY_F5: Key.F5
           ,GLUT_KEY_F6: Key.F6
           ,GLUT_KEY_F7: Key.F7
           ,GLUT_KEY_F8: Key.F8
           ,GLUT_KEY_F9: Key.F9
           ,GLUT_KEY_F10: Key.F10
           ,GLUT_KEY_F11: Key.F11
           ,GLUT_KEY_F12: Key.F12
           ,GLUT_KEY_LEFT: Key.ArrowLeft
           ,GLUT_KEY_UP: Key.ArrowUp
           ,GLUT_KEY_RIGHT: Key.ArrowRight
           ,GLUT_KEY_DOWN: Key.ArrowDown
           ,GLUT_KEY_PAGE_UP: Key.PageUp
           ,GLUT_KEY_PAGE_DOWN: Key.PageDown
           ,GLUT_KEY_HOME: Key.Home
           ,GLUT_KEY_END: Key.End
           ,GLUT_KEY_INSERT: Key.Insert}

    def init_static_data(self):
        self.quitFlag = False
        self.lastFrameTime = 0
        self.glut_modifiers = dict(
                shift=dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_SHIFT
                          ,scancode=Key.LeftShift)
               ,ctrl =dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_CTRL
                          ,scancode=Key.LeftControl)
               ,alt  =dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_ALT
                          ,scancode=Key.LeftAlt))
        self.fps_lastTime = 0
        self.fps_frames = 0
        self.fps_textbuff = ''
        self.logo_geometry = None

    def __init__(self, x=0, y=0, width=800, height=600
            ,title='PyCEGUI GLUT Base Application'):
        PyCeguiBaseApplication.__init__(self)

        self.init_static_data()
        self.root = None
        self.rot = 0

        # Do GLUT init
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGBA)
        glutInitWindowSize(width, height)
        glutInitWindowPosition(x, y)
        glutCreateWindow(title)
        glutSetCursor(GLUT_CURSOR_NONE)

        self.renderer = PyCEGUIOpenGLRenderer.OpenGLRenderer.bootstrapSystem()

        glutDisplayFunc(self.drawFrame)
        glutReshapeFunc(self.reshape)

        glutMotionFunc(self.mouseMotion)
        glutPassiveMotionFunc(self.mouseMotion)
        glutMouseFunc(self.mouseButton)

        glutKeyboardFunc(self.keyChar)
        glutSpecialFunc(self.keySpecial)

        glutKeyboardUpFunc(self.keyCharUp)
        glutSpecialUpFunc(self.keySpecialUp)

        # Set the clear color
        glClearColor(0.0, 0.0, 0.0, 1.0)

        self.initialiseResourceGroupDirectories()
        self.initialiseDefaultResourceGroups()

        # setup required to do direct rendering of FPS value
        scrn = Rect(Vector2(0,0), self.renderer.getDisplaySize())
        self.fps_geometry = self.renderer.createGeometryBuffer()
        self.fps_geometry.setClippingRegion(scrn)

        # setup for logo
        ImagesetManager.getSingleton().createFromImageFile(
                'cegui_logo', 'logo.png', 'imagesets')
        self.logo_geometry = self.renderer.createGeometryBuffer()
        self.logo_geometry.setClippingRegion(scrn)
        self.logo_geometry.setPivot(Vector3(50, 34.75, 0))
        self.logo_geometry.setTranslation(Vector3(10, 520, 0))
        ImagesetManager.getSingleton().get('cegui_logo').getImage(
             'full_image').draw(self.logo_geometry ,Rect(0,0, 100, 69.5) ,None)

        # clearing this queue actually makes sure it's created(!)
        self.renderer.getDefaultRenderingRoot().clearGeometry(RQ_OVERLAY)

        # subscribe handler to render overlay items
        self.renderer.getDefaultRenderingRoot().subscribeEvent(
              RenderingSurface.EventRenderQueueStarted, self, 'overlayHandler')

    def __del__(self):
        PyCEGUIOpenGLRenderer.OpenGLRenderer.destroySystem()

    def overlayHandler(self, args):
        # queueID attribute missing in CEGUI Python bindings
        # if not args.queueID == RQ_OVERLAY:
        #     return False

        # render FPS:
        fnt = System.getSingleton().getDefaultFont()
        if fnt:
            self.fps_geometry.reset()
            fnt.drawText(self.fps_geometry, self.fps_textbuff, Vector2(0, 0)
                    ,None, colour(0xFFFFFFFF))
            self.fps_geometry.draw()
        self.logo_geometry.draw()
        return True

    def execute(self, sampleApp):
        sampleApp.initialiseSample()

        # set starting time
        self.fps_lastTime = self.lastFrameTime = glutGet(GLUT_ELAPSED_TIME)

        glutMainLoop()

        return True

    def drawFrame(self):
        guiSystem = System.getSingleton()
        # do time based updates
        thisTime = glutGet(GLUT_ELAPSED_TIME)
        elapsed = thisTime - self.lastFrameTime
        self.lastFrameTime = thisTime
        # inject the time pulse
        guiSystem.injectTimePulse(elapsed / 1000.0)
        # update fps fields
        self.doFPSUpdate()

        # update logo rotation
        self.logo_geometry.setRotation(Vector3(self.rot, 0, 0))
        self.rot += (180.0 * elapsed) / 1000.0
        if self.rot > 360.0:
            self.rot -= 360.0

        # do rendering for this frame.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        guiSystem.renderGUI()
        glutPostRedisplay()
        glutSwapBuffers()

        # here we check the 'quitting' state and cleanup as required.
        # this is probably not the best way to do this, but since we're
        # using glut, and glutMainLoop can never return, we need some
        # way of checking when to exit.  And this is it...
        if self.quitFlag:
            PyCEGUIOpenGLRenderer.OpenGLRenderer.destroySystem()
            exit(0)

    def reshape(self, w, h):
        glViewport(0, 0, w, h)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(60.0, float(w)/h, 1.0, 50.0)
        glMatrixMode(GL_MODELVIEW)
        System.getSingleton().notifyDisplaySizeChanged(Size(w, h))

    def mouseMotion(self, x, y):
        System.getSingleton().injectMousePosition(x, y)

    def mouseButton(self, button, state, x, y):
        if button == GLUT_LEFT_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(LeftButton)
            else:
                System.getSingleton().injectMouseButtonDown(LeftButton)

        elif button == GLUT_RIGHT_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(RightButton)
            else:
                System.getSingleton().injectMouseButtonDown(RightButton)

        elif button == GLUT_MIDDLE_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(MiddleButton)
            else:
                System.getSingleton().injectMouseButtonDown(MiddleButton)

    def keyChar(self, key, x, y):
        key = key.encode('ascii', 'ignore')
        self.handleModifierKeys()

        scancode = self.ascii_to_scancode(key)
        if scancode:
            custom_args = KeyEventArgs(None)
            custom_args.scancode = scancode
            logi('BEFORE keyChar.fireEvent: %s' %
                    pretty_attr(custom_args ,'custom_args'))
            System.getSingleton().fireEvent('AppKeyDown', custom_args)
            logc('AFTER  keyChar.fireEvent: %s \n' %
                    pretty_attr(custom_args ,'custom_args'))

            log('INJECT KEYDN: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.down')
            result = System.getSingleton().injectKeyDown(int(scancode))
            log('RESULT injectKeyDown: %s' % result, 'result')

        log('INJECT CHAR: [%3d]' % ord(key), 'inject.char')
        result = System.getSingleton().injectChar(ord(key))
        log('RESULT injectChar: %s' % result, 'result')
        if scancode == Key.Escape:
            self.quitFlag = True

        return False

    def keyCharUp(self, key, x, y):
        key = key.encode('ascii', 'ignore')
        self.handleModifierKeys()

        scancode = self.ascii_to_scancode(key)
        if scancode:
            log('INJECT KEYUP: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.up')
            System.getSingleton().injectKeyUp(int(scancode))
        return False

    def keySpecial(self, key, x, y):
        self.handleModifierKeys()
        scancode = self.special_to_scancode(key)
        if scancode:
            custom_args = KeyEventArgs(None)
            custom_args.scancode = scancode
            logi('BEFORE keySpecial.fireevent: %s' %
                    pretty_attr(custom_args ,'custom_args'))

            GlobalEventSet.getSingleton().fireEvent(
                    'AppKeyDown', custom_args)

            logc('AFTER  keySpecial.fireevent: %s \n' %
                    pretty_attr(custom_args ,'custom_args'))

            log('INJECT KEYDN: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.down')
            result = System.getSingleton().injectKeyDown(int(scancode))
            log('RESULT injectKeyDown: %s' % result, 'result')
        return False

    def keySpecialUp(self, key, x, y):
        self.handleModifierKeys()
        scancode = self.special_to_scancode(key)
        if scancode:
            log('INJECT KEYUP: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.up')
            System.getSingleton().injectKeyUp(int(scancode))
        return False

    def handleModifierKeys(self):
        status = glutGetModifiers()

        for name, key in self.glut_modifiers.items():
            if (status & key['bit_flag']):
                if not key['is_held']:
                    key['is_held'] = True
                    log('INJECT MODDN: %s' % key['scancode'], 'inject.key.down')
                    System.getSingleton().injectKeyDown(key['scancode'])
            else:
                if key['is_held']:
                    key['is_held'] = False
                    log('INJECT MODUP: %s' % key['scancode'], 'inject.key.up')
                    System.getSingleton().injectKeyUp(key['scancode'])

    def doFPSUpdate(self):
        # another frame
        self.fps_frames += 1

        # has at least a second passed since we last updated the text?
        if (self.lastFrameTime - self.fps_lastTime >= 1000):
            # update FPS text to output
            self.fps_textbuff = 'FPS: %d' % self.fps_frames
            self.fps_frames = 0
            self.fps_lastTime = self.lastFrameTime

    mapping = dict([(ord(c), getattr(Key, c.upper()))
                   for c in string.ascii_letters])
    mapping.update({
                 96: Key.Grave,        126: Key.Grave,
                 49: Key.One,           33: Key.One,
                 50: Key.Two,           64: Key.At,
                 51: Key.Three,         35: Key.Three,
                 52: Key.Four,          36: Key.Four,
                 53: Key.Five,          37: Key.Five,
                 54: Key.Six,           94: Key.Six,
                 55: Key.Seven,         38: Key.Seven,
                 56: Key.Eight,         42: Key.Multiply,
                 57: Key.Nine,          40: Key.Nine,
                 48: Key.Zero,          41: Key.Zero,
                 45: Key.Minus,         95: Key.Underline,
                 61: Key.Equals,        43: Key.Equals,
                 91: Key.LeftBracket,  123: Key.LeftBracket,
                 93: Key.RightBracket, 125: Key.RightBracket,
                 59: Key.Semicolon,     58: Key.Colon,
                 39: Key.Apostrophe,    34: Key.Apostrophe,
                 92: Key.Backslash,    124: Key.Backslash,
                 44: Key.Comma,         60: Key.Comma,
                 46: Key.Period,        62: Key.Period,
                 47: Key.Slash,         63: Key.Slash,
                 13: Key.Return,
                  8: Key.Backspace,
                  9: Key.Tab,
                 32: Key.Space,
                127: Key.Delete,
                 27: Key.Escape})

    def ascii_to_scancode(self, a):
        a = ord(a)
        return self.mapping[a] if (a in self.mapping) else 0

    def special_to_scancode(self, c):
        return self.specialKeyMap[c] if (c in self.specialKeyMap) else 0

    def modifier_keys_state(self):
        s = ''
        if self.glut_modifiers['ctrl']['is_held']:
            s += 'CTRL '
        if self.glut_modifiers['alt']['is_held']:
            s += 'ALT '
        if self.glut_modifiers['shift']['is_held']:
            s += 'SHIFT '
        return s



class PyCeguiSample(object):
    def run(self):
        if self.initialise:
            self.initialise()
            self.cleanup()
        return 0

    def initialise(self):
        self.sampleApp = PyCeguiGlutBaseApplication(x=0, y=0
                , width=800, height=600 ,title='PyCEGUI Falagard Demo')

        # execute the base application (which sets
        # up the demo via 'self' and runs it.
        if self.sampleApp.execute(self):
            # signal that app initialised and ran
            return True

        self.sampleApp = None

        # signal app did not initialise and run.
        return False

    def cleanup(self):
        if self.sampleApp:
            self.sampleApp = None



class FalagardDemo1Sample(PyCeguiSample):
    def __init__(self):
        self.console = None

    def initialiseSample(self):
        # Get window manager which we wil use for a few jobs here.
        winMgr = WindowManager.getSingleton()
        # Load the scheme to initialse the VanillaSkin which we use
        # in this sample
        SchemeManager.getSingleton().create('VanillaSkin.scheme')
        # set default mouse image
        System.getSingleton().setDefaultMouseCursor('Vanilla-Images'
                ,'MouseArrow')

        # load an image to use as a background
        ImagesetManager.getSingleton().createFromImageFile('BackgroundImage'
                ,'GPN-2000-001437.tga')

        # here we will use a StaticImage as the root,
        # then we can use it to place a background image
        background = winMgr.createWindow('Vanilla/StaticImage')

        # set area rectangle
        background.setArea(URect(cegui_reldim(0) ,cegui_reldim(0)
                                ,cegui_reldim(1) ,cegui_reldim(1)))

        # disable frame and standard background
        background.setProperty('FrameEnabled', 'false')
        background.setProperty('BackgroundEnabled', 'false')
        #set the background image
        background.setProperty('Image', 'set:BackgroundImage image:full_image')

        # install this as the root GUI sheet
        System.getSingleton().setGUISheet(background)

        FontManager.getSingleton().create('DejaVuSans-10.font')

        # load some demo windows and attach to the background 'root'
        background.addChildWindow(
                winMgr.loadWindowLayout('VanillaWindows.layout'))

        # create an instance of the console class.
        self.console = DemoConsole('Demo')

        # listen for key presses on the root window.
        GlobalEventSet.getSingleton().subscribeEvent('/AppKeyDown'
                ,self, 'handleRootKeydown')

        # activate the background window
        background.activate()

        # success!
        return True

    def cleanupSample(self):
        self.console = None

    def handleRootKeydown(self, keyArgs):
        if keyArgs.scancode == Key.F12:
            self.console.toggleVisibility()
            logi('BEFORE handleRootKeydown: %s' %
                  pretty_attr(keyArgs, 'WindowEventArgs'))
            keyArgs.handled += 1
            logc('AFTER  handleRootKeydown: %s' %
                  pretty_attr(keyArgs, 'WindowEventArgs'))
            return True
        return False



class DemoConsole(object):
    def __init__(self, name, parent=None):
        # these must match the IDs assigned in the layout
        self.submitButtonID = 1;
        self.entryBoxID     = 2;
        self.historyID      = 3;

        window_manager = WindowManager.getSingleton()
        self.console_root = window_manager.loadWindowLayout(
                'VanillaConsole.layout', name)
        self.history_pos = 0
        self.history = []



        # we will destroy the console box windows ourselves
        self.console_root.setDestroyedByParent(False)

        # Do events wire-up
        GlobalEventSet.getSingleton().subscribeEvent('/AppKeyDown',
                self, 'handleKeydown')
        self.console_root.getChild(self.submitButtonID).subscribeEvent(
                PushButton.EventClicked, self, 'handleSubmit')
        self.console_root.getChild(self.entryBoxID).subscribeEvent(
                Editbox.EventTextAccepted, self, 'handleSubmit')

        # decide where to attach the console main window
        parent = parent or System.getSingleton().getGUISheet()

        # attach this window if parent is valid
        if parent:
            parent.addChildWindow(self.console_root)

    def __del__(self):
        # destroy the windows that we loaded earlier
        WindowManager.getSingleton().destroyWindow(self.console_root)

    def toggleVisibility(self):
        self.console_root.hide() if self.console_root.isVisible(True
                ) else self.console_root.show()

    def handleSubmit(self, WindowEventArgs):
        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)
        # get text out of the editbox
        edit_text = editbox.getText()
        # if the string is not empty
        if edit_text:
            # add this entry to the command history buffer
            self.history.append(edit_text)
            # reset history position
            self.history_pos = len(self.history)
            # append newline to this entry
            edit_text += '\n'
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)
            # erase text in text entry box.
            editbox.setText('')

        # re-activate the text entry box
        editbox.activate()
        return True

    def handleKeydown(self, keyEventArgs):
        # if no input focus, ignore events
        if not self.console_root.getActiveChild():
            return False

        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)

        if keyEventArgs.scancode == Key.ArrowUp:
            self.history_pos = max(self.history_pos - 1, -1)
            if self.history_pos >= 0:
                editbox.setText(self.history[self.history_pos])
                editbox.setCaratIndex(sys.maxint)
            else:
                editbox.setText('')
            editbox.activate()

        elif keyEventArgs.scancode == Key.ArrowDown:
            self.history_pos = min(self.history_pos + 1, len(self.history))
            if self.history_pos < len(self.history):
                editbox.setText(self.history[self.history_pos])
                editbox.setCaratIndex(sys.maxint)
            else:
                editbox.setText('')
            editbox.activate()
        else:
            return False

        logi('BEFORE handleKeyDown: %s' %
                pretty_attr(keyEventArgs, 'keyEventArgs'))
        keyEventArgs.handled += 100
        logc('AFTER  handleKeyDown: %s' %
                pretty_attr(keyEventArgs, 'keyEventArgs'))
        return True

# macro to aid in the creation of UDims (copied from CEGUIUDim.h)
def cegui_reldim(x):
    return UDim(x, 0)


def pretty_attr(py_object, label, interesting_classes='default'):
    if interesting_classes == 'default':
        interesting_classes = pretty_attr.default_classes

    def _pretty_attr(py_object, label, interesting_classes, indent='.'):
        if isinstance(py_object, pretty_attr.instancemethodType):
            return label + ' [instancemethod]'
        elif interesting_classes and isinstance(py_object, interesting_classes):
            result = ''
            for attribute_name in dir(py_object):
                if not attribute_name.startswith('__'):
                    attribute = getattr(py_object, attribute_name)
                    result += indent + _pretty_attr(attribute
                                                   ,attribute_name + ' = '
                                                   ,interesting_classes
                                                   ,indent + '.') + '\n'
            return label + str(py_object) + '\n' + result
        # TODO: add support for lists/tuples/dicts
        else:
            # just print simple string for uninteresting objects
            return label + str(py_object)

    return _pretty_attr(py_object, label + ' = ', interesting_classes)[:-1]

pretty_attr.instancemethodType = type(
            new.instancemethod(pretty_attr, None, object))

pretty_attr.default_classes = (
        KeyEventArgs
       ,WindowEventArgs)


import fnmatch;
def block_indent(ss):
    return ss.replace('\n', '\n' +  log.indent)


def log(message, message_class='default', indent=None):
    if indent is None:
        indent = log.indent_level * ' '
    for wildcard_pattern in log.classes:
        if fnmatch.fnmatch(message_class, wildcard_pattern):
            print block_indent(indent + message)

def logi(message, message_class='default'):
    log(message, message_class, log.indent_level * ' ')
    log.indent_level += 2
    log.indent = ' ' * log.indent_level

def logc(message, message_class='default'):
    log.indent_level  = log.indent_level - 2
    log.indent = ' ' * log.indent_level
    log(message, message_class, log.indent_level * ' ')

log.indent_level = 0
log.indent = ''
log.classes = (
        'default'
       #,'inject*'
       #,'result'
       ,)

app = FalagardDemo1Sample()
app.run()
del app


User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: Python port of Falagard demo (console window with histor

Postby CrazyEddie » Wed Feb 16, 2011 08:08

Thanks for posting this. I really am interested in what people can achieve now that CEGUI is so accessible via Python - it's like the dawn of a new age :lol:

CE.

User avatar
Leftium
Just popping in
Just popping in
Posts: 12
Joined: Sat Feb 05, 2011 19:48
Contact:

Re: Python port of Falagard demo (console window with histor

Postby Leftium » Tue Jul 26, 2011 02:43

OK, here is an initial version of the Falagard console with immediate mode Python. You can try PyCEGUI commands and see the results right away! Although it needs to be made a little more robust and easier to edit, it's already proven useful!

Class PyConsole simply sub-classes the original DemoConsole class from above to add the interactive Python interpreter:

Code: Select all

import code
class PyConsole(DemoConsole):
    def __init__(self, name, parent):
        DemoConsole.__init__(self, name=name, parent=parent)

        sys.stdout = self
        # sys.stderr = self

        self.inter = code.InteractiveInterpreter(globals())

    def write(self, edit_text):
        if edit_text and edit_text != '\n':
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)

    def handleSubmit(self, WindowEventArgs):
        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)
        # get text out of the editbox
        edit_text = editbox.getText()
        # if the string is not empty
        if edit_text:
            # add this entry to the command history buffer
            self.history.append(edit_text)
            # reset history position
            self.history_pos = len(self.history)
            # append newline to this entry
            # edit_text += '\n'
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)
            # erase text in text entry box.
            editbox.setText('')

            self.inter.runsource(edit_text)


For convenience, all the code in one file. You can directly run this script:

Code: Select all

# based on sample from http://www.cegui.org.uk/wiki/index.php/PyCEGUI
import os, sys
import new
import string

# you must have PyCEGUI python package installed (see pypi repository)
# or you must make it work yourself using binary bindings from SDK
from PyCEGUI import *

class PyCeguiBaseApplication(object):
    def __init__(self):
        # TODO: figure out smart default
        self.CEGUI_SAMPLE_DATAPATH = './'
        self.DATAPATH_VAR_NAME = 'CEGUI_SAMPLE_DATAPATH'

    def baseDataPath(self):
        # CEGUI data base path prefix / base directory.  This will be either
        # from an environment variable or based on the PyCEGUI library path
        if self.DATAPATH_VAR_NAME in os.environ:
            return os.environ[self.DATAPATH_VAR_NAME]
        else:
            return self.CEGUI_SAMPLE_DATAPATH

    def initialiseResourceGroupDirectories(self):
        # initialise the required dirs for the DefaultResourceProvider
        rp = System.getSingleton().getResourceProvider()

        basePath = self.baseDataPath() + 'datafiles/'

        # for each resource type, set a resource group directory
        rp.setResourceGroupDirectory('schemes', basePath + 'schemes')
        rp.setResourceGroupDirectory('imagesets', basePath + 'imagesets')
        rp.setResourceGroupDirectory('fonts', basePath + 'fonts')
        rp.setResourceGroupDirectory('layouts', basePath + 'layouts')
        rp.setResourceGroupDirectory('looknfeels', basePath + 'looknfeel')
        rp.setResourceGroupDirectory('schemas', basePath + 'xml_schemas')
        rp.setResourceGroupDirectory('animations', basePath + 'animations')

    def initialiseDefaultResourceGroups(self):
        # set the default resource groups to be used
        Imageset.setDefaultResourceGroup('imagesets')
        Font.setDefaultResourceGroup('fonts')
        Scheme.setDefaultResourceGroup('schemes')
        WidgetLookManager.setDefaultResourceGroup('looknfeels')
        WindowManager.setDefaultResourceGroup('layouts')
        AnimationManager.setDefaultResourceGroup('animations')

        # set up default group for validation schemas
        parser = System.getSingleton().getXMLParser()
        if parser.isPropertyPresent('SchemaDefaultResourceGroup'):
            parser.setProperty('SchemaDefaultResourceGroup', 'schemas')


from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import PyCEGUIOpenGLRenderer
class PyCeguiGlutBaseApplication(PyCeguiBaseApplication):
    specialKeyMap = {
            GLUT_KEY_F1: Key.F1
           ,GLUT_KEY_F2: Key.F2
           ,GLUT_KEY_F3: Key.F3
           ,GLUT_KEY_F4: Key.F4
           ,GLUT_KEY_F5: Key.F5
           ,GLUT_KEY_F6: Key.F6
           ,GLUT_KEY_F7: Key.F7
           ,GLUT_KEY_F8: Key.F8
           ,GLUT_KEY_F9: Key.F9
           ,GLUT_KEY_F10: Key.F10
           ,GLUT_KEY_F11: Key.F11
           ,GLUT_KEY_F12: Key.F12
           ,GLUT_KEY_LEFT: Key.ArrowLeft
           ,GLUT_KEY_UP: Key.ArrowUp
           ,GLUT_KEY_RIGHT: Key.ArrowRight
           ,GLUT_KEY_DOWN: Key.ArrowDown
           ,GLUT_KEY_PAGE_UP: Key.PageUp
           ,GLUT_KEY_PAGE_DOWN: Key.PageDown
           ,GLUT_KEY_HOME: Key.Home
           ,GLUT_KEY_END: Key.End
           ,GLUT_KEY_INSERT: Key.Insert}

    def init_static_data(self):
        self.quitFlag = False
        self.lastFrameTime = 0
        self.glut_modifiers = dict(
                shift=dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_SHIFT
                          ,scancode=Key.LeftShift)
               ,ctrl =dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_CTRL
                          ,scancode=Key.LeftControl)
               ,alt  =dict(is_held=False
                          ,bit_flag=GLUT_ACTIVE_ALT
                          ,scancode=Key.LeftAlt))
        self.fps_lastTime = 0
        self.fps_frames = 0
        self.fps_textbuff = ''
        self.logo_geometry = None

    def __init__(self, x=0, y=0, width=800, height=600
            ,title='PyCEGUI GLUT Base Application'):
        PyCeguiBaseApplication.__init__(self)

        self.init_static_data()
        self.root = None
        self.rot = 0

        # Do GLUT init
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGBA)
        glutInitWindowSize(width, height)
        glutInitWindowPosition(x, y)
        glutCreateWindow(title)
        glutSetCursor(GLUT_CURSOR_NONE)

        self.renderer = PyCEGUIOpenGLRenderer.OpenGLRenderer.bootstrapSystem()

        glutDisplayFunc(self.drawFrame)
        glutReshapeFunc(self.reshape)

        glutMotionFunc(self.mouseMotion)
        glutPassiveMotionFunc(self.mouseMotion)
        glutMouseFunc(self.mouseButton)

        glutKeyboardFunc(self.keyChar)
        glutSpecialFunc(self.keySpecial)

        glutKeyboardUpFunc(self.keyCharUp)
        glutSpecialUpFunc(self.keySpecialUp)

        # Set the clear color
        glClearColor(0.0, 0.0, 0.0, 1.0)

        self.initialiseResourceGroupDirectories()
        self.initialiseDefaultResourceGroups()

        # setup required to do direct rendering of FPS value
        scrn = Rect(Vector2(0,0), self.renderer.getDisplaySize())
        self.fps_geometry = self.renderer.createGeometryBuffer()
        self.fps_geometry.setClippingRegion(scrn)

        # setup for logo
        ImagesetManager.getSingleton().createFromImageFile(
                'cegui_logo', 'logo.png', 'imagesets')
        self.logo_geometry = self.renderer.createGeometryBuffer()
        self.logo_geometry.setClippingRegion(scrn)
        self.logo_geometry.setPivot(Vector3(50, 34.75, 0))
        self.logo_geometry.setTranslation(Vector3(10, 520, 0))
        ImagesetManager.getSingleton().get('cegui_logo').getImage(
             'full_image').draw(self.logo_geometry ,Rect(0,0, 100, 69.5) ,None)

        # clearing this queue actually makes sure it's created(!)
        self.renderer.getDefaultRenderingRoot().clearGeometry(RQ_OVERLAY)

        # subscribe handler to render overlay items
        self.renderer.getDefaultRenderingRoot().subscribeEvent(
              RenderingSurface.EventRenderQueueStarted, self, 'overlayHandler')

    def __del__(self):
        PyCEGUIOpenGLRenderer.OpenGLRenderer.destroySystem()

    def overlayHandler(self, args):
        # queueID attribute missing in CEGUI Python bindings
        # if not args.queueID == RQ_OVERLAY:
        #     return False

        # render FPS:
        fnt = System.getSingleton().getDefaultFont()
        if fnt:
            self.fps_geometry.reset()
            fnt.drawText(self.fps_geometry, self.fps_textbuff, Vector2(0, 0)
                    ,None, colour(0xFFFFFFFF))
            self.fps_geometry.draw()
        self.logo_geometry.draw()
        return True

    def execute(self, sampleApp):
        sampleApp.initialiseSample()

        # set starting time
        self.fps_lastTime = self.lastFrameTime = glutGet(GLUT_ELAPSED_TIME)

        glutMainLoop()

        return True

    def drawFrame(self):
        guiSystem = System.getSingleton()
        # do time based updates
        thisTime = glutGet(GLUT_ELAPSED_TIME)
        elapsed = thisTime - self.lastFrameTime
        self.lastFrameTime = thisTime
        # inject the time pulse
        guiSystem.injectTimePulse(elapsed / 1000.0)
        # update fps fields
        self.doFPSUpdate()

        # update logo rotation
        self.logo_geometry.setRotation(Vector3(self.rot, 0, 0))
        self.rot += (180.0 * elapsed) / 1000.0
        if self.rot > 360.0:
            self.rot -= 360.0

        # do rendering for this frame.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        guiSystem.renderGUI()
        glutPostRedisplay()
        glutSwapBuffers()

        # here we check the 'quitting' state and cleanup as required.
        # this is probably not the best way to do this, but since we're
        # using glut, and glutMainLoop can never return, we need some
        # way of checking when to exit.  And this is it...
        if self.quitFlag:
            PyCEGUIOpenGLRenderer.OpenGLRenderer.destroySystem()
            exit(0)

    def reshape(self, w, h):
            glViewport(0, 0, w, h)
            glMatrixMode(GL_PROJECTION)
            glLoadIdentity()
            gluPerspective(60.0, float(w)/h, 1.0, 50.0)
            glMatrixMode(GL_MODELVIEW)
            System.getSingleton().notifyDisplaySizeChanged(Size(w, h))

    def mouseMotion(self, x, y):
        System.getSingleton().injectMousePosition(x, y)

    def mouseButton(self, button, state, x, y):
        if button == GLUT_LEFT_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(LeftButton)
            else:
                System.getSingleton().injectMouseButtonDown(LeftButton)

        elif button == GLUT_RIGHT_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(RightButton)
            else:
                System.getSingleton().injectMouseButtonDown(RightButton)

        elif button == GLUT_MIDDLE_BUTTON:
            if state == GLUT_UP:
                System.getSingleton().injectMouseButtonUp(MiddleButton)
            else:
                System.getSingleton().injectMouseButtonDown(MiddleButton)

    def keyChar(self, key, x, y):
        key = key.encode('ascii', 'ignore')
        self.handleModifierKeys()

        scancode = self.ascii_to_scancode(key)
        if scancode:
            custom_args = KeyEventArgs(None)
            custom_args.scancode = scancode
            logi('BEFORE keyChar.fireEvent: %s' %
                    pretty_attr(custom_args ,'custom_args'))
            System.getSingleton().fireEvent('AppKeyDown', custom_args)
            logc('AFTER  keyChar.fireEvent: %s \n' %
                    pretty_attr(custom_args ,'custom_args'))

            log('INJECT KEYDN: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.down')
            result = System.getSingleton().injectKeyDown(int(scancode))
            log('RESULT injectKeyDown: %s' % result, 'result')

        log('INJECT CHAR: [%3d]' % ord(key), 'inject.char')
        result = System.getSingleton().injectChar(ord(key))
        log('RESULT injectChar: %s' % result, 'result')
        if scancode == Key.Escape:
            self.quitFlag = True

        return False

    def keyCharUp(self, key, x, y):
        key = key.encode('ascii', 'ignore')
        self.handleModifierKeys()

        scancode = self.ascii_to_scancode(key)
        if scancode:
            log('INJECT KEYUP: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.up')
            System.getSingleton().injectKeyUp(int(scancode))
        return False

    def keySpecial(self, key, x, y):
        self.handleModifierKeys()
        scancode = self.special_to_scancode(key)
        if scancode:
            custom_args = KeyEventArgs(None)
            custom_args.scancode = scancode
            logi('BEFORE keySpecial.fireevent: %s' %
                    pretty_attr(custom_args ,'custom_args'))

            GlobalEventSet.getSingleton().fireEvent(
                    'AppKeyDown', custom_args)

            logc('AFTER  keySpecial.fireevent: %s \n' %
                    pretty_attr(custom_args ,'custom_args'))

            log('INJECT KEYDN: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.down')
            result = System.getSingleton().injectKeyDown(int(scancode))
            log('RESULT injectKeyDown: %s' % result, 'result')
        return False

    def keySpecialUp(self, key, x, y):
        self.handleModifierKeys()
        scancode = self.special_to_scancode(key)
        if scancode:
            log('INJECT KEYUP: %s %s' % (scancode, self.modifier_keys_state())
                    ,'inject.key.up')
            System.getSingleton().injectKeyUp(int(scancode))
        return False

    def handleModifierKeys(self):
        status = glutGetModifiers()

        for name, key in self.glut_modifiers.items():
            if (status & key['bit_flag']):
                if not key['is_held']:
                    key['is_held'] = True
                    log('INJECT MODDN: %s' % key['scancode'], 'inject.key.down')
                    System.getSingleton().injectKeyDown(key['scancode'])
            else:
                if key['is_held']:
                    key['is_held'] = False
                    log('INJECT MODUP: %s' % key['scancode'], 'inject.key.up')
                    System.getSingleton().injectKeyUp(key['scancode'])

    def doFPSUpdate(self):
        # another frame
        self.fps_frames += 1

        # has at least a second passed since we last updated the text?
        if (self.lastFrameTime - self.fps_lastTime >= 1000):
            # update FPS text to output
            self.fps_textbuff = 'FPS: %d' % self.fps_frames
            self.fps_frames = 0
            self.fps_lastTime = self.lastFrameTime

    mapping = dict([(ord(c), getattr(Key, c.upper()))
                   for c in string.ascii_letters])
    mapping.update({
                 96: Key.Grave,        126: Key.Grave,
                 49: Key.One,           33: Key.One,
                 50: Key.Two,           64: Key.At,
                 51: Key.Three,         35: Key.Three,
                 52: Key.Four,          36: Key.Four,
                 53: Key.Five,          37: Key.Five,
                 54: Key.Six,           94: Key.Six,
                 55: Key.Seven,         38: Key.Seven,
                 56: Key.Eight,         42: Key.Multiply,
                 57: Key.Nine,          40: Key.Nine,
                 48: Key.Zero,          41: Key.Zero,
                 45: Key.Minus,         95: Key.Underline,
                 61: Key.Equals,        43: Key.Equals,
                 91: Key.LeftBracket,  123: Key.LeftBracket,
                 93: Key.RightBracket, 125: Key.RightBracket,
                 59: Key.Semicolon,     58: Key.Colon,
                 39: Key.Apostrophe,    34: Key.Apostrophe,
                 92: Key.Backslash,    124: Key.Backslash,
                 44: Key.Comma,         60: Key.Comma,
                 46: Key.Period,        62: Key.Period,
                 47: Key.Slash,         63: Key.Slash,
                 13: Key.Return,
                  8: Key.Backspace,
                  9: Key.Tab,
                 32: Key.Space,
                127: Key.Delete,
                 27: Key.Escape})

    def ascii_to_scancode(self, a):
        a = ord(a)
        return self.mapping[a] if (a in self.mapping) else 0

    def special_to_scancode(self, c):
        return self.specialKeyMap[c] if (c in self.specialKeyMap) else 0

    def modifier_keys_state(self):
        s = ''
        if self.glut_modifiers['ctrl']['is_held']:
            s += 'CTRL '
        if self.glut_modifiers['alt']['is_held']:
            s += 'ALT '
        if self.glut_modifiers['shift']['is_held']:
            s += 'SHIFT '
        return s



class PyCeguiBaseSample(object):
    def run(self):
        if self.initialise:
            self.initialise()
            self.cleanup()
        return 0

    def initialise(self):
        self.sampleApp = PyCeguiGlutBaseApplication(x=0, y=0
                , width=800, height=600 ,title='PyCEGUI Falagard Demo')

        # execute the base application (which sets
        # up the demo via 'self' and runs it.)
        if self.sampleApp.execute(self):
            # signal that app initialised and ran
            return True

        self.sampleApp = None

        # signal app did not initialise and run.
        return False

    def cleanup(self):
        if self.sampleApp:
            self.sampleApp = None



class FalagardDemo1Sample(PyCeguiBaseSample):
    def __init__(self):
        self.console = None

    def initialiseSample(self):
        # Get window manager which we wil use for a few jobs here.
        winMgr = WindowManager.getSingleton()
        # Load the scheme to initialse the VanillaSkin which we use
        # in this sample
        SchemeManager.getSingleton().create('VanillaSkin.scheme')
        # set default mouse image
        System.getSingleton().setDefaultMouseCursor('Vanilla-Images'
                ,'MouseArrow')

        # load an image to use as a background
        ImagesetManager.getSingleton().createFromImageFile('BackgroundImage'
                ,'GPN-2000-001437.tga')

        # here we will use a StaticImage as the root,
        # then we can use it to place a background image
        background = winMgr.createWindow('Vanilla/StaticImage', 'bg')

        # set area rectangle
        background.setArea(URect(cegui_reldim(0) ,cegui_reldim(0)
                                ,cegui_reldim(1) ,cegui_reldim(1)))

        # disable frame and standard background
        background.setProperty('FrameEnabled', 'false')
        background.setProperty('BackgroundEnabled', 'false')
        #set the background iwm.mage
        background.setProperty('Image', 'set:BackgroundImage image:full_image')

        # install this as the root GUI sheet
        System.getSingleton().setGUISheet(background)

        FontManager.getSingleton().create('DejaVuSans-10.font')

        # load some demo windows and attach to the background 'root'
        # background.addChildWindow(
        #     winMgr.loadWindowLayout('VanillaWindows.layout'))

        # create an instance of the console class.
        self.console = PyConsole('Demo', None)

        # listen for key presses on the root window.
        GlobalEventSet.getSingleton().subscribeEvent('/AppKeyDown'
                ,self, 'handleRootKeydown')

        # activate the background window
        background.activate()

        # success!
        return True

    def cleanupSample(self):
        self.console = None

    def handleRootKeydown(self, keyArgs):
        if keyArgs.scancode == Key.F12:
            self.console.toggleVisibility()
            logi('BEFORE handleRootKeydown: %s' %
                  pretty_attr(keyArgs, 'WindowEventArgs'))
            keyArgs.handled += 1
            logc('AFTER  handleRootKeydown: %s' %
                  pretty_attr(keyArgs, 'WindowEventArgs'))
            return True
        return False



class DemoConsole(object):
    def __init__(self, name, parent=None):
        # these must match the IDs assigned in the layout
        self.submitButtonID = 1;
        self.entryBoxID     = 2;
        self.historyID      = 3;

        window_manager = WindowManager.getSingleton()
        self.console_root = window_manager.loadWindowLayout(
                'VanillaConsole.layout', name)
        self.history_pos = 0
        self.history = []



        # we will destroy the console box windows ourselves
        self.console_root.setDestroyedByParent(False)

        # Do events wire-up
        GlobalEventSet.getSingleton().subscribeEvent('/AppKeyDown',
                self, 'handleKeydown')
        self.console_root.getChild(self.submitButtonID).subscribeEvent(
                PushButton.EventClicked, self, 'handleSubmit')
        self.console_root.getChild(self.entryBoxID).subscribeEvent(
                Editbox.EventTextAccepted, self, 'handleSubmit')

        # decide where to attach the console main window
        parent = parent or System.getSingleton().getGUISheet()

        # attach this window if parent is valid
        if parent:
            parent.addChildWindow(self.console_root)

    def __del__(self):
        # destroy the windows that we loaded earlier
        WindowManager.getSingleton().destroyWindow(self.console_root)

    def toggleVisibility(self):
        self.console_root.hide() if self.console_root.isVisible(True
                ) else self.console_root.show()

    def handleSubmit(self, WindowEventArgs):
        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)
        # get text out of the editbox
        edit_text = editbox.getText()
        # if the string is not empty
        if edit_text:
            # add this entry to the command history buffer
            self.history.append(edit_text)
            # reset history position
            self.history_pos = len(self.history)
            # append newline to this entry
            edit_text += '\n'
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)
            # erase text in text entry box.
            editbox.setText('')

        # re-activate the text entry box
        editbox.activate()
        return True

    def handleKeydown(self, keyEventArgs):
        # if no input focus, ignore events
        if not self.console_root.getActiveChild():
            return False

        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)

        if keyEventArgs.scancode == Key.ArrowUp:
            self.history_pos = max(self.history_pos - 1, -1)
            if self.history_pos >= 0:
                editbox.setText(self.history[self.history_pos])
                editbox.setCaratIndex(sys.maxint)
            else:
                editbox.setText('')
            editbox.activate()

        elif keyEventArgs.scancode == Key.ArrowDown:
            self.history_pos = min(self.history_pos + 1, len(self.history))
            if self.history_pos < len(self.history):
                editbox.setText(self.history[self.history_pos])
                editbox.setCaratIndex(sys.maxint)
            else:
                editbox.setText('')
            editbox.activate()
        else:
            return False

        logi('BEFORE handleKeyDown: %s' %
                pretty_attr(keyEventArgs, 'keyEventArgs'))
        keyEventArgs.handled += 100
        logc('AFTER  handleKeyDown: %s' %
                pretty_attr(keyEventArgs, 'keyEventArgs'))
        return True

import code
class PyConsole(DemoConsole):
    def __init__(self, name, parent):
        DemoConsole.__init__(self, name=name, parent=parent)

        sys.stdout = self
        # sys.stderr = self

        self.inter = code.InteractiveInterpreter(globals())

    def write(self, edit_text):
        if edit_text and edit_text != '\n':
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)

    def handleSubmit(self, WindowEventArgs):
        # get the text entry editbox
        editbox = self.console_root.getChild(self.entryBoxID)
        # get text out of the editbox
        edit_text = editbox.getText()
        # if the string is not empty
        if edit_text:
            # add this entry to the command history buffer
            self.history.append(edit_text)
            # reset history position
            self.history_pos = len(self.history)
            # append newline to this entry
            # edit_text += '\n'
            # get history window
            history = self.console_root.getChild(self.historyID)
            # append new text to history output
            history.setText(history.getText() + edit_text)
            # scroll to bottom of history output
            history.setCaratIndex(sys.maxint)
            # erase text in text entry box.
            editbox.setText('')

            self.inter.runsource(edit_text)


# macro to aid in the creation of UDims (copied from CEGUIUDim.h)
def cegui_reldim(x):
    return UDim(x, 0)


def pretty_attr(py_object, label, interesting_classes='default'):
    if interesting_classes == 'default':
        interesting_classes = pretty_attr.default_classes

    def _pretty_attr(py_object, label, interesting_classes, indent='.'):
        if isinstance(py_object, pretty_attr.instancemethodType):
            return label + ' [instancemethod]'
        elif interesting_classes and isinstance(py_object, interesting_classes):
            result = ''
            for attribute_name in dir(py_object):
                if not attribute_name.startswith('__'):
                    attribute = getattr(py_object, attribute_name)
                    result += indent + _pretty_attr(attribute
                                                   ,attribute_name + ' = '
                                                   ,interesting_classes
                                                   ,indent + '.') + '\n'
            return label + str(py_object) + '\n' + result
        # TODO: add support for lists/tuples/dicts
        else:
            # just print simple string for uninteresting objects
            return label + str(py_object)

    return _pretty_attr(py_object, label + ' = ', interesting_classes)[:-1]

pretty_attr.instancemethodType = type(
            new.instancemethod(pretty_attr, None, object))

pretty_attr.default_classes = (
        KeyEventArgs
       ,WindowEventArgs)


import fnmatch;
def block_indent(ss):
    return ss.replace('\n', '\n' +  log.indent)


def log(message, message_class='default', indent=None):
    if indent is None:
        indent = log.indent_level * ' '
    for wildcard_pattern in log.classes:
        if fnmatch.fnmatch(message_class, wildcard_pattern):
            print block_indent(indent + message)

def logi(message, message_class='default'):
    log(message, message_class, log.indent_level * ' ')

    log.indent_level += 2
    log.indent = ' ' * log.indent_level

def logc(message, message_class='default'):
    log.indent_level  = log.indent_level - 2
    log.indent = ' ' * log.indent_level

    log(message, message_class, log.indent_level * ' ')

log.indent_level = 0
log.indent = ''
log.classes = (
       '_default'
       #,'inject*'
       #,'result'
       ,)

app = FalagardDemo1Sample()
app.run()
del app



User avatar
Kulik
CEGUI Team
Posts: 1382
Joined: Mon Jul 26, 2010 18:47
Location: Czech Republic
Contact:

Re: Python port of Falagard demo (console window with histor

Postby Kulik » Tue Jul 26, 2011 08:26

Wow, great work!

I think it would be great to add this to CEGUI itself (and the SDKs shipped) because it could serve as both a demo and a development tool (trying commands right away is definitely a great feature to have).


Return to “User Projects”

Who is online

Users browsing this forum: No registered users and 1 guest