Я пытаюсь использовать Python, чтобы открыть диалоговое окно, чтобы принять ввод в мое приложение C ++.
Вот очень минимальное представление о том, что я пытаюсь сделать:
#include <iostream>
#include <Python.h>
int main()
{
/* Begin Python Ititialization - only needs to be done once. */
PyObject *ip_module_name = NULL;
PyObject *ip_module = NULL;
PyObject *ip_module_contents = NULL;
PyObject *ip_module_getip_func = NULL;
Py_Initialize();
PyEval_InitThreads();
ip_module_name = PyString_FromString( "get_ip" );
ip_module = PyImport_Import( ip_module_name );
ip_module_contents = PyModule_GetDict( ip_module );
ip_module_getip_func = PyDict_GetItemString( ip_module_contents, "get_ip_address" );
/* End Initialization */
PyGILState_STATE state = PyGILState_Ensure();
PyObject *result = PyObject_CallObject( ip_module_getip_func, NULL );
if( result == Py_None )
printf( "None\n" );
else
printf( "%s\n", PyString_AsString( result ) );
PyGILState_Release( state );
/* This is called when the progam exits. */
Py_Finalize();
}
Однако, когда я вызываю функцию с помощью PyObject_CallObject, приложение перестает работать. Я предполагаю, что это потому, что я использую библиотеку Tk. Я пытался связать свое приложение с _tkinter.lib, tk85.lib, tcl85.lib, tkstub85.lib, tclstub85.lib, и ничего из этого не помогает. Я довольно озадачен …
Вот сценарий:
import Tkinter as tk
from tkSimpleDialog import askstring
from tkMessageBox import showerror
def get_ip_address():
root = tk.Tk()
root.withdraw()
ip = askstring( 'Server Address', 'Enter IP:' )
if ip is None:
return None
ip = ip.strip()
if ip is '':
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
if len(ip.split(".")) is not 4:
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
for octlet in ip.split("."):
x = 0
if octlet.isdigit():
x = int(octlet)
else:
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
if not ( x < 256 and x >= 0 ):
showerror( 'Error', 'Please enter a valid IP address' )
return get_ip_address()
return ip
Редактировать: добавлены мои настройки потоков
добавлять PySys_SetArgv(argc, argv)
(вместе с int argc, char **argv
параметры для main
), и ваш код будет работать.
tk.Tk()
доступ sys.argv
, который не существует, если PySys_SetArgv
был вызван. Это вызывает исключение, которое распространяется из get_ip
и сообщается в Python / C PyObject_CallObject
возврате NULL
, NULL
хранится в result
и перешел к PyString_AsString
, что является непосредственной причиной наблюдаемой аварии.
Несколько замечаний по коду:
Чтобы отладить это, потребовались усилия, потому что код не проверяет ошибки вообще, он слепо нажимает вперед, пока не завершится сбоем из-за передачи указателей NULL. Самое меньшее, что можно сделать, это написать что-то вроде:
if (!ip_module_name) {
PyErr_Print();
exit(1);
}
// and so on for every PyObject* that you get from a Python API call
В реальном коде вы бы не exit()
, но сделай уборку и вернись NULL
(или поднять исключение уровня C ++, или что угодно).
Нет необходимости звонить PyGILState_Ensure
в теме, которую вы уже знаете, содержит GIL. Как документация PyEval_InitThreads
заявляет, что инициализирует GIL и приобретает Это. Вам нужно только повторно получить GIL при вызове в Python из обратного вызова C, который происходит, скажем, из цикла событий набора инструментов, который не имеет ничего общего с Python.
Новые ссылки, полученные от Python должны быть Py_DECREF
«Один раз больше не нужен. Подсчет ссылок может быть опущен в минимальном примере для краткости, но его всегда следует учитывать.
Других решений пока нет …