- NB: Document based on OpenViBE 1.1.0.
Introduction
This part of the tutorial will introduce the use of Gtk and Glade, to design the GUI which will be called when the user will ask for the “properties” of your driver.
Gtk is a LGPL toolkit for creating Graphic User Interface (GUI), written in C. Glade is a Rapid Development Tool (RAD) that enables quick and easy development of such user interfaces. Glade saves the GUI as XML, then the OpenViBE acquisition server uses the Gtk Builder to load the interface dynamically as needed.
The Gtk toolkit is already installed with the OpenViBE dependencies, but you have to install Glade on your own (http://glade.gnome.org/). Please keep in my mind that OpenViBE use version 2 of Gtk.
The .ui XML files have to be placed in the directory:
contrib/plugins/server-drivers/[YourDriver]/share/
You can look at .ui files of other drivers for examples.
The Configuration Process
The configuration process needs 3 entities:
- The Header object
- The Driver, which contains a Header object
- The Configuration object, which one instance is created in the
configure(void)
function of the driver.
The purpose of the configuration process is to fill the Header with user-defined data. The driver creates an instance of the Configuration given a .ui file name, and calls the configure(&IHeader)
function on the Header member. The user has to fill in the data in the GUI, then when he presses “Apply”, all data stored into the Configuration object will be used to fill the driver Header.
The basic header contains information about:
- The subject: identifier, age, gender
- The channels: number, names
- The acquisition: sampling frequency
- The configuration: impedance checking
See OpenViBEAcquisitionServer::IHeader for more details about this header.
The Generic Oscillator driver uses such basic Header. The configuration GUI looks like this:
The corresponding configure(void)
function in OpenViBEAcquisitionServer::CDriverGenericOscillator
is:
boolean CDriverGenericOscillator::configure(void) { // We use the basic CConfigurationBuilder object, with the corresponding .ui file CConfigurationBuilder m_oConfiguration( OpenViBE::Directories::getDataDir() + "applications/acquisition-server/interface-Generic-Oscillator.ui"); // We configure the Header with it return m_oConfiguration.configure(m_oHeader); }
Most of the time, driver developers will use the basic header with a slightly different configuration. For example, the OpenEEG Modular EEG driver needs the COM port number on which the device is connected. Besides the basic information, user can select the COM port used.
The OpenViBEAcquisitionServer::CConfigurationOpenEEGModularEEG
inherits from OpenViBEAcquisitionServer::CConfigurationBuilder
, and needs a reference to the variable (member of the driver) which will contains the device COM port selected. Thus, the configure(void)
function in the driver looks like this:
boolean CDriverOpenEEGModularEEG::configure(void) { // We use a specialized Configuration object that will fill the // m_ui32DeviceIdentifier of the driver with the right value CConfigurationOpenEEGModularEEG m_oConfiguration(OpenViBE::Directories::getDataDir() + "applications/acquisition-server/interface-OpenEEG-ModularEEG.ui" , m_ui32DeviceIdentifier); // We configure the basic Header return m_oConfiguration.configure(m_oHeader); }
Coding the Configuration class and designing the GUI with Glade
The simplest solution is to start from the CConfigurationBuilder
class and the interface-Generic-Oscillator.ui file. First of all, think about the basic information that the acquisition client will need. You may want to keep what is related to the subject’s information “As is”, but the max-min channels number may change depending on the device, so as the allowed sampling frequencies. Tweak the .ui file according to your device.
Then list what your driver will need besides these information: USB port ? Serial port ? Hostname/port to a third party acquisition software ? You will have to translate this into code:
- Driver class: add new member variables describing these information.
- GUI: use Glade to Drag’n'Drop combo-box, spin-button, labels, etc. to make your own GUI.
- Configuration class: the
CConfigurationBuilder
class is probably not enough, implement your own inherited class.
Your configuration class will need references to the driver variables, and will redefine the preConfigure(void)
and PostConfigure(void)
functions of OpenViBEAcquisitionServer::CConfigurationBuilder
. See the ovasCConfigurationGTecGUSBamp.h/cpp files for a complete example:
preConfigure(void)
: scan USB ports, fill the gtk-combo-box with available devices
boolean CConfigurationGTecGUSBamp::preConfigure(void) { if(!CConfigurationBuilder::preConfigure()) { return false; } ::GtkComboBox* l_pComboBox=GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilderConfigureInterface, "combobox_device")); char l_sBuffer[1024]; int l_iCount=0; boolean l_bSelected=false; // autodetection of the connected device for(uint32 i=1; i<11; i++) { ::HANDLE l_pHandle=::GT_OpenDevice(i); if(l_pHandle) { ::GT_CloseDevice(&l_pHandle); sprintf(l_sBuffer, "USB port %i", i); ::gtk_combo_box_append_text(l_pComboBox, l_sBuffer); if(m_rUSBIndex==i) { ::gtk_combo_box_set_active(l_pComboBox, l_iCount); l_bSelected=true; } l_iCount++; } } if(!l_bSelected && l_iCount!=0) { ::gtk_combo_box_set_active(l_pComboBox, 0); } return true; }
postConfigure(void)
: take the selected device and update the driver reference
boolean CConfigurationGTecGUSBamp::postConfigure(void) { ::GtkComboBox* l_pComboBox=GTK_COMBO_BOX(gtk_builder_get_object(m_pBuilderConfigureInterface, "combobox_device")); if(m_bApplyConfiguration) { int l_iUSBIndex=0; if(::sscanf(gtk_combo_box_get_active_text(l_pComboBox), "USB port %i", &l_iUSBIndex)==1) { m_rUSBIndex=(uint32)l_iUSBIndex; } } if(!CConfigurationBuilder::postConfigure()) { return false; } return true; }
Please note the calls to CConfigurationBuilder::preConfigure(void)
and CConfigurationBuilder::postConfigure(void)
, so that basic header information are set.
Complex configuration
The previous example may not be enough for a more complex device. For example, the bipolar capacities of the VAmp-16 device needed a specific implementation.
Configuring a driver is hardly dependent on the device itself and the interface provided by the manufacturer : API, communication protocol, connection type, etc. We cannot cover here all the possible aspects, but the developer community can help you in the process!
Feel free to use the forum, IRC or mailists. We will help you asap !