# Programmer's Guide

## Contents

- Overview
  - Big Picture
- Creating New Modules
  - Module Configuration
  - BasicModule Instance Variables and Helper Methods
  - Methods to Extend
  - Module Icon
  - Minimal Example
- Editing the Documentation

## Overview

ExpyVR is a framework written in Python developed by the Laboratory of Cognitive Neuroscience at EPFL. It is made for designing and running computerised experiments, most notably experiments in virtual-reality.
The ExpyVR framework is still in development. This guide might therefore not always be up-to-date.

Back to contents

### Big Picture

Every of the colourful boxes above represent a Python package. The core of the framework is made up of a controller that takes an instance file generated by the Experiment Designer and prepares the display(s) and the logger. This is needed in all the experiments. Every module knows about the controller and all information exchanged between modules flows through the controller. The blue boxes above represent components that can be used within the experiment. The core is clearly separated from the other modules and there is a very simply interface. This means that a programmer does not need to understand how everything works but can simply write a new module that adheres to the interface.

Back to contents

## Creating New Modules

To create a new module that can be inserted from the Experiment Designer GUI and used in the experiment you have to create a new class called ModuleMain that extends from BasicModule or one of the other classes in /src/abstract/AbstractClasses.py. If your module should somehow be displayed in the virtual scene, you should extend from DrawableModule. This class will be instantiated during runtime of the experiment and will do whatever it is that this module does. Static dictionaries are also used to determine what parameters can be configured by the user in the Experiment Designer.

The other thing that you have to do to get your module to be available in the Experiment Designer is to add it to /expbuilder/app/components/components.xml.

Please look at existing modules and especially at the example below to see what conventions are in place and try to follow them.

Back to contents

### Module Configuration

Every module is configured through two dictionaries that are passed to the __init__ method. The initConf is used for parameters that are set only once for the module and left unchanged during the whole experiment. The runConf is for parameters that can change every time the component is activated. To define what parameters are needed by your module, you have to overwrite the following three static class members:

- defaultInitConf: A dictionary mapping the names of the init parameters to their default value. Every module hast to have at least the parameter name.
- defaultRunConf: A dictionary mapping the names of the run parameters to their default value. Can be empty.
- confDescription: A list that describes both the init and run config parameters. The order in which the parameters appear in this list, will be the order they are shown in the Experiment Designer. Every list element is a 3- or 4-tuple with the following elements:
  - Name: The same name as in the defaultInit/RunConf dictionary.
  - Type: Type of this parameters: int, float, bool (will be displayed as a checkbox), str, code (for more complex types such as tuples and dicts).
  - Description: Description of this parameter that will be shown to the user. If the description ends with a *, the parameter will be shown in bold indicating that it can be accessed by scripts.
  - Allowed values: Optional list of allowed values for this parameter. If present the parameters will be displayed as a dropdown box.

Back to contents

### BasicModule Instance Variables and Helper Methods

The BasicModule class has a few instance variables that can be used by your module. It is best to only read these variables and let the BasicModule take care of updating them to appropriate values. To make sure this can always be done, you need to call the parent method whenever you extend a method. The instance variables are:

- initConf: Complete dictionary of initConf values.
- runConfs: A dictionary of dictionaries. All the run configs that will be used in the experiment indexed by their name. It is useful to have all of the runConfs already at initialisation time if one for example wants to load all images that will later be displayed at the beginning.
- activeConf: The dictionary of the currently active run config. This is how the current run config should be accessed in most cases. This is the same as self.runConfs[self.activeConfName].
- activeConfName: The name of the currently active run config.
- controller: A reference to the MainControl object that is managing the experiment.
- started: Boolean value whether the component is currently started or stopped.

Additionally there are a couple of helper methods at your disposal:

- log: Logs something to the logger. The logger takes care to keep track of which component logs what. The argument to this method can be a simple string or any other object that can be converted to a string.
- getName: Returns the name of this component. This is not the name of the module itself, but the name the user gave the instance in the Experiment Builder.
- getStartingTimes: Returns the times when the module was started: [absoluteTime, experimentTime, displayTime].

Back to contents

### Methods to Extend

The following methods should most likely be extended by your module. Remember, ff you overwrite a method make sure you call the parent method, even if it is empty at the moment because this might change in the future. The methods that can be extended are:

- __init__: Initialises the component. It is crucial to call the parent method first so that the above mentioned instance variables can be initialised. You should not access the parameters of this method directly, but instead rely on the instance variables described before.
- start: Called whenever the component is started/activated.
- stop: Called whenever the component is stopped/deactivated.
- cleanup: Called when the component is destroyed. Make sure you clean up all the open files, connections, etc. here.
- draw: Only if you extend from DrawableModule. Called on every frame while the component is visible. Should contain all the OpenGL calls to draw whatever this module needs to display.

Back to contents

### Module Icon

To get your component to be shown with a cool icon in the Experiment Builder, place a png file called yourmodulename.png (lower-case, no separation between words) in /expbuilder/app/components/resources.

Back to contents

### Minimal Example

Here is the code for the class that is necessary to create a minimal module that is displayed on the screen. The module simply draws a dot at the origin that changes colour depending on whether it is activated or not. It has one parameters called colour that is used to determine the colour when active. This module is also in the examples in the SVN and can be copied into the main source code to see how it works. Copy the whole stub folder into the /src/ folder and add the following line to /expbuilder/app/components/components.xml:

    <Component type="Stub" importPath="stubs.stubComponent" />

#### stubComponent.py

    """
    Stub module that shows the interface for creating new components.

    @author: Tobias Leugger
    @since: June 2010
    """

    from pyglet.gl import *

    from scene.scene import SceneElement
    from abstract.AbstractClasses import DrawableModule

    class ModuleMain(SceneElement, DrawableModule):
        """
        Main class of this module
        """
        defaultInitConf = {
            'name': 'stubby'            # any string
        }

        defaultRunConf = {
            'colour': '[1., 0., 0.]'    # the colour of the point to draw
        }

        confDescription = [
            ('name', 'str', "Name of this component"),
            ('colour', 'str', "Colour when active"),
        ]

        def __init__(self, controller, initConfig=None, runConfigs=None):
            SceneElement.__init__(self)
            DrawableModule.__init__(self, controller, initConfig, runConfigs)

            self.active = False

        def draw(self, window_width, window_height, eye=-1):
            """
            Draws a dot at the origin, the colour depends on whether the component is active or not
            """
            DrawableModule.draw(self, window_width, window_height, eye)
            colour = eval(self.activeConf['colour'])
            if self.active:
                glColor3f(colour[0], colour[1], colour[2])
            else:
                glColor3f(1., 1., 1.)
            glPointSize(20)
            glBegin(GL_POINTS)
            glVertex3f(0, 0, 0)
            glEnd()

        def start(self, dt=0, duration=-1, configName=None):
            """
            Activate this module
            """
            DrawableModule.start(self, dt, configName)

            # activate
            self.active = True

        def stop(self, dt=0):
            DrawableModule.stop(self, dt)

            # stop
            self.active = False

Back to contents

## Editing the Documentation

As you might have noticed, the documentation consists of simple HTML files. Take your HTML editor of choice and keep the documentation up-to-date! To show that you did, don't forget to add your name and to update the Last update in the footer of the page you edited. If you don't have an editor of choice, you might want to try SeaMonkey that contains a decent what-you-see-is-what-you-get HTML editor.

Back to contents

Last update: 8.11.2011 | Author: Tobias Leugger