Openframeworks DatGui

ofxDatGui is a simple to use, fully customizable, high-resolution graphical user interface for OpenFrameworks inspired by the popular JavaScript datgui interface.

Features

ofxDatGui offers the following features & components:

Installation

ofxDatGui is built on top of C++11 and requires the new openFrameworks 0.9.0 release which you can download here.

  1. Once you've downloaded openFrameworks, clone or download this repository into your openFrameworks/addons directory.

  2. Create a new project using the project generator and include ofxDatGui by selecting the addons button in the generator.

  3. Copy the ofxbraitsch directory in the root of this repository to your project's bin/data directory. This directory contains the fonts & icons used by ofxDatGui.

  4. Add ofxDatGui to your project by adding #include "ofxDatGui.h" to the top of your ofApp.h file and you're ready to go!

Basic Usage

There are two ways to work with ofxDatGui. You can either instantiate each component on its own or consolidate them into a gui panel that you can drag around. The individual component examples included in this repository demonstrate how to use each on its own.

To create an ofxDatGui panel that groups components together simply pass in the X and Y coordinates where you would like it to live or use one of the convenient pre-defined anchors.

ofxDatGui* gui = new ofxDatGui( 100, 100 );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::TOP_LEFT );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::TOP_RIGHT );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::BOTTOM_LEFT );
ofxDatGui* gui = new ofxDatGui( ofxDatGuiAnchor::BOTTOM_RIGHT );

Adding components to ofxDatGui is as simple as:

gui->addButton("Click!");

This generates a Basic Button with the label "Click!"

ofxDatGui

Gui Components

Most components can be grouped together into an ofxDatGui panel via the gui->add* method.
Click on any component's name to view its full documentation.

Basic Button

gui->addButton(string label);
ofxDatGui

Toggle Button

gui->addToggle(string label, bool enabled = true);
ofxDatGui

Label

gui->addLabel(string label);
ofxDatGui

Text Input

gui->addTextInput(string label, string value = "");
ofxDatGui

Range Slider

gui->addSlider(string label, float min, float max);
ofxDatGui

Color Picker

gui->addColorPicker(string label, ofColor color = ofColor::black);
ofxDatGui

Dropdown Menu

vector<string> options = {"ONE", "TWO", "THREE", "FOUR"};
gui->addDropdown(options);
ofxDatGui

Groups / Folders

gui->addFolder("My White Folder", ofColor::white);
ofxDatGui

Value Plotter

gui->addValuePlotter(string label, float min, float max);
ofxDatGui

Waveform Monitor

gui->addWaveMonitor(string label, float frequency, float amplitude);
ofxDatGui

Button Matrix

gui->addMatrix(string label, int numButtons)
ofxDatGui

2D Coordinate Pad

gui->add2dPad(string label, ofRectangle bounds);
ofxDatGui

Framerate Monitor

gui->addFRM();
ofxDatGui

Padded Breaks

You can easily space components apart by placing padded breaks between them.

gui->addLabel("Above");
gui->addBreak()->setHeight(10.0f);
gui->addLabel("Stuck in the Middle");
gui->addBreak()->setHeight(10.0f);
gui->addLabel("Below");
ofxDatGui

Headers & Footers

ofxDatGui also provides an optional header and footer that allows you to title your gui, drag it around and conveniently collapse and expand it. The AllComponentsGui example offers a nice demonstration of their use.

Header

gui->addHeader(":: Drag Me To Reposition ::");
ofxDatGui

You can change the header's label just like any other component by calling:

gui->getHeader()->setLabel("PANEL 1");

Adding a header to your gui will allow you to drag it around, however you can easily disable this via:

gui->getHeader()->setDraggable(false);

Or by passing in false as the second argument to the constructor.

gui->addHeader(":: My Stationary Panel ::", false);

Footer

gui->addFooter();
ofxDatGui

Footers have two labels that each map to the gui's expanded and collapsed states.
By default these labels are "Collapse Controls" and "Expand Controls" although you can override these defaults via:

gui->getFooter()->setLabelWhenExpanded("CLOSE PANEL 1");
gui->getFooter()->setLabelWhenCollapsed("EXPAND PANEL 1");

If your gui has a footer you can programmatically open & close it.

// expand the gui //
gui->expand();
// collapse the gui //
gui->collapse();
// toggle the gui open & closed //
gui->toggle();

If your gui doesn't have a footer simply call setVisible to show or hide it.

// hide the gui //
gui->setVisible(false);

Events

ofxDatGuiEvents are designed to be as simple and convenient to work with as possible.
To listen for an event simply register a callback (or assign a lambda) to be executed when an event you care about is fired:

Assigning a callback

gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
    cout << "A button was clicked!" << endl;
}

Assigning a lambda (anonymous function)

gui->onButtonEvent([&](ofxDatGuiButtonEvent e) 
{
    cout << "A button was clicked!" << endl;
}

Every event handler will receive an event object that contains a pointer (called target) to the object that dispatched the event.

gui->addButton("My Button");
gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
    cout << e.target->getLabel() << endl; // prints "My Button"
}

If you saved the pointer returned by gui->add* in a variable you can compare it to the event target to decide how to handle the event.

ofxDatGuiButton* b1 = gui->addButton("Button 1");
ofxDatGuiButton* b2 = gui->addButton("Button 2");
gui->onButtonEvent(this, &ofApp::onButtonEvent);
void onButtonEvent(ofxDatGuiButtonEvent e)
{
    if (e.target == b1){
        cout << "Button 1 was clicked" << endl;
    } else if (e.target == b2){
    //  button 2 was clicked, do something else //
    }
}

However a more convenient way of determining which component dispatched the event is by using the built in is operator.

gui->addButton("Button 1");
gui->addButton("Button 2");

void onButtonEvent(ofxDatGuiButtonEvent e)
{
    if (e.target->is("button 1")){
        cout << "Button 1 was clicked" << endl;
    } else if (e.target->is("button 2")){
    //  button 2 was clicked, do something else //
    }
}

This performs a case-insensitive search against the component's name which by default is the same as its label.


Interactive components dispatch event objects that have properties unique to that type of component.
All ofxDatGuiEvents and their properties are covered in more detail in the Component API.

Component Search

If you're lazy and don't feel like storing your components in variables you can easily retrieve them by name which by default is the same as whatever you set its label to.

ofxDatGuiButton* gui->getButton("My Button"); // button name
ofxDatGuiSlider* gui->getSlider("My Slider"); // slider name

Note: gui->getComponent performs a case-insensitive lookup against the component's name so the following works fine as well.

ofxDatGuiButton* gui->getButton("my bUTTon"); // button name
ofxDatGuiSlider* gui->getSlider("mY sLiDEr"); // slider name

To change a component's label or name simply:

gui->getButton("My Button")->setName("b1");
gui->getButton("b1")->setLabel("Your Button");

If you have multiple components with the same name nested in separate folders just specify the folder to search.

ofxDatGuiButton* gui->getButton("Reset Button", "Folder 1");
ofxDatGuiButton* gui->getButton("Reset Button", "Folder 2");

Otherwise the function will return the first component whose name & type match the query.

Variable Binding

ofxDatGui sliders & coordinate pads can also be bound to object variables. Just pass a reference to the variable to the component and set a range by which to limit its movement.

For example the following snippet binds a circle's position to a range slider and limits its movement to the width & height of the screen.

// draw a circle with a radius of 100px
// and position it in the middle of the screen //
    circle = new Circle(100);
    circle->x = ofGetWidth()/2;
    circle->y = ofGetHeight()/2;

// instantiate a gui and a couple of range sliders //
    gui = new ofxDatGui( ofxDatGuiAnchor::TOP_RIGHT );
    ofxDatGuiSlider* sx = gui->addSlider("CIRCLE X", 0, ofGetWidth());
    ofxDatGuiSlider* sy = gui->addSlider("CIRCLE Y", 0, ofGetHeight());

// bind the circle's x & y movement to the sliders //
    sx->bind(circle->x);
    sy->bind(circle->y);

Here's a video of the sketch generated by the snippet above.

ofParameter Support

ofxDatGui v1.1 introduces limited support for ofParameter (currently sliders only)
To bind an ofParameter to an ofxDatGuiSlider simply pass it into the slider's constructor.

// setup three ofParameters //
    ofParameter<int> p1;
    ofParameter<float> p2;
    ofParameter<int> p3;
    p1.set("position X", 75, 0, 120);
    p2.set("position Y", 200.0f, -40.0f, 240.0f);
    p3.set("position Z", -40, -80, 120);

// and bind them to slider's by passing them into the constructors //
    gui = new ofxDatGui();
    gui->addLabel("gui from of_parameters");
    gui->addSlider(p1);
    gui->addSlider(p2);
    gui->addSlider(p3);
    gui->onSliderEvent(this, &ofApp::onSliderEvent);

Take a look at the ofParameter example for more details.

Automatic Rendering

ofxDatGui automatically updates and draws itself on top of your application so there is no need to call update or draw on it. However you can easily disable this if you like via:

gui->setAutoDraw(false);

Logging

ofxDatGui will softly warn you if you forget to attach an event listener to a component you've created or if you attempt to perform an action on a component that does not exist.

[ERROR] :: Component Not Found : GHOST BUTTON
[WARNING] :: Event Handler Not Set : MY BUTTON

However you can easily suppress these warnings by calling:

ofxDatGuiLog::quiet();

Save & Load Settings

The ability to save and load settings from an external file is currently in development.
Check out the project roadmap for more information.