Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Python

Embedding Python in C++. Example using Boost Python and GTK+

5.00/5 (1 vote)
3 Jun 2015MIT2 min read 20.4K   129  
In this tutorial, we will learn how to embed Python in a C++ application. In particular, we are going to see an example in which we will be able to interact with a GUI (made with GTK+).

Introduction

In this tutorial, we will learn how to embed Python in a C++ application. In particular, we are going to see an example in which we will be able to interact with a GUI (made with GTK+).

So the result will be something like this:

IMG_HERE

There is one label that we can set to any value. And there is a text entry where you can introduce your Python commands.

Background

I assume you know C++ and some python.

The Guide

First of all, let's create the interface.

C++
#include <gtk/gtk.h>
#include <iostream>
#include <boost/python.hpp>
#include <Python.h>

using namespace std;

GtkWidget *window;
GtkWidget *layout;

GtkWidget *label;
GtkWidget *entry;

void command( GtkWidget* widget, gpointer data );

void setLabelText(const char* text);

int main( int argc, char *argv[])
{ 
    // Initialize GTK
    gtk_init(&argc, &argv);
    
    // create window
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    
    // set vertical layout
    layout = gtk_vbox_new( TRUE, 1 );
    gtk_container_add(GTK_CONTAINER(window), layout);
    
    // create a label and a text entry
    label = gtk_label_new("nothing");
    entry = gtk_entry_new();
    
    // put the label and the entry in the layout
    gtk_box_pack_start( GTK_BOX(layout), label, TRUE, TRUE, 0 );
    gtk_box_pack_start( GTK_BOX(layout), entry, TRUE, TRUE, 0 );
    
    // connect the destroy signal so when you click "the cross" the
    // window closes
    g_signal_connect_swapped
    (
        G_OBJECT(window),
        "destroy",
        G_CALLBACK(gtk_main_quit),
        G_OBJECT(window)
    );
    
    // when enter is pressed in the text entry call "command"
    g_signal_connect
    (
        G_OBJECT(entry),
        "activate",
        G_CALLBACK(command),
        NULL
    );
    
    gtk_widget_show_all(window);

    gtk_main();

    return 0;
}

void command( GtkWidget* widget, gpointer data )
{
    // we will change this line by the python interpreter
    setLabelText( gtk_entry_get_text( GTK_ENTRY(widget) ) );    
}

// set the value of the label
void setLabelText(const char* text)
{    
    gtk_label_set_text
    (
        GTK_LABEL( label ),
        text
    );    
}

If you compile the previous code, you will see that when you press enter, the contents of the entry will be copied to the label.

If you are compiling this on Linux, this command might work:

g++ window.cpp `pkg-config --libs --cflags gtk+-2.0` -I /usr/include/python2.7/ 
-lpython2.7 -lboost_python-py27 -o window

./window

Now let's embed python.
The first thing you need to do is to initialize the python interpreter.

C++
Py_Initialize();

When you are done with the interpreter, you must release it.

C++
Py_Finalize();

In order to access a C function from the python interpreter, we must declare a python module.

C++
BOOST_PYTHON_MODULE( label_module )
{
    using namespace boost::python;
    def( "setLabelText", setLabelText );
}

So from now on, we can access "setLabelText" from python. Well, not yet, we must initialize and import the module in main first.

C++
object main_module = import("__main__");
object main_namespace = main_module.attr("__dict__");

initlabel_module();
exec( "from label_module import *", main_namespace );

We have imported all the variables of the module in the main namespace. You might be asking yourself where is initlabel_module defined. The definition of that function is automatically generated when the module is defined by the BOOST_PYTHON_MODULE macro. So, this macro declares a function by taking the name of the module and prepending "init" (init + label_module).

Finally, when enter is pressed, the content we have written in the text entry must be forwarded to the interpreter. And if there is an error, we would like to handle it (we are just printing the error).

C++
void command( GtkWidget* widget, gpointer data )
{    
    using namespace boost::python;
    
    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");
    
    try
    {
        object obj =
        exec
        (
            gtk_entry_get_text( GTK_ENTRY(widget) ),
            main_namespace
        );        
    }
    catch(error_already_set)
    {
        PyErr_Print();
    }    
}

So that's all. You can write commands in the text entry and set the label text.

Here, you have the complete code:

C++
#include <gtk/gtk.h>
#include <iostream>
#include <boost/python.hpp>
#include <Python.h>

using namespace std;

GtkWidget *window;
GtkWidget *layout;

GtkWidget *label;
GtkWidget *entry;

void command( GtkWidget* widget, gpointer data );

void setLabelText(const char* text)
{    
    gtk_label_set_text
    (
        GTK_LABEL( label ),
        text
    );    
}

BOOST_PYTHON_MODULE( label_module )
{
    using namespace boost::python;
    def( "setLabelText", setLabelText );
}

int main( int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    
    Py_Initialize();
    
    using namespace boost::python;
    
    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");
    
    initlabel_module();
    exec( "from label_module import *", main_namespace );

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    layout = gtk_vbox_new( TRUE, 1 );
    gtk_container_add(GTK_CONTAINER(window), layout);

    label = gtk_label_new("nothing");
    entry = gtk_entry_new();

    gtk_box_pack_start( GTK_BOX(layout), label, TRUE, TRUE, 0 );
    gtk_box_pack_start( GTK_BOX(layout), entry, TRUE, TRUE, 0 );

    g_signal_connect_swapped
    (
        G_OBJECT(window),
        "destroy",
        G_CALLBACK(gtk_main_quit),
        G_OBJECT(window)
    );

    g_signal_connect
    (
        G_OBJECT(entry),
        "activate",
        G_CALLBACK(command),
        label
    );

    gtk_widget_show_all(window);

    gtk_main();
    
    Py_Finalize();

    return 0;
}

void command( GtkWidget* widget, gpointer data )
{    
    using namespace boost::python;
    
    object main_module = import("__main__");
    object main_namespace = main_module.attr("__dict__");
    
    try
    {
        object obj =
        exec
        (
            gtk_entry_get_text( GTK_ENTRY(widget) ),
            main_namespace
        );
        
    }
    catch(error_already_set)
    {
        PyErr_Print();
    }    
}

License

This article, along with any associated source code and files, is licensed under The MIT License