Difference between revisions of "User:Crond/sandbox/openglEventExample"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(Created page with "{{VersionBadge|0.7}} {{VersionAtLeast|0.7.5}} <br /><br /><br /><br /> == Introduction == This presents the basics of using subscriptions to respond to events. The example retri...")
 
Line 217: Line 217:
 
     sys.exit(main())
 
     sys.exit(main())
 
</source>
 
</source>
 +
 +
== Demonstration ==
 +
Just click either of the two buttons on the 'NewNode' frame window; some output will appear on the console.
 +
 +
== Examination ==
 +
=== Layout to Python ===
 +
To subscribe to an event, the application needs two fundamental details: the layout, and the widget that should respond to an event. The layout is simple - we just pass the name of it to the PyCEGUI WindowManager object; getting child windows and/or widgets can be a little more subtle - to do this, we have to know the name of the window in question.
 +
 +
In the case of the example above, we want to respond to events on the 'NewNode' frame window, so we first retrieve a copy of it - as indicated by this line:
 +
 +
<source lang="python">
 +
        self.windowNewNode = self.rootWindow.getChild('Demo/NewNode')
 +
</source>
 +
 +
Then, we need a copy of the buttons which we want to listen for events, as indicated by these lines:
 +
 +
<source lang="python">
 +
        # Now, we get the buttons.
 +
        self.buttonOkay = self.windowNewNode.getChild('Demo/NewNode/Okay')
 +
        self.buttonCancel = self.windowNewNode.getChild('Demo/NewNode/Cancel')
 +
</source>
 +
 +
Finally, we want to set what events we respond to and how we respond to them. We do this by defining methods on our object, and telling PyCEGUI to call them when specific events happen. This is indicated by the following:
 +
 +
<source lang="python">
 +
        # And subscribe to the events, telling PyCEGUI what method it should call when the event happens.
 +
        self.buttonOkay.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonOkayClicked')
 +
        self.buttonCancel.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonCancelClicked')
 +
</source>
 +
 +
The first argument is specifying what event to listen for (in this case, the clicking of the button), and the second/third arguments specify what function/method should be invoked when the event in question happens.
 +
 +
'''Note: Future versions do not require the 'self' argument.'''
 +
 +
=== Layout editing ===
 +
The exact name of the widgets you want to access can be determined via the XML 'Name' property, as found in the layout files.
 +
 +
In the event you are not creating your layout files by hand, the names can be found via the CELayoutEditor under the 'Name' property in the Main Dialog.

Revision as of 21:13, 18 June 2011

Written for CEGUI 0.7


Works with versions 0.7.x (obsolete)

Requires at least version
0.7.5





Introduction

This presents the basics of using subscriptions to respond to events. The example retrieves two buttons and defines a method to be called when they are each clicked.

Requirements

  • Python 2.6 (untested on Python 3.+)
  • PyCEGUI
  • PyOpenGL (GLU and GLUT, as well)

Notes

The source code uses a modified version of the OpenGL example posted here.

Source

#!/usr/bin/env python
#
#
# example.py
 
 
# Import: std
import sys
 
# Import: PyOpenGL
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
 
# Import: PyCEGUI
import PyCEGUI
from PyCEGUIOpenGLRenderer import OpenGLRenderer as Renderer
 
# Constants
PATH_RESOURCES = './'
 
 
# Application
class Application(object):
 
    # Constructor
    def __init__(self):
        super(Application, self).__init__()
        self.lastFrameTime = 0
        self.updateFPS = 0
        return
 
    # Initialize: OpenGL
    # - A full list of values for `glutInitDisplayMode` can be found in the GLUT
    #   documentation.
    def initializeOpenGL(self):
        glutInit()
        glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA)
        glutInitWindowSize(1024, 768)
        glutInitWindowPosition(-1, -1) # Let the windowing system figure it out
        glutCreateWindow("Crazy Eddie's GUI Mk-2 - glut Base Application")
        glutSetCursor(GLUT_CURSOR_NONE)
        return
 
    # Initialize: Handlers
    # - Setup the methods which will be called when events happen.
    def initializeHandlers(self):
        glutDisplayFunc(self.handlerDisplay)
        glutReshapeFunc(self.handlerResize)
        glutMouseFunc(self.handlerMouse)
        glutMotionFunc(self.handlerMouseMotion)
        glutPassiveMotionFunc(self.handlerMouseMotion)
        return
 
    # Initialize: PyCEGUI
    # - Store some components; saves a lot of typing.
    def initializePyCEGUI(self):
        Renderer.bootstrapSystem()
        self.sys = PyCEGUI.System.getSingleton()
        self.rp = self.sys.getResourceProvider()
        self.scheme = PyCEGUI.SchemeManager.getSingleton()
        self.wm = PyCEGUI.WindowManager.getSingleton()
        return
 
    # Initialize: Defaults
    # - Resource locations.
    def initializeDefaults(self):
        self.rp.setResourceGroupDirectory('schemes', './datafiles/schemes')
        self.rp.setResourceGroupDirectory('imagesets', './datafiles/imagesets')
        self.rp.setResourceGroupDirectory('fonts', './datafiles/fonts')
        self.rp.setResourceGroupDirectory('layouts', './datafiles/layouts')
        self.rp.setResourceGroupDirectory('looknfeels', './datafiles/looknfeel')
        self.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 = self.sys.getXMLParser()
        if parser.isPropertyPresent('SchemaDefaultResourceGroup'):
            parser.setProperty('SchemaDefaultResourceGroup', 'schemas')
        return
 
    # Initialize: GUI
    # - This is where we are actually setting up the windows we will see.
    def initializeGUI(self):
        self.scheme.create('VanillaSkin.scheme')
        self.scheme.create('TaharezLook.scheme')
 
        # GUISheet
        self.rootWindow = self.wm.loadWindowLayout('VanillaWindows.layout')
        self.sys.setGUISheet(self.rootWindow)
 
        # Cursor
        self.sys.setDefaultMouseCursor('Vanilla-Images', 'MouseArrow')
 
        # An extra window
        w = self.wm.createWindow('TaharezLook/FrameWindow', 'Demo window')
        self.rootWindow.addChildWindow(w)
        return
 
    # Initialize: Subscriptions
    def initializeSubscriptions(self):
 
        # In order to be able to access the buttons, we need to get a copy of their parent.
        self.windowNewNode = self.rootWindow.getChild('Demo/NewNode')
 
        # Now, we get the buttons.
        self.buttonOkay = self.windowNewNode.getChild('Demo/NewNode/Okay')
        self.buttonCancel = self.windowNewNode.getChild('Demo/NewNode/Cancel')
 
        # And subscribe to the events, telling PyCEGUI what method it should call when the event happens.
        self.buttonOkay.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonOkayClicked')
        self.buttonCancel.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonCancelClicked')
        return
 
    # Initialize
    def Initialize(self):
        self.initializeOpenGL()
        self.initializeHandlers()
        self.initializePyCEGUI()
        self.initializeDefaults()
        self.initializeGUI()
        self.initializeSubscriptions()
        return
 
    # Handler: Display
    def handlerDisplay(self):
 
        # Injecting the time allows CEGUI to know how much time has passed, and
        # use that to coordinate certain activities - fading, animation, tooltips,
        # etc.
        now = glutGet(GLUT_ELAPSED_TIME)
        elapsed = (now - self.lastFrameTime) / 1000.0
        self.lastFrameTime = now
        self.updateFPS = self.updateFPS - elapsed
        self.sys.injectTimePulse(elapsed)
 
        # Actual rendering
        # - `renderGUI` updates CEGUI's picture of the GUI.
        # - `glutPostRedisplay` is what actually marks the window as needing to
        #   be redrawn by OpenGL.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.sys.renderGUI()
        glutPostRedisplay()
        glutSwapBuffers()
        return
 
    # Handler: Resize
    # - `glViewport` modifies the OpenGL viewport to whatever the window size is.
    def handlerResize(self, width, height):
        glViewport(0, 0, width, height)
        self.sys.notifyDisplaySizeChanged(PyCEGUI.Size(width, height))
        return
 
    # Handler: Mouse buttons
    def handlerMouse(self, button, state, x, y):
        if button == GLUT_LEFT_BUTTON:
            if state == GLUT_UP:
                self.sys.injectMouseButtonUp(PyCEGUI.LeftButton)
            else:
                self.sys.injectMouseButtonDown(PyCEGUI.LeftButton)
        elif button == GLUT_RIGHT_BUTTON:
            if state == GLUT_UP:
                self.sys.injectMouseButtonUp(PyCEGUI.RightButton)
            else:
                self.sys.injectMouseButtonDown(PyCEGUI.RightButton)
        return
 
    # Handler: Mouse motion
    def handlerMouseMotion(self, x, y):
        self.sys.injectMousePosition(x, y)
        return
 
    # Handler: buttonOkayClicked
    def buttonOkayClicked(self, args):
        print('Okay has been clicked.')
        return
 
    # Handler: buttonCancelClicked
    def buttonCancelClicked(self, args):
        print('Cancel has been clicked.')
        return
 
    # Run
    def Run(self):
        self.lastFrameTime = glutGet(GLUT_ELAPSED_TIME)
        glutMainLoop()
        return
 
# Main
def main():
    app = Application()
    app.Initialize()
    app.Run()
    return 0
 
 
# Guard
if __name__ == '__main__':
    sys.exit(main())

Demonstration

Just click either of the two buttons on the 'NewNode' frame window; some output will appear on the console.

Examination

Layout to Python

To subscribe to an event, the application needs two fundamental details: the layout, and the widget that should respond to an event. The layout is simple - we just pass the name of it to the PyCEGUI WindowManager object; getting child windows and/or widgets can be a little more subtle - to do this, we have to know the name of the window in question.

In the case of the example above, we want to respond to events on the 'NewNode' frame window, so we first retrieve a copy of it - as indicated by this line:

        self.windowNewNode = self.rootWindow.getChild('Demo/NewNode')

Then, we need a copy of the buttons which we want to listen for events, as indicated by these lines:

        # Now, we get the buttons.
        self.buttonOkay = self.windowNewNode.getChild('Demo/NewNode/Okay')
        self.buttonCancel = self.windowNewNode.getChild('Demo/NewNode/Cancel')

Finally, we want to set what events we respond to and how we respond to them. We do this by defining methods on our object, and telling PyCEGUI to call them when specific events happen. This is indicated by the following:

        # And subscribe to the events, telling PyCEGUI what method it should call when the event happens.
        self.buttonOkay.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonOkayClicked')
        self.buttonCancel.subscribeEvent(PyCEGUI.PushButton.EventClicked, self, 'buttonCancelClicked')

The first argument is specifying what event to listen for (in this case, the clicking of the button), and the second/third arguments specify what function/method should be invoked when the event in question happens.

Note: Future versions do not require the 'self' argument.

Layout editing

The exact name of the widgets you want to access can be determined via the XML 'Name' property, as found in the layout files.

In the event you are not creating your layout files by hand, the names can be found via the CELayoutEditor under the 'Name' property in the Main Dialog.