Incorporer / Inclure Python.h dans C ++ [Guide complet] (Python 3.9) (Windows) (Qt 5.15) [dupliquer]

Dec 07 2020

Lorsque j'essayais d'incorporer un script Python dans mon programme Qt C ++, je rencontrais plusieurs problèmes en essayant d'inclure Python.h. Les fonctionnalités suivantes, je voudrais fournir:

  • Inclure python.h
  • Exécuter des chaînes Python
  • Exécuter des scripts Python
  • Exécuter des scripts Python avec des arguments
  • Cela devrait également fonctionner lorsque Python n'est pas installé sur la machine déployée

J'ai donc cherché sur Internet pour essayer de trouver une solution. Et j'ai trouvé beaucoup de questions et de blogs, mais ils n'ont pas couvert tous mes problèmes et cela m'a quand même pris plusieurs heures et beaucoup de frustration. C'est pourquoi je dois écrire une entrée StackOverflow avec ma solution complète afin que cela puisse vous aider et accélérer tout votre travail :)

Réponses

3 Olgidos Dec 07 2020 at 22:01

(Cette réponse et tous ses exemples de code fonctionnent également dans un environnement non-Qt. Seuls les 2. et 4. sont spécifiques à Qt)

  1. Téléchargez et installez Python https://www.python.org/downloads/release
  2. Modifiez le fichier .pro de votre projet et ajoutez les lignes suivantes (modifiez le chemin python correct):
INCLUDEPATH = "C:\Users\Public\AppData\Local\Programs\Python\Python39\include" 
LIBS += -L"C:\Users\Public\AppData\Local\Programs\Python\Python39\libs" -l"python39"
  1. Exemple de code main.cpp:
#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();
}
  1. Chaque fois que vous #include <Python.h>, utilisez le code suivant à la place. (Les slots de Python seront sinon en conflit avec les slots Qt
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
  1. Après la compilation, ajoutez les dossiers python3.dll, python39.dll, ainsi que les DLL et Lib Python à votre dossier de compilation. Vous pouvez les trouver dans le répertoire racine de votre installation Python. Cela vous permettra d'exécuter le code C ++ intégré même lorsque python n'est pas installé.

Avec ces étapes, j'ai pu faire fonctionner python dans Qt avec le compilateur 64 bits MinGW et MSVC. Seul le MSVC en mode débogage a encore un problème.

PLUS LOIN:

Si vous souhaitez passer des arguments au script python, vous avez besoin de la fonction suivante (elle peut être facilement copiée dans votre 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;
}

Pour utiliser cette fonction, appelez-la comme ceci (par exemple dans votre 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);

Avec le script test.py dans le dossier de test:

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)

vous obtenez la sortie suivante:

My command line args are Progamm and Hello