Click here to Skip to main content
15,886,919 members
Articles / Programming Languages / Python
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
3 Jun 2015MIT2 min read 19.7K   127   3   4
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


Written By
Spain Spain
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhere is initlabel_module() defined? Pin
Member 1207908422-Oct-15 4:10
Member 1207908422-Oct-15 4:10 
AnswerRe: Where is initlabel_module() defined? Pin
TUKET BO22-Oct-15 21:48
TUKET BO22-Oct-15 21:48 
BugNice article - but Pictures aren't working Pin
C3D15-Jun-15 4:34
professionalC3D15-Jun-15 4:34 
GeneralRe: Nice article - but Pictures aren't working Pin
TUKET BO18-Jun-15 5:17
TUKET BO18-Jun-15 5:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.