在Windows上使用tkinter支持嵌入Python 3.5

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (299)

我的项目结构如下所示:

emb
|   CMakeLists.txt
|   main.c
|   python35.lib
|   stdlib.zip
|   _tkinter.pyd
|
+---include
|   |
|   |   abstract.h
|   |   accu.h
|   |   asdl.h
...
|   |   warnings.h
|   |   weakrefobject.h
|
+---build
|   |   emb.exe

stdlib.zip包含Python 3.5.2安装中的DLLLibsite-packages目录,其路径被追加到sys.path。我通过链接到python35.lib隐式加载python35.dllpython35.lib包含DLL中所有导出函数的存根。这是CMakeLists.txt的内容:

cmake_minimum_required(VERSION 3.6)
project(embpython)

set(SOURCE_FILES main.c)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})

set(PYTHON_INCLUDE_DIR include)
include_directories(${PYTHON_INCLUDE_DIR})

target_link_libraries(
        ${PROJECT_NAME}
        ${CMAKE_CURRENT_LIST_DIR}/python35.lib
        ${CMAKE_CURRENT_LIST_DIR}/_tkinter.pyd)

这是main.c的内容:

#include <Python.h>

int main(int argc, char** argv)
{
    wchar_t* program_name;
    wchar_t* sys_path;
    char* path;

    program_name = Py_DecodeLocale(argv[0], NULL);
    if (program_name == NULL)
    {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program_name);

    path = "stdlib.zip;stdlib.zip/DLLs;stdlib.zip/Lib;"
        "stdlib.zip/site-packages";
    sys_path = Py_DecodeLocale(path, NULL);
    Py_SetPath(sys_path);

    Py_Initialize();

    PySys_SetArgv(argc, argv);

    PyRun_SimpleString("import tkinter\n");

    Py_Finalize();
    PyMem_RawFree(sys_path);
    PyMem_RawFree(program_name);
    return 0;
}

现在,这是我得到的错误:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File " ... emb\stdlib.zip\Lib\tkinter\__init__.py", line 35, in <module>
ImportError: DLL load failed: The specified module could not be found.

我做错了什么,我该如何解决?

提问于
用户回答回答于

放弃

这个答案并不是使用Tkinter支持嵌入Python 3.5的正确或最佳方法。逐步格式只反映了这样的事实,即我设法让所有东西都在我的机器上工作,而且由于我无法在其他地方测试这个解决方案,我无法确认它是否适用于所有甚至大多数情况。

我是怎么做到的

  1. 在项目根目录中创建includeliblib \ python35src目录。
  2. path \ to \ python35 \ include中的所有文件复制到项目根目录中的include目录。
  3. path \ to \ python35 \ Lib中的所有文件压缩到一个名为stdlib.zip的文件中,并将其放在项目根目录中
  4. path \ to \ python35 \ DLLs中的所有文件复制到项目根目录中的lib \ python35目录。该_tkinter.pyd库文件应inside.²
  5. libpython35.a导入库从path \复制到\ python35 \ libs到项目根目录中的lib目录。
  6. 在项目根目录的src目录中创建一个main.py文件,其中包含以下内容: import tkinter as tk def run(): root = tk.Tk() root.mainloop()
  7. main.py压缩到名为source.zip的单个文件中,并将其放在项目根目录中。
  8. 在项目根目录的src目录中创建一个main.c文件,其中包含以下内容: // WARNING: I did not check for errors but you definitely should! #import <Python.h> static const char* SYS_PATH = "source.zip;stdlib.zip;lib/python35"; int main(int argc, char** argv) { wchar_t* program = NULL; wchar_t** wargv = NULL; wchar_t* sys_path = NULL; int i; program = Py_DecodeLocale(argv[0], NULL); Py_SetProgramName(program); sys_path = Py_DecodeLocale(SYS_PATH, NULL); Py_SetPath(sys_path); Py_Initialize(); wargv = (wchar_t**) malloc(argc * sizeof(wchar_t*)); for (i = 0; i < argc; i++) wargv[i] = Py_DecodeLocale(argv[i], NULL); PySys_SetArgv(argc, wargv); PyRun_SimpleString("import main\n" "main.run()\n"); Py_Finalize(); PyMem_RawFree(program); PyMem_RawFree(sys_path); for (i = 0; i < argc; i++) PyMem_RawFree(wargv[i]); free(wargv); return 0; }
  9. 使用以下内容在项目根目录中创建CMakeLists.txt文件: cmake_minimum_required(VERSION 3.6) project(emb) set(SOURCE_FILES src/main.c) add_executable(emb ${SOURCE_FILES}) include_directories(include) add_library(libpython35 STATIC IMPORTED) set_property( TARGET libpython35 PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/lib/libpython35.a) target_link_libraries(emb libpython35)
  10. 建立并运行。如果到目前为止你做的一切都正确,你应该看到这样的事情: Traceback (most recent call last): File "<string>", line 2, in <module> File "C:\path\to\project\stdlib.zip\tkinter\__init__.py", line 1868, in __init__ _tkinter.TclError: Can't find a usable init.tcl in the following directories: C:/path/to/project/lib/lib/tcl8.6 C:/path/to/project/lib/tcl8.6 C:/path/to/project/library C:/path/to/project/tcl8.6.4/library Tcl和Tk目录无处可寻。我们需要将它们带入并更新TCL_LIBRARY环境变量。
  11. tcl8.6tk8.6目录从C:\ path \复制到\ python35 \ tcl到项目根目录中的lib目录。
  12. 创建并设置TCL_LIBRARY环境变量"lib\tcl8.6"

现在一切都应该有效。

¹ 这不是绝对必要的。您也可以将.py文件保存在目录中并将其路径追加到sys.path

² 之前python引发的原因ImportError是因为_tkinter.pyd在一个zip文件中,因此无法加载。

用户回答回答于

只需在Jovito回答完所有内容后进行更新,以指定如何使其轻松安装(步骤3到5,这样可以节省空间):

_tkinter.pyd我所在的文件复制\Pythonpath\DLLs\到您python35.dll为嵌入式安装找到的目录中。您还需要2个DLL,能够tkinter在同一个地方工作tcl86t.dlltk86t.dll

您需要这些目录:\Pythonpath\Lib\tkinter\Pythonpath\tcl\tcl8.6\Pythonpath\tcl\tk8.6并且必须在main.py脚本中设置,如下所示:

import os
os.environ['TCL_LIBRARY'] = "tcl//tcl8.6"
os.environ['TK_LIBRARY'] = "tcl//tk8.6"

这使得Jovito的答案尽可能轻巧。使用他的其余答案。适合我。

扫码关注云+社区

领取腾讯云代金券