2
votes

I'd like to re-use a Python script that makes use of matplotlib in interactive mode by embedding a call to it from a C application -- what do I need to do in order to get interactive mode in an embedded Python script working?

I set up an extremely simple example using PyRun_SimpleString(); it works when interactive mode is turned off, but when interactive mode is on the plot window only shows up for a brief interval right before the application ends

#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime,sleep\n"
                       "import matplotlib.pyplot as plt\n"
                       "import matplotlib\n"
                       "print(matplotlib.get_backend())\n"
                       "plt.ion()\n"
                       "plt.plot([1,2,3,4,3,5,7])\n"
                       "sleep(5)\n");
    Py_Finalize();
    PyMem_RawFree(program);
    return 0;
}

The backend used by matplotlib is Qt4Agg. When I execute the lines above in the Python interpreter, it behaves as I'd expect. If I change the plt.ion() to a plt.ioff() and put in a plt.show() after the plt.plot() call, I also see the plot.

1

1 Answers

0
votes

I'm assuming when you say, "[w]hen I execute the lines above in the Python interpreter, it behaves as I'd expect," you're referring to running it within the Python command line/REPL. The right way to test one to one behavior with embedded Python would be to instead place your in a python script and then try running it with python script.py.

You'll notice that within a script your code gives a similar problem. I think the trick is to use plt.pause(5) instead of sleep(5), this allows the event loop for the plot GUI to fire and actually draw the window properly (https://stackoverflow.com/a/35119003/11365663).

For REPLs, matplotlib has some extra magic so that the details of the event loop are hidden from you.

This worked for me:

#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime,sleep\n"
                       "import matplotlib.pyplot as plt\n"
                       "import matplotlib\n"
                       "print(matplotlib.get_backend())\n"
                       "plt.ion()\n"
                       "plt.plot([1,2,3,4,3,5,7])\n"
                       "plt.pause(5)\n");
    Py_Finalize();
    PyMem_RawFree(program);
    return 0;
}