Introduction

I have used ROS to implement our robotics algorithms for years. My usual way to write a ROS program is command-line based:

open emacs

create a package using catkin command, and write programs using c++ or python

configure CMake files for the project

run catkin_make to compile

To run a ros program, also I open emacs or terminal first, then type:

roscore

rosrun or roslaunch

The fact is that I have dozens of launch files to test. For me, the command: roslannch package_name launchfile_name is the most frequently used one, so my hands are always busy on the keyboard.

To improve the user experience of using my program to run various tests and experiments, I want to implement a simple GUI, so that those frequently used operations can be achieved without constant typing commands on the keyboard.

Considering the feature of my research project, the major specification for the simple GUI include:

  • Buttons to start:
    • roscore
    • rviz
    • rosclean
    • catkin_make
    • roslaunch
  • A subwindow to show files:
    • launch files
    • log files
    • lattice files (the graph representations of our lattice patterns)
  • A button with related entry boxes for launch file generation
  • A button with related entry boxes for experiment script generation

For buttons like “start roscore” or “start rviz”, the signal is actually a bash command that starts a new terminal in which executes the related ros commands, for example:

gnome-terminal -x sh -c \"roscore; bash\"

is the signal called by the “start roscore” button.

Implement the entry completion

For command rosrun or roslaunch, it needs arguments to execute. For exmaple, I built a ros package called lattice_controller, and have an XML format launch file named test_sq_3bots.launch, in which multiple rosnodes are configured. Then the roslaunch commnd is:

roslaunch lattice_controller test_sq_3bots.launch

The idea is: create an entry box, each time user enters one launch file name and clicks a “roslaunch” button to execute the roslaunch command. Since I have a lot of launch files under a package, I want to have some typeahead effect like Google’s autocomplete effect in its search box [1]:

google autocomplete

The animated GIF shows how the typeahead works in my RosGUI program.

  1. All existed launch files are listed in the textview region.
  2. User types the first serveral characters of the desired file name, “test”, in the entry box, the GUI lists all existed file names containing the entered characters.
  3. User selects one launch file name “test_sq_3bots.launch” and clicks the “Roslaunch button” to execute roslaunch command

Rosgui entry completion

Fortunately, GTKMM 3 provides entry completion API. The example in the tutorial is helpful [2].

Generally, to use this API, first create a simple class deriving from the Gtk::TreeModelColumnRecord class, which is used to setup a TreeModel, such as Gtk::ListStore or Gtk::TreeStore. Call this class CompletionRecord, it keeps a record of Gtk::TreeModelColumn, that is, the text I want to complete.

// in header file
class CompletionRecord : public Gtk::TreeModel::ColumnRecord{
public:
    CompletionRecord(){
        add(col_text);
    }
    Gtk::TreeModelColumn<Glib::ustring> col_text;
};
Glib::RefPtr<Gtk::EntryCompletion> m_refEntryCompletion;
Glib::RefPtr<Gtk::ListStore> m_refCompletionModel;
Gtk::Entry m_Entry;
// in cpp file
// initiate a TreeModel (ListStore) object using the CompletionRecord object
m_refCompletionModel = Gtk::ListStore::create(m_completionRecord);
// Configure the Completion
m_refEntryCompletion = Gtk::EntryCompletion::create();
// associate the entry completion object with the TreeModel
m_refEntryCompletion->set_model(m_refCompletionModel);
m_refEntryCompletion->set_text_column (m_completionRecord.col_text);
m_refEntryCompletion->set_minimum_key_length(1);
m_refEntryCompletion->set_popup_completion(true);
// configure the TreeModel (liststore) object
// let each row of the tree model show one file name
// read_dir is a function that reads a path of a directory 
// and outputs a list of file names in that folder
vector<string> launch_files = read_dir(launch_path); 
Gtk::TreeModel::Row row;
for(string s : launch_files){
   row =*(m_refCompletionModel->append());
   row[m_completionRecord.col_text] = s;
}
// associate gtk entry with entry completion
m_Entry.set_completion(m_refEntryCompletion);

References

[1] Google Autocomplete

[2] gtkmm-tutorial: entry-completion-example

[3] Stackoverflow: how to recursively list directories in C on linux


Yang Song

Ph.D. Student in Robotics