Paste Python 3.5 with tkinter support on Windows

My project structure is as follows:

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 contains the DLLs, Libs , and site directories from the Python 3.5.2 installation, whose paths are added to sys.path . I implicitly load python35.dll , referencing python35.lib , which contains stubs for all exported functions in the DLL. Here is the contents of 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) 

And here is the contents of 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; } 

Now here is the error I get:

 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. 

What am I doing wrong and how to fix it?

+6
source share
2 answers

How i did it

  • Create the include, lib, lib \ python35 and src directories in the project root directory.
  • Copy all the files inside the path \ to \ python35 \ include to the include directory in the root directory of the project.
  • Lock all the files inside the \ to \ python35 \ Lib path into a single file named stdlib.zip and place it in the root directory of the project.ยน
  • Copy all the files inside the \ to \ python35 \ DLL path to the lib \ python35 directory in the project root directory. The _tkinter.pyd library file must be inside .ยฒ
  • Copy the import library libpython35.a from the path \ to \ python35 \ libs to the lib directory in the root directory of the project.
  • Create a main.py file inside the src directory in the project root directory with the following contents:

     import tkinter as tk def run(): root = tk.Tk() root.mainloop() 
  • Zip main.py into a single file called source.zip and place it in the root directory of the project.
  • Create a main.c file inside the src directory in the project root directory with the following contents:

     // 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; } 
  • Create the CMakeLists.txt file in the root directory of the project with the following contents:

     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) 
  • Create and run. If you did everything right before this point, you should see something like this:

     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 

    The Tcl and Tk directories were not found anywhere. We need to cast them and update the TCL_LIBRARY environment variable.

  • Copy the tcl8.6 and tk8.6 directories from the C: \ path \ to \ python35 \ tcl directory to the lib directory in the project root directory.

  • Create and set the environment variable TCL_LIBRARY to "lib\tcl8.6" .

Now everything should work.


Renouncement

This answer does not claim to be the best solution for embedding Python 3.5 with Tkinter support. The step-by-step format reflects only the fact that I managed to get everything that works on my machine, and since I could not verify this solution elsewhere, I can not confirm that it will work in all or even in most cases.


ยน This is not strictly necessary. You could also save your .py files in a directory and add your sys.path .

ยฒ The reason python raised ImportError earlier was because _tkinter.pyd was inside a zip file and therefore could not be loaded.

+2
source

Just an update after Jovito answered everything to indicate how to make it easy to install (steps 3 through 5 this saves space):

Copy the _tkinter.pyd my mine file in \Pythonpath\DLLs\ to the directory where python35.dll is located for your built-in installation. You also need two DLLs for tkinter to work in one place tcl86t.dll and tk86t.dll

You need these directories: \Pythonpath\Lib\tkinter and \Pythonpath\tcl\tcl8.6 and \Pythonpath\tcl\tk8.6 and must be installed in the main.py script, as shown below:

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

This makes Jovito as light as possible. Use the rest of his answer. It works for me.

0
source

Source: https://habr.com/ru/post/1012156/


All Articles