C++ library python binding generation with Shiboken - python

I followed the tutorial up on the wiki http://qt-project.org/wiki/PySide_Binding_Generation_Tutorial
but I can't get it to work properly. I'm on MacOSX
So far here's what I did:
Build FooLib (static) ---> libFooLib.a
Create the typesystem_foo.xml
Run shiboken with the following command:
shiboken-2.7 global.h --include-paths=.:/opt/local/include/PySide-2.7:/opt/local/include --typesystem-paths=/opt/local/share/PySide-2.7/typesystems --output-directory=../FooLibBinding typesystem_foo.xml
Build the FooLibBinding dynamic library from the resulted generated c++ code --> libFooLibBinding.dylib
Now instead of just running python interpreter from the command line, I made a C++ program which would load the python interpreter and open a .py script using the FooLib. This program links dynamically against libFooLibBinding.dylib so I guess all the symbols needed for the foolib module to work are there;)
here's the code:
#include <iostream>
#include <Python.h>
int main(int argc, char* argv[])
{
///Python init
Py_SetProgramName(argv[0]);
Py_Initialize();
PySys_SetArgv(argc, argv); /// relative module import
///Try out loading the module, this is just for testing
/// -----------
PyObject *sysPath = PySys_GetObject("path");
PyObject *path = PyString_FromString("/Users/alexandre/Downloads/BindingTest");
int result = PyList_Insert(sysPath, 0, path);
PyObject *pModule = PyImport_ImportModule("foolib");
if (PyErr_Occurred())
PyErr_Print();
/// -----------
///Our python file to interpret
const char* filename = "/Users/alexandre/Downloads/BindingTest/FooLibTest/foolib_test.py";
FILE* file = fopen(filename,"r");
PyRun_SimpleFile(file,filename);
///close python
Py_Finalize();
return 0;
}
When running the program, it fails when trying to load the module a first time saying:
ImportError: No module named foolib
And then a second time when running the .py script :
Traceback (most recent call last):
File "/Users/alexandre/Downloads/BindingTest/FooLibTest/foolib_test.py", line 1, in <module>
from foolib import FooClass
ImportError: No module named foolib
So obviously it cannot find the module generated from the bindings. My question is what should I do so it can find it ?
The tutorial uses a Makefile but doesn't seem to do much more than just linking the binding dynamic library.

Your include path for the Shiboken command line doesn't include the path to your foo.h. I can't tell if this is the cause of your problem, but if I did the same, it won't generate the following files:
math_wrapper.cpp
math_wrapper.h
...which you'd obviously need to be able to compile support for the Maths class in the foo library.

Related

Embed / Include Python.h into C++ [Full Guide] (Python 3.9) (Windows) (Qt 5.15) [duplicate]

This question already has answers here:
how can i include python.h in QMake
(1 answer)
Embedding python 3.4 into C++ Qt Application?
(4 answers)
Closed 2 years ago.
When I was trying to embed a Python script into my Qt C++ program, I run into multiple problems when trying to include Python.h.
The following features, I would like to provide:
Include python.h
Execute Python Strings
Execute Python Scripts
Execute Python Scripts with Arguments
It should also work when Python is not installed on the deployed machine
Therefore I searched around the Internet to try to find a solution. And found a lot of Questions and Blogs, but non have them covered all my Problems and it still took me multiple hours and a lot of frustration.
That's why I have to write down a StackOverflow entry with my full solution so it might help and might accelerate all your work :)
(This answer and all its code examples work also in a non-Qt environment. Only 2. and 4. are Qt specific)
Download and install Python https://www.python.org/downloads/release
Alter the .pro file of your project and add the following lines (edit for your correct python path):
INCLUDEPATH = "C:\Users\Public\AppData\Local\Programs\Python\Python39\include"
LIBS += -L"C:\Users\Public\AppData\Local\Programs\Python\Python39\libs" -l"python39"
Example main.cpp code:
#include <QCoreApplication>
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
/*!
* \brief runPy can execut a Python string
* \param string (Python code)
*/
static void runPy(const char* string){
Py_Initialize();
PyRun_SimpleString(string);
Py_Finalize();
}
/*!
* \brief runPyScript executs a Python script
* \param file (the path of the script)
*/
static void runPyScript(const char* file){
FILE* fp;
Py_Initialize();
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
runPy("from time import time,ctime\n"
"print('Today is', ctime(time()))\n");
//uncomment the following line to run a script
//runPyScript("test/decode.py");
return a.exec();
}
Whenever you #include <Python.h> use the following code instead. (The Slots from Python will otherwise conflict with the Qt Slots
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
After compiling, add the python3.dll, python39.dll, as well as the DLLs and Lib Python folders to your compilation folder. You can find them in the root directory of your Python installation. This will allow you to run the embedded c++ code even when python is not installed.
With these steps, I was able to get python running in Qt with the 64 bit MinGW and MSVC compiler. Only the MSVC in debug mode got still a problem.
FURTHER:
If you want to pass arguments to the python script, you need the following function (It can be easy copy-pasted into your code):
/*!
* \brief runPyScriptArgs executs a Python script and passes arguments
* \param file (the path of the script)
* \param argc amount of arguments
* \param argv array of arguments with size of argc
*/
static void runPyScriptArgs(const char* file, int argc, char *argv[]){
FILE* fp;
wchar_t** wargv = new wchar_t*[argc];
for(int i = 0; i < argc; i++)
{
wargv[i] = Py_DecodeLocale(argv[i], nullptr);
if(wargv[i] == nullptr)
{
return;
}
}
Py_SetProgramName(wargv[0]);
Py_Initialize();
PySys_SetArgv(argc, wargv);
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
for(int i = 0; i < argc; i++)
{
PyMem_RawFree(wargv[i]);
wargv[i] = nullptr;
}
delete[] wargv;
wargv = nullptr;
}
To use this function, call it like this (For example in your main):
int py_argc = 2;
char* py_argv[py_argc];
py_argv[0] = "Progamm";
py_argv[1] = "Hello";
runPyScriptArgs("test/test.py", py_argc, py_argv);
Together with the test.py script in the test folder:
import sys
if len(sys.argv) != 2:
sys.exit("Not enough args")
ca_one = str(sys.argv[0])
ca_two = str(sys.argv[1])
print ("My command line args are " + ca_one + " and " + ca_two)
you get the following output:
My command line args are Progamm and Hello

The correct CMakeLists.txt file to call a MAXON libarary in a Python script using pybind11

I'm very new to the whole CMake. Following this and this posts, now I want to call a MAXON function inside Python, using pybind11. What I have done so far:
The library can be downloaded from this page (direct download link).
wget https://www.maxongroup.com/medias/sys_master/root/8837358518302/EPOS-Linux-Library-En.zip
unzip:
unzip EPOS-Linux-Library-En.zip
make the install shell script executable and run it:
chmod +x ./install.sh
sudo ./install.sh
Then going to the example folder:
cd /opt/EposCmdLib_6.6.1.0/examples/HelloEposCmd/
Now combining the CMakeLists.txt files from here:
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project (HelloEposCmd)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(pybind11 REQUIRED)
pybind11_add_module(${PROJECT_NAME} HelloEposCmd.cpp)
add_executable(${PROJECT_NAME} HelloEposCmd.cpp)
target_link_libraries(${PROJECT_NAME} -lEposCmd)
and the HelloEposCmd.cpp this line is added right after other header files:
#include <pybind11/pybind11.h>
the main function is renamed to:
int run(int argc, char** argv)
and the pybind11 syntax to add the module is written at the end:
PYBIND11_MODULE(HelloEposCmd, m) {
m.def("run", &run, "runs the HelloEposCmd");
}
However, When I run the cmake . I get the error:
CMake Error at CMakeLists.txt:13 (add_executable):
add_executable can not create target "HelloEposCmd" because another target with the same name already exists. The existing target is a module library created in source directory "/opt/EposCmdLib_6.6.1.0/examples/HelloEposCmd" See documentation for policy CMP0002 for more details.
...
I was wondering if you could be kind to help me get the right CMakeList.txt file. Ideally, I should be able to call the compiled module in python:
# HelloEposCmd.py
import HelloEposCmd
HelloEposCmd.run()
Thanks for your support in advance.
pybind11_add_module already creates a target for you. So you don't need add_executable anymore. Just remove that line and when you will build you will get a library with the name HelloEposCmd
add_executable is needed if you are building an executable (.exe), which I believe is not what you want.
Documenation of pybind11 says.
This function behaves very much like CMake’s builtin add_library (in fact, it’s a wrapper function around that command).
Thanks to abhilb post and his kind followup in the comments I was able to figure the problem out. well, at least find a temporary workaround:
According to this post, the last two lines of the CMakeLists.txt file should change to
# this line can be removed
# add_executable(${PROJECT_NAME} HelloEposCmd.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE -lEposCmd)
and then because according to this post pybind11 doesn't support double pointers we change the run function to:
int run() {
int argc = 1;
char* argv[] = {"./HelloEposCmd"};
...
}
which I suppose to be a horrible workaround (inspired by information from this page). Now running cmake ., make and python3 HelloEposCmd.py should work properly (except a small c++ warning!).
P.S.1. Maybe someone could use std::vector<std::string> as suggested here. This idea was proposed here and there are already some answers worth investigating.
P.S.2. Following this discussion, another workaround could be something like:
#include <stdio.h>
#include <stdlib.h>
void myFunc(int argc, char* argv[]) {
for (int i = 0; i < argc; ++i) {
printf("%s\n", argv[i]);
}
}
int run(int argc, long* argv_) {
char** argv = (char**)malloc(argc * sizeof(char*));
for (int i = 0; i < argc; ++i) {
argv[i] = (char*)(argv_[i]);
}
myFunc(argc, argv);
free(argv);
return 0;
}

Boost - Importing Qt created C++ Python module in Python

I have the following code in the Qt Quick Application together with Boost.
In this Cpp there is a personal module created using BOOST_PYTHON_MODULE(hello). The main goal is to be able to import hello in Python and call the methods of hello struct. My Python script only contains very simple structure as i just want to see no errors when importing hello.
import hello
print("Import was successful!")
Most of the codes below are copied from a different question in stackoverflow but not entirely so i had to repost the parts.
Main.cpp
#include <cstdlib> // setenv, atoi
#include <iostream> // cerr, cout, endl
#include <boost/python.hpp>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
struct World
{
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
std::string msg;
};
//---------------------------------------------------------------------------------------------------------------------
/// Staticly linking a Python extension for embedded Python.
BOOST_PYTHON_MODULE(hello)
{
namespace python = boost::python;
python::class_<World>("World")
.def("greet", &World::greet)
.def("set", &World::set)
;
}
//---------------------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
namespace python = boost::python;
try
{
int uploaded = PyImport_AppendInittab("hello", &PyInit_hello);
//This executes else part
if(uploaded == -1)
std::cout<< "Module table was not extended: " << uploaded << std::endl;
else
std::cout<< "Module Table was extended" << std::endl;
Py_Initialize();
} catch (...)
{
PyErr_Print();
return 1;
}
return app.exec();
}
Finally, I run my QT application and the return app.exec(); keeps it running while i try and run my python script as mentioned above from the terminal. The python script is in the same directory as the currently running application, not sure if that makes any difference.
Then the error i get is:
Traceback (most recent call last):
File "test_hilton.py", line 1, in <module>
import hello
ModuleNotFoundError: No module named 'hello'
Not sure what i am missing here. According to the Python API:
PyImport_AppendInittab - Add a single module to the existing table of
built-in modules. This is a convenience wrapper around
PyImport_ExtendInittab(), returning -1 if the table could not be
extended. The new module can be imported by the name name, and uses
the function initfunc as the initialization function called on the
first attempted import.
And the If-else part inside the try-catch block inside the main proves that the hello module is being added to the table. Out of ideas on what to do, looked in different places. But still stuck with this part of the problem.
Since the hello module is defined in that Qt program it is available only in that program. Executing the program doesn't make it available to python interpreter that expects to find hello.py or hello.so (the file extension may vary depending on the operating system) when importing hello by import hello.
You need to build a python module, answer might help.

Problems using embedded Python in a C++ application

I'm trying to embed Python in my C++ application, somewhat akin to the method found here in section 1.4:
https://docs.python.org/3.5/extending/embedding.html
The synopsis of the problem I'm having is that I can't get the C++ application to work with .py files that import the 'emb' module, ie, the Python extension module that's written into the C++ code.
I have a Python file, testmod.py:
import emb
# define some functions
def printhello(input):
emb.numargs()
return 2
def timesfour(input):
print(input * 4)
In my C++ application, I have this code which works:
PyImport_AppendInittab("emb", &(mynamespace::PyInit_emb) );
Py_Initialize();
PyObject *globals = PyModule_GetDict(PyImport_AddModule("__main__"));
PyObject *testModule = PyImport_ImportModule("emb");
PyObject* pFunc = PyObject_GetAttrString(testModule, "numargs");
After this, pFunc is non-NULL; things look good. So I think the 'embedded module' is fine.
If I change the last two lines from above to:
PyObject* testModule = PyImport_ImportModule("testmod");
PyObject* pFunc = PyObject_GetAttrString(testModule, "printhello");
This also works fine, provided the line emb.numargs() is removed from testmod.py Once I add that line, and re-run the C++ application, testModule becomes NULL, which means something has gone wrong.
Any ideas?
Is this the way this capability is supposed to be used?

using ctypes to link c++ and python in linux

I am writing a program in python. now i want to use ctypes to use some functions of a class i have in c++.
so basically , i have an array of data in python. i have another program in c++ which is supposed to use that array and transform it to an image class. here is the program i have in c++. i have already made the makefile in linux and it compiles.
the name of the file is 'pclink.cpp'
#include <stdio.h>
#include "MImage.h"
#include<stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
int conv(double* data, int x,int y, int z, int len) {
MImage M;
//M.MLoadFromArray(data,x,y,z,len);
return 0;
}
#ifdef __cplusplus
}
#endif
I have also made the .so and .o files every thing seems fine. but when in python i type
import numpy as np
import ctypes
import os
pclink = np.ctypeslib.load_library('pclink','.')
this is the message i get
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
pclink=np.ctypeslib.load_library('pclink','.')
File "/usr/lib/python2.7/dist-packages/numpy/ctypeslib.py", line 131, in load_library
raise exc
OSError: /home/lucy94/pclink.so: undefined symbol: _ZN6MImage14MLoadFromArrayEPdiiii
previously i tried to link c++ and python with a simple program which does not have any classes and it works fine. it seems the problem is when i try to define an object from another class. so any one knows how to fix the problem?
thanks
Looks like your built .so file depends on another shareable library containing the MImage stuff. I don’t think Python ctypes will automatically load dependent libraries for you, you have to pull them in yourself. And you’ll need to specify the RTLD_GLOBAL flag so the symbols from that MImage library are available to pclink.so when you load that.

Categories