Difference between revisions of "User:Crond/sandbox"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(PyCEGUI/PyOpenGL: 1)
(blanked)
Line 1: Line 1:
== Introduction ==
 
This is an example which creates a light, non-comprehensive framework for PyCEGUI, with the rendering system used being [Py]OpenGL. The reason OpenGL is used is because it's free, easy to get on any Ubuntu system (which this was written on), and portable. Also, it's the one demonstrated on the official [[PyCEGUI]] page.
 
  
== Links of interest ==
 
*[http://docs.python.org/release/2.6.5/ Python 2.6.5 documentation]
 
*[http://pyopengl.sourceforge.net/documentation/manual-3.0/index.xhtml PyOpenGL reference]
 
*[http://pyopengl.sourceforge.net/documentation/manual-3.0/index.xhtml#GLUT PyOpenGL GLUT reference]
 
 
== Requirements ==
 
=== General ===
 
*Python 2.6
 
*PyCEGUI
 
*PyOpenGL, GLU, GLUT
 
=== Datafiles ===
 
This example assumes the CEGUI datafiles are in the same directory as `demo.py`; see the following section.
 
=== Directory hierarchy ===
 
Replace `/home/foo/bar` as necessary.
 
*/home/foo/bar/demo
 
**/home/foo/bar/demo/datafiles
 
***/home/foo/bar/demo/datafiles/layouts/DemoMenu.layout
 
**/home/foo/bar/demo/config
 
***/home/foo/bar/demo/config/constants.py
 
**/home/foo/bar/demo/error
 
***/home/foo/bar/demo/error/errors.py
 
**/home/foo/bar/demo/input
 
***/home/foo/bar/demo/input/input.py
 
**/home/foo/bar/demo/video
 
***/home/foo/bar/demo/video/video.py
 
**/home/foo/bar/demo/gui
 
***/home/foo/bar/demo/gui/gui.py
 
***/home/foo/bar/demo/gui/demomenu.py
 
**/home/foo/bar/demo/demo.py
 
 
== General notes ==
 
This is not intended to be fully comprehensive; rather, the intention is to illustrate '''one way''' of setting up a GUI system. A minimal amount of thought was given to "problems down the road;" which is to say that there is no guarantee that a particular design constraint will not render the framework unsuitable for a particular purpose.
 
 
In short: your mileage may vary.
 
 
== Comments, improvements, etc ==
 
Use the discussion page.
 
 
== Source ==
 
=== constants.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# constants.py
 
 
 
"""Constant values.
 
 
Values currently contained in this file include:
 
 
    Version information
 
    Window name(s)
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
 
# Versioning
 
MINOR_VERSION = '01'
 
MAJOR_VERSION = '0'
 
FULL_VERSION = '.'.join([MAJOR_VERSION, MINOR_VERSION])
 
 
# Name: Root window
 
NAME_ROOT_WINDOW = 'Demo: %s' % FULL_VERSION
 
</source>
 
 
=== errors.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# errors.py
 
 
"""Errors.
 
 
Currently defined errors include:
 
 
    InitializationError
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
 
# InitializationError
 
class InitializationError(Exception):
 
    def __init__(self, msg):
 
        self.msg = msg
 
    def __str__(self):
 
        return ('InitializationError: %s' % self.msg)
 
</source>
 
 
=== video.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# video.py
 
 
 
"""Video functionality.
 
 
This class brings together PyCEGUI and OpenGL into a comfortable interface.
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
# Import: OpenGL
 
from OpenGL.GL import *
 
from OpenGL.GLU import *
 
from OpenGL.GLUT import *
 
 
# Import: PyCEGUI
 
import PyCEGUI
 
from PyCEGUIOpenGLRenderer import OpenGLRenderer as Renderer
 
 
# Import: User
 
from constants import *
 
from errors import InitializationError
 
 
 
# Video
 
class Video(object):
 
 
    # Initialize: OpenGL
 
    def initializeOpenGL(self):
 
        glutInit()
 
        glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA)
 
        glutInitWindowSize(1024, 768)
 
        glutInitWindowPosition(-1, -1)
 
        glutCreateWindow(NAME_ROOT_WINDOW)
 
        glutSetCursor(GLUT_CURSOR_NONE)
 
 
        # Handlers
 
        glutDisplayFunc(self.handlerDisplay)
 
        glutReshapeFunc(self.handlerReshape)
 
        return
 
 
    # Initialize: PyCEGUI
 
    def initializePyCEGUI(self):
 
        self.renderer = Renderer.bootstrapSystem()
 
        return
 
 
    # Initialize
 
    def Initialize(self):
 
        try:
 
            self.initializeOpenGL()
 
            self.initializePyCEGUI()
 
        except Exception, msg:
 
            raise InitializationError(msg)
 
        return
 
 
    # Shutdown
 
    # - For implicit use, use the Python special method `__del__`.
 
    def Shutdown(self):
 
        self.renderer.destroySystem()
 
        return
 
 
    # Handler: Display
 
    # - This is called to refresh the screen.
 
    # - See PyOpenGL documentation.
 
    def handlerDisplay(self):
 
        thisTime = glutGet(GLUT_ELAPSED_TIME)
 
        elapsed = (thisTime - self.lastFrameTime) / 1000.0
 
        self.lastFrameTime = thisTime
 
        self.updateFPS = self.updateFPS - elapsed
 
        PyCEGUI.System.getSingleton().injectTimePulse(elapsed)
 
 
        # Render this frame
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        PyCEGUI.System.getSingleton().renderGUI()
 
        glutPostRedisplay()
 
        glutSwapBuffers()
 
        return
 
 
    # Handler: Reshape
 
    # - This is called when the window is resized and/or switches to fullscreen.
 
    # - See PyOpenGL documentation.
 
    def handlerReshape(self, width, height):
 
        glViewport(0, 0, width, height)
 
        glMatrixMode(GL_PROJECTION)
 
        glLoadIdentity()
 
        gluPerspective(60.0, width / height, 1.0, 50.0)
 
        glMatrixMode(GL_MODELVIEW)
 
        PyCEGUI.System.getSingleton().notifyDisplaySizeChanged(PyCEGUI.Size(width, height))
 
        return
 
 
    # Main loop
 
    # - Set the initial values.
 
    # - This never returns; once this gets called, the application is driven entirely by events.
 
    def EnterMainLoop(self):
 
        self.lastFrameTime = glutGet(GLUT_ELAPSED_TIME)
 
        self.updateFPS = 0
 
        glutMainLoop()
 
        return
 
</source>
 
 
=== input.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# input.py
 
 
 
"""Input functionality.
 
 
This class sets up input processing by creating handlers for GLUT that inject
 
input into PyCEGUI.
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
# Import: OpenGL
 
from OpenGL.GL import *
 
from OpenGL.GLU import *
 
from OpenGL.GLUT import *
 
 
# Import: PyCEGUI
 
import PyCEGUI
 
 
# Import: User
 
from constants import *
 
from errors import InitializationError
 
 
 
# Input
 
class Input(object):
 
 
    # Initialize: Handlers
 
    def initializeHandlers(self):
 
        glutKeyboardFunc(self.handlerKeyDown)
 
        glutMouseFunc(self.handlerMouseButton)
 
 
        # The difference between these two is that the passive one is called when there is
 
        # mouse motion while no buttons are pressed, and the other is called when there
 
        # is mouse motion while buttons are pressed. See PyOpenGL documentation.
 
        glutMotionFunc(self.handlerMouseMotion)
 
        glutPassiveMotionFunc(self.handlerMouseMotion)
 
        return
 
 
    # Initialize
 
    def Initialize(self):
 
        try:
 
            self.initializeHandlers()
 
        except Exception, msg:
 
            raise InitializationError(msg)
 
        return
 
 
    # Handler: Key Down
 
    # - `ord` is a built-in Python function.
 
    def handlerKeyDown(self, key, x, y):
 
        PyCEGUI.System.getSingleton().injectChar(ord(key))
 
        return
 
 
    # Handler: Mouse Button
 
    def handlerMouseButton(self, button, state, x, y):
 
        if button == GLUT_LEFT_BUTTON:
 
            if state == GLUT_UP:
 
                PyCEGUI.System.getSingleton().injectMouseButtonUp(PyCEGUI.LeftButton)
 
            else:
 
                PyCEGUI.System.getSingleton().injectMouseButtonDown(PyCEGUI.LeftButton)
 
 
        # A thought is to turn this into an `else` clause; however, this implies that any
 
        # button besides the left is interpreted as the right button - this seems undesirable
 
        # for any mouse with more than two buttons.
 
        elif button == GLUT_RIGHT_BUTTON:
 
            if state == GLUT_UP:
 
                PyCEGUI.System.getSingleton().injectMouseButtonUp(PyCEGUI.RightButton)
 
            else:
 
                PyCEGUI.System.getSingleton().injectMouseButtonDown(PyCEGUI.RightButton)
 
 
        # An `else` clause could also go here to perform some arbitrary action on unhandled
 
        # mouse input; this is left as an exercise for the reader. Instead, we just implicitly
 
        # ignore it.
 
        return
 
 
    # Handler: Mouse Motion
 
    # - This might seem arbitrary, but in fact this is required or else the position of the mouse
 
    # will never be updated inside the window.
 
    def handlerMouseMotion(self, x, y):
 
        PyCEGUI.System.getSingleton().injectMousePosition(x, y)
 
        return
 
</source>
 
 
=== gui.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# gui.py
 
 
 
"""GUI.
 
 
This class is the entry point for the GUI; which is to say that it starts the
 
main menu and the rest is event driven.
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
# Import: PyCEGUI
 
import PyCEGUI
 
 
# Import: User
 
from errors import InitializationError
 
import demomenu
 
 
 
# GUI
 
class GUI(object):
 
 
    # Initialize: Resources
 
    def initializeResources(self):
 
        rp = PyCEGUI.System.getSingleton().getResourceProvider()
 
        rp.setResourceGroupDirectory('schemes', './datafiles/schemes')
 
        rp.setResourceGroupDirectory('imagesets', './datafiles/imagesets')
 
        rp.setResourceGroupDirectory('fonts', './datafiles/fonts')
 
        rp.setResourceGroupDirectory('layouts', './datafiles/layouts')
 
        rp.setResourceGroupDirectory('looknfeels', './datafiles/looknfeel')
 
        rp.setResourceGroupDirectory('schemas', './datafiles/xml_schemas')
 
        PyCEGUI.Imageset.setDefaultResourceGroup('imagesets')
 
        PyCEGUI.Font.setDefaultResourceGroup('fonts')
 
        PyCEGUI.Scheme.setDefaultResourceGroup('schemes')
 
        PyCEGUI.WidgetLookManager.setDefaultResourceGroup('looknfeels')
 
        PyCEGUI.WindowManager.setDefaultResourceGroup('layouts')
 
        parser = PyCEGUI.System.getSingleton().getXMLParser()
 
        if parser.isPropertyPresent('SchemaDefaultResourceGroup'):
 
            parser.setProperty('SchemaDefaultResourceGroup', 'schemas')
 
        return
 
 
    # Initialize: Defaults
 
    def initializeDefaults(self):
 
        sm = PyCEGUI.SchemeManager.getSingleton()
 
        sm.create('VanillaSkin.scheme')
 
        sm.create('TaharezLook.scheme')
 
        PyCEGUI.System.getSingleton().setDefaultMouseCursor('Vanilla-Images', 'MouseArrow')
 
        return
 
 
    # Initialize
 
    def Initialize(self):
 
        try:
 
            self.initializeResources()
 
            self.initializeDefaults()
 
 
            # GUISheet
 
            self.GUISheet = PyCEGUI.WindowManager.getSingleton().createWindow('DefaultWindow', 'Root')
 
            PyCEGUI.System.getSingleton().setGUISheet(self.GUISheet)
 
        except Exception, msg:
 
            raise InitializationError(msg)
 
        return
 
 
    # Setup
 
    # - Important: the instance of `demomenu.DemoMenu` has to be bound to this object; if it is
 
    # a local variable (read: destroyed when it goes out of scope), exceptions will be raised
 
    # about the `buttonClicked` method not existing. This is a drawback of the type of setup
 
    # this example uses, and as a consequence of Python being a garbage collected language.
 
    def Setup(self):
 
        self.demoMenu = demomenu.DemoMenu()
 
        self.demoMenu.Initialize(self.GUISheet)
 
        self.demoMenu.Setup()
 
        return
 
 
    # Setup: Interface
 
    def setupInterface(self):
 
        self.Setup()
 
        return
 
</source>
 
 
=== demomenu.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# demomenu.py
 
 
 
"""DemoMenu.
 
 
This class represents a demonstration of how to control a menu with PyCEGUI and PyOpenGL.
 
 
"""
 
 
# Import: std
 
import sys
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
# Import: PyCEGUI
 
import PyCEGUI
 
 
# Import: User
 
from errors import InitializationError
 
 
 
# DemoMenu
 
class DemoMenu(object):
 
 
    # Initialize
 
    def Initialize(self, guiSheet):
 
        self.GUISheet = guiSheet
 
 
        # Load the layout
 
        self.menu = PyCEGUI.WindowManager.getSingleton().loadWindowLayout('DemoMenu.layout')
 
        return
 
 
    # connectHandlers
 
    # - Wrapper method to define the subscription/listener relationships.
 
    # - If there are a lot, it may behoove the coder to encapsulate them in methods, then call those methods here.
 
    def connectHandlers(self):
 
        self.menu.getChild('DemoMenu/ButtonUsername').subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonClicked')
 
        return
 
 
    # Setup
 
    def Setup(self):
 
 
        # Connect the handlers
 
        self.connectHandlers()
 
 
        # Attach
 
        self.GUISheet.addChildWindow(self.menu)
 
        return
 
 
    # Handler: buttonClicked
 
    def buttonClicked(self, args):
 
        print('buttonClicked')
 
        return
 
</source>
 
 
=== demo.py ===
 
<source lang="python">
 
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
#
 
# demo.py
 
 
 
"""Demo entry point.
 
 
A demonstration; mildly comprehensive, but not really.
 
 
"""
 
 
# Import: std
 
import sys, os.path
 
sys.path.append(os.path.join(os.getcwd(), 'config'))
 
sys.path.append(os.path.join(os.getcwd(), 'error'))
 
sys.path.append(os.path.join(os.getcwd(), 'input'))
 
sys.path.append(os.path.join(os.getcwd(), 'video'))
 
sys.path.append(os.path.join(os.getcwd(), 'gui'))
 
 
# Import: psyco
 
try:
 
    import psyco
 
    psyco.full()
 
except ImportError:
 
    pass
 
 
# Import: User
 
from errors import InitializationError
 
from input import Input
 
from video import Video
 
from gui import GUI
 
 
 
# Main
 
def main():
 
    gfx = Video()
 
    inp = Input()
 
    gui = GUI()
 
 
    # Initialize
 
    try:
 
        gfx.Initialize()
 
        inp.Initialize()
 
        gui.Initialize()
 
    except InitializationError as error:
 
        print(error)
 
        return 1
 
 
    # Setup the interface
 
    gui.setupInterface()
 
 
    # Main Loop
 
    gfx.EnterMainLoop()
 
 
    # Done
 
    gfx.Shutdown()
 
    return 0
 
 
# Guard
 
if __name__ == '__main__':
 
    sys.exit(main())
 
</source>
 
 
=== mainmenu.layout ===
 
<source lang="xml">
 
<?xml version="1.0" encoding="UTF-8"?>
 
 
<GUILayout >
 
    <Window Type="TaharezLook/FrameWindow" Name="DemoMenu" >
 
        <Property Name="Font" Value="DejaVuSans-10" />
 
        <Property Name="Text" Value="Demo Menu" />
 
        <Property Name="TitlebarFont" Value="DejaVuSans-10" />
 
        <Property Name="RollUpEnabled" Value="False" />
 
        <Property Name="TitlebarEnabled" Value="True" />
 
        <Property Name="UnifiedAreaRect" Value="{{0.157031,0},{0.194911,0},{0.783984,0},{0.687913,0}}" />
 
        <Property Name="DragMovingEnabled" Value="False" />
 
        <Property Name="CloseButtonEnabled" Value="False" />
 
        <Property Name="EWSizingCursorImage" Value="set:Vanilla-Images image:MouseArrow" />
 
        <Property Name="InheritsTooltipText" Value="False" />
 
        <Property Name="NSSizingCursorImage" Value="set:Vanilla-Images image:MouseArrow" />
 
        <Property Name="NESWSizingCursorImage" Value="set:Vanilla-Images image:MouseArrow" />
 
        <Property Name="NWSESizingCursorImage" Value="set:Vanilla-Images image:MouseArrow" />
 
        <Window Type="TaharezLook/StaticText" Name="DemoMenu/Text" >
 
            <Property Name="Text" Value="Enter some text" />
 
            <Property Name="UnifiedAreaRect" Value="{{0.0212363,0},{0.619781,0},{0.346946,0},{0.751932,0}}" />
 
        </Window>
 
        <Window Type="TaharezLook/Editbox" Name="DemoMenu/Edit" >
 
            <Property Name="MaxTextLength" Value="1073741823" />
 
            <Property Name="UnifiedAreaRect" Value="{{0.36704,0},{0.623671,0},{0.769671,0},{0.750419,0}}" />
 
            <Property Name="TextParsingEnabled" Value="False" />
 
        </Window>
 
        <Window Type="TaharezLook/Button" Name="DemoMenu/Button" >
 
            <Property Name="Text" Value="Click me" />
 
            <Property Name="UnifiedAreaRect" Value="{{0.78567,0},{0.620646,0},{0.979596,0},{0.749355,0}}" />
 
        </Window>
 
    </Window>
 
</GUILayout>
 
</source>
 
 
== Modifications ==
 
=== demomenu.py ===
 
To further demonstrate usage of how to manipulate the widgets and branch the script/code divide, consider the following modification to the `demomenu.DemoMenu.buttonClicked` method:
 
 
<source lang="python">
 
def buttonClicked(self, args):
 
    editbox = self.menu.getChild('DemoMenu/Edit')
 
    print(editbox.getText())
 
    return
 
</source>
 
 
Now, when the button is clicked, it will print to the console whatever has been typed in the box.
 
 
As an exercise to the reader, consider adding support for Backspace/Delete/Arrow keys (hint: Special) keys.
 

Revision as of 19:32, 21 May 2011