Related Documentation:
This document outlines the design process employed in the development of the g-tube, a graphical user interface which brings all components of the GENESIS3 system together. With the g-tube a user can load, create and run models, track all aspects of the simulation including the schedule, parameters, models, underlying software versions, and directly load the output of simulation runs into Atoms that can be processed into tables, plots, and formatted text suitable for inserting into manuscripts. The end result of a g-tube project will be a compiled electronic publication, containing the finalized document, with all of the data used to generate the figures tag-associated with the model experiment runs.
User interfaces for running simulations have always been difficult to create due to the structure of the simulators they are meant to be used for. The desire for a unified graphical environment for being able to load, run, display output, and save your simulation data has been coveted for quite some time. In GENESIS 2 a model was composed of a series of scripts, which were essentially programs often containing their own logic and flow control. Building a GUI on top of such logic is very difficult as inputs, outputs and internal data structures of the model can all be changed within the logic of the models scripts, thus making it difficult for the GUI logic to track and control all of possible use cases. With the emergence of declarative formats in computational neuroscience, the ability to build interfaces on top of models in a more general way has become more feasible.
A project is a class data structure that is used to track all work done by the user within the g-tube. Projects are treated as a combination of categorized lists of data that grow and shrink as the user goes through the user workflow. Data tracked includes:
From a file storage perspective a project consists of a directory containing all files in the users workspace along with a yaml file that bears the same name as the project. The yaml file provides a mapping for all of the files in the project directory so that they can be loaded by the appropriate data management objects within the GUI.
The g-tube is set up with a set of ”management” classes; their purpose is to provide high level control for the GUI to work with all of the underlying systems, without allowing code from unrelated systems to interlace with one another. This separation allows these complex data management classes to be developed and tested separately.
The Atom management object allows for basic list operations such as adding, deleting, and editing members. Manuscript and Review managers are subclasses of the Atom manager, that display tag-filtered views of atoms in the project workspace. Management objects encapsulate wxPython operations for allowing the end user to perform these actions graphically by drawing their control widgets to the Main Frame.
Due to the nice separation of tasks in the GENESIS user workflow it is easy to disseminate responsibilities amongst higher level management classes.
To constrain the complexities of interfacing with the whole of the GENESIS3 system, modeling functionality is isolated to a single class. In principle this should allow other simulators to be encapsulated within the same abstract data type so long it is mapped to the predefined inputs and outputs the SimulatorManager uses to communicate with the rest of the system. Currently interfacing is done via the gshell over file descriptors via the object class called bridge.
To navigate the various files the user works with in a project there are four tabs which correspond to each management class. Each tab functionality is categorized as such:
Due to the unique requirements for some neuroscience projects, some aspects of the software may need to be changed. Since many newer users feel more comfortable using a user interface, allowing the gtube to let users drop in their own implementations can be beneficial. Using the powerful Python language the gtube will employ a true plugin based system for added extensibility beyond the scope of the initial development. Among attributes that will be extendable are:
Below is an example of the levels of abstraction needed to create a usable plugin system for simulator management.
|
The Simulator object is an abstraction of the User Workflow. This allows for a neater separation of code that is specific to their own tasks within the workflow. The top level of the object contains the operations necessary to start, stop, and detect if the simulator is running while internal logic saves simulator state, results, and command history.
|
The Simulation Manager is an object that allows users to create and track simulator objects. One of its core features is the Simulator Registry, an object that allows dynamic loading of simulator modules as well as dynamic instantiation of simulator objects. With the Simulator Registry a core user requirement is fulfilled; it makes it possible to use other simulators (as well as specialized versions of genesis3) with the user workflow and thus the gtube.
A simulator plugin consists of directory which contains the Python implementation for a wrapper to a simulator, and a file named ”simulator.yml,” which acts as a mount point for the plugin. Every plugin must adhere to a particular set of naming conventions in order for it to work. The top level class must be named ”SimulatorPlugin,” such that creating an object of its type gives a user complete access to all of the required top level methods needed for progressing through the user workflow (Figure 4). Since the plugins are dynamically loaded, any implementation can be loaded without any modifications to the core source, so long as it follows these code guidelines and contains the appropriate simulator.yml file.
An example simulator.yml file is shown here:
---
name: simple_purkinje label: Simple Purkinje Cell version: 0.1 description: This is a module that holds data from the simple purkinje cell test case. file: simple_purkinje.py source: simple_purkinje.py module: simple_purkinje |
The keys in the file are:
A simulator specification needs at least one of the following options defined for it to be properly loaded: file, source, or module.
Presentation of widgets is implemented with the wxPython Advanced User Interface (AUI) toolkit. This allows for several dockable panes that the user can rearrange to their liking.
The implementation of the g-tube is done in Python using the wxPython widget toolkit. The wxPython toolkit is a Python extension module of the popular wxWidgets coss-platform GUI library. This allows the user to create widgets with the look and feel of the host operating system while using the powerful Python language. wxPython is open source, has many contributors, boasts a large collection of example tutorials and documentation, and possesses one of the friendliest communities.
The following Python dependencies are also required:
Higher level GUI construction is done using XRCed, a simple graphical editor that creates Python classes with embedded (or externally loaded) XRC files.
To ensure maximum compatibility across different versions of python, coding will follow the Python Code Style Guide with the exception of using camel case for module names, a convention inherited from wxPython.
Python does not have any truly private members within classes, so prefixing with ”_” is used for weak privatizing of operations or attributes, and ”__” is used for strong privatizing (also called ”name mangling”). Example:
class foo:
def __init__(self,value): self.__my_value = value # top level methods def pub_get_value(self): return self.__priv_get_value() # private methods def __priv_get_value(self): return self.__my_value if __name__ == ’__main__’: bar = foo(21) print "My value is %s\n" % bar.pub_get_value() |
The result of this script is ”My value is 21”. The value is set to a private internal variable __my_value. The public method pub_get_value calls the private method __priv_get_variable()) to retrieve the value. A direct call to __priv_get_value( like this:
if __name__ == ’__main__’:
bar = foo(21) print "My value is %s\n" % bar.__priv_get_value() |
will produce an error. If the private function was declared with weak privatizing using a single underscore like, _priv_get_value(), then it would be possible to call it from the main as follows:
if __name__ == ’__main__’:
bar = foo(21) print "My value is %s\n" % bar._priv_get_value() |
Privatizing was typically considered ”Unpythonic,” however due to potential program breaking name conflicts which can occur during inheritance, it is common to privatize methods that are not to be overridden. The gtube, being a program that makes use of many object relationships, uses private members where appropriate.
For UML and other software figure drawing a ”project” directory has been placed in the top level source code directory. In this directory there are project files for:
As the project increases in scope and size, engineering tools such as this help to keep the project manageable, especially for programmers wishing to modify existing code.