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:
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.
#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[])
{
gtk_init(&argc, &argv);
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),
NULL
);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
void command( GtkWidget* widget, gpointer data )
{
setLabelText( gtk_entry_get_text( GTK_ENTRY(widget) ) );
}
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.
Py_Initialize();
When you are done with the interpreter, you must release it.
Py_Finalize();
In order to access a C function from the python interpreter, we must declare a python module.
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.
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).
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:
#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();
}
}