iOS-приложение для iPhone - возможно ли передать аргументы и получить вывод из скрипта Python, встроенного в приложение? - PullRequest
0 голосов
/ 02 июля 2018

Я изучал это некоторое время, но не добился большого успеха. Обратите внимание, что я НЕ пытаюсь написать целое приложение на python (многие результаты поиска указывают на это).

У меня просто есть скрипт на python, который, например, принимает несколько чисел, выполняет какую-то операцию с числами и затем выводит строку. Возможно ли встроить этот скрипт на моем уже существующем приложении Objective C и запустить его из моего приложения? Моему приложению для iOS необходимо передать числа и получить результирующую строку вывода.

Как я могу это сделать?

EDIT:

Мне удалось использовать Pybee для добавления Python в мое приложение и, по крайней мере, для выполнения моего сценария python.

Я использовал эти 2 ресурса: Используйте Python 3.6 (используя cookiecutter для сборки проекта Xcode): https://github.com/pybee/Python-iOS-template

Загрузите пакет поддержки Python Apple для iOS по ссылке выше и добавьте библиотеки в проект Xcode.

После этого я создал свой AppDelegate для проекта XCode, он успешно запустился и распечатал использование моего скрипта в консоли NSLog.

Однако я не могу понять, как передать аргументы скрипту. Есть идеи?

РЕДАКТИРОВАТЬ 2: Я смог заставить это работать. Python_argv и PySys_SetArgv могут использоваться для передачи аргументов! Удивительно!

Вот как мой файл main.m:

//
//  main.m
//  A main module for starting Python projects under iOS.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <Python.h>
#include <dlfcn.h>

int main(int argc, char *argv[]) {
    int ret = 0;
    unsigned int i;
    NSString *tmp_path;
    NSString *python_home;
    NSString *python_path;
    wchar_t *wpython_home;
    const char* main_script;
    wchar_t** python_argv;

    @autoreleasepool {

        NSString * resourcePath = [[NSBundle mainBundle] resourcePath];

        // Special environment to prefer .pyo; also, don't write bytecode
        // because the process will not have write permissions on the device.
        putenv("PYTHONOPTIMIZE=1");
        putenv("PYTHONDONTWRITEBYTECODE=1");

        // Set the home for the Python interpreter
        python_home = [NSString stringWithFormat:@"%@/Library/Python", resourcePath, nil];
        NSLog(@"PythonHome is: %@", python_home);
        wpython_home = Py_DecodeLocale([python_home UTF8String], NULL);
        Py_SetPythonHome(wpython_home);

        // Set the PYTHONPATH
        python_path = [NSString stringWithFormat:@"PYTHONPATH=%@/Library/Application Support/com.blahblah.testapp/app:%@/Library/Application Support/com.blahblah.testapp/app_packages", resourcePath, resourcePath, nil];
        NSLog(@"PYTHONPATH is: %@", python_path);
        putenv((char *)[python_path UTF8String]);

        // iOS provides a specific directory for temp files.
        tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil];
        putenv((char *)[tmp_path UTF8String]);

        NSLog(@"Initializing Python runtime");
        Py_Initialize();

        // Set the name of the main script
        main_script = [
            [[NSBundle mainBundle] pathForResource:@"Library/Application Support/com.blahblah.testapp/app/testapp/app"
                                            ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding];

        if (main_script == NULL) {
            NSLog(@"Unable to locate testapp main module file");
            exit(-1);
        }

        // Construct argv for the interpreter
        python_argv = PyMem_RawMalloc(sizeof(wchar_t*) * argc);

        python_argv[0] = Py_DecodeLocale(main_script, NULL);
        for (i = 1; i < argc; i++) {
            python_argv[i] = Py_DecodeLocale(argv[i], NULL);

        }

        //NSLog(@"ARGS: %@",python_argv);
        python_argv[1]=(wchar_t *)[@"myargumentiwanttopass" cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
        PySys_SetArgv(2, python_argv);

        // If other modules are using threads, we need to initialize them.
        PyEval_InitThreads();

        // Start the main.py script
        NSLog(@"Running %s", main_script);

        @try {
            FILE* fd = fopen(main_script, "r");
            if (fd == NULL) {
                ret = 1;
                NSLog(@"Unable to open main.py, abort.");
            } else {
                ret = PyRun_SimpleFileEx(fd, main_script, 1);
                if (ret != 0) {
                    NSLog(@"Application quit abnormally!");
                } else {
                    // In a normal iOS application, the following line is what
                    // actually runs the application. It requires that the
                    // Objective-C runtime environment has a class named
                    // "PythonAppDelegate". This project doesn't define
                    // one, because Objective-C bridging isn't something
                    // Python does out of the box. You'll need to use
                    // a library like Rubicon-ObjC [1], Pyobjus [2] or
                    // PyObjC [3] if you want to run an *actual* iOS app.
                    // [1] http://pybee.org/rubicon
                    // [2] http://pyobjus.readthedocs.org/
                    // [3] https://pythonhosted.org/pyobjc/

                    UIApplicationMain(argc, argv, nil, @"PythonAppDelegate");
                }
            }
        }
        @catch (NSException *exception) {
            NSLog(@"Python runtime error: %@", [exception reason]);
        }
        @finally {
            Py_Finalize();
        }

        PyMem_RawFree(wpython_home);
        if (python_argv) {
            for (i = 0; i < argc; i++) {
                PyMem_RawFree(python_argv[i]);
            }
            PyMem_RawFree(python_argv);
        }
        NSLog(@"Leaving");
    }

    exit(ret);
    return ret;
}
...