Boost - Importing Qt created C++ Python module in Python - 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.

Related

How to call a Python function from C++ on Ubuntu 20.04

I'm currently in a class where I'm asked to establish integration between Python and C++ as we will be working with both later in the class. My instructor has provided some simple C++ and Python code so we can test the integration and has also provided instructions on how to set this up using Visual Studio. I currently only have access to Ubuntu and my (very old) laptop won't be able to handle a VM.
I'm currently using Clion as my IDE and Anaconda as a virtual environment. I've spent most of the day trying to figure this out and I believe the issue is related to my CMake file as I know pretty much nothing about CMake right now, but plan to learn it soon.
The CMakeLists.txt, main.cpp, and myPython.py are all in the main project directory.
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(myProject)
set(CMAKE_CXX_STANDARD 14)
add_executable(myProject main.cpp)
target_include_directories(myProject PUBLIC /usr/include/python3.9)
target_link_directories(myProject PUBLIC /usr/lib/python3.9)
target_link_libraries(myProject PUBLIC python3.9)
For CMakeLists.txt I also tried the following but got the same error/output regardless of which one I used
cmake_minimum_required(VERSION 3.21)
project(myProject)
set(CMAKE_CXX_STANDARD 14)
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
add_executable(myProject main.cpp)
target_link_libraries(myProject PUBLIC Python3::Python)
main.cpp
#include <python3.9/Python.h>
#include <iostream>
#include <string>
using namespace std;
// the main in the provided code is void but Clion gave me an error saying to change it to int
int main()
{
cout << "Start 1 \n";
Py_Initialize();
cout << "2\n";
PyObject* my_module = PyImport_ImportModule("myPython");
cerr << my_module << "\n";
PyErr_Print();
cout << "3\n";
PyObject* my_function = PyObject_GetAttrString(my_module,
"printsomething");
cout << "4\n";
PyObject* my_result = PyObject_CallObject(my_function, NULL);
Py_Finalize();
return 0;
}
myPython.py
import re
import string
def printsomething():
print("Hello from Python!")
The expected output is
Start 1
2
01592AE0 // this will vary since it's the .py files location
3
4
Hello from Python!
what I'm getting when I run it is
Start 1
2
0 // output is red
3
ModuleNotFoundError: No module named 'myPython' // output is red
Since I'm not familiar with CMake and would consider myself a "general user" when it comes to Ubuntu, I may need some additional details on any steps provided.
Add this in your main function after Py_Initialize();
PySys_SetPath(L".:/usr/lib/python3.8");
this is the search path for python module. Add . to search in the current path.

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

How to debug ctypes without error message

I have a simple python script that uses a c/c++ library with ctypes. My c++ library also contains a main method, so I can compile it without the -shared flag and it can be excecuted and it runs without issues.
However, when I run the same code from a python script using ctypes, a part of the c++ program is excecuted (I can tell that from the cout calls). Then the entire application, including the python script, termiantes (I can tell that from the missing cout and print calls). There is no error message, no segfault, no python stacktrace.
My question is: How can I debug this? What are possible reasons for this to happen?
Here is part of the code, however, since there is no error message, I don't know which code is relevant.
import ctypes
interface = ctypes.CDLL("apprunner.so")
interface.start()
print "complete"
.
#include "../../app/ShaderApp.cpp"
#include <iostream>
#include "TestApp.cpp"
TestApp* app = 0;
extern "C" void start() {
app = new TestApp();
cout << "Running from library" << endl;
app->run();
}
int main( int argc, const char* argv[]) {
cout << "Running from excecutable" << endl;
start();
}
Typically you begin from a small mock-up library that just lets you test the function calls from python. When this is ready (all the debug prints are ok) you proceed further. In your example, comment out #include "testapp.cpp" and get the prints to cout working.

How to import a function from python file by Boost.Python

I am totally new to boost.python.
I reviewed a lot of recommending of using boost.python to apply with python, however still not easy to understand and find a solution for me.
What I want is to import a function or class that directly from a python "SourceFile"
Example File:
Main.cpp
MyPythonClass.py
Let's says if there is a "Dog" class in "MyPythonClass.py" with "bark()" function, how do I get callback and send argument in cpp?
I have no idea what I should do!
Please help me!
When one needs to call Python from C++, and C++ owns the main function, then one must embed the Python interrupter within the C++ program. The Boost.Python API is not a complete wrapper around the Python/C API, so one may find the need to directly invoke parts of the Python/C API. Nevertheless, Boost.Python's API can make interoperability easier. Consider reading the official Boost.Python embedding tutorial for more information.
Here is a basic skeleton for a C++ program that embeds Python:
int main()
{
// Initialize Python.
Py_Initialize();
namespace python = boost::python;
try
{
... Boost.Python calls ...
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}
When embedding Python, it may be necessary to augment the module search path via PYTHONPATH so that modules can be imported from custom locations.
// Allow Python to load modules from the current directory.
setenv("PYTHONPATH", ".", 1);
// Initialize Python.
Py_Initialize();
Often times, the Boost.Python API provides a way to write C++ code in a Python-ish manner. The following example demonstrates embedding a Python interpreter in C++, and having C++ import a MyPythonClass Python module from disk, instantiate an instance of MyPythonClass.Dog, and then invoking bark() on the Dog instance:
#include <boost/python.hpp>
#include <cstdlib> // setenv
int main()
{
// Allow Python to load modules from the current directory.
setenv("PYTHONPATH", ".", 1);
// Initialize Python.
Py_Initialize();
namespace python = boost::python;
try
{
// >>> import MyPythonClass
python::object my_python_class_module = python::import("MyPythonClass");
// >>> dog = MyPythonClass.Dog()
python::object dog = my_python_class_module.attr("Dog")();
// >>> dog.bark("woof");
dog.attr("bark")("woof");
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}
Given a MyPythonClass module that contains:
class Dog():
def bark(self, message):
print "The dog barks: {}".format(message)
The above program outputs:
The dog barks: woof
Boost python is used to call cplusplus functions from a python source. Pretty much like the Perl xs module.
If you have a function say bark() in main.cpp, you can use boost python to convert this main.cpp into a python callable module.
Then from python script(assuming link output file is main.so):
import main
main.bark()

C++ library python binding generation with Shiboken

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.

Categories