Embed Python 3.5 with tkinter support on Windows Embed Python 3.5 with tkinter support on Windows tkinter tkinter

Embed Python 3.5 with tkinter support on Windows


Disclaimer

This answer does not purport to be the correct or best way to embed Python 3.5 with Tkinter support. The step-by-step format only reflects the fact that this was how I managed to get everything working on my machine, and since I am unable to test this solution elsewhere, I cannot confirm that it will work in all or even most cases.


How I did it

  1. Create include, lib, lib\python35 and src directories in theproject root directory.
  2. Copy all files inside of path\to\python35\include to the include directory in the project root directory.
  3. Zip all files inside of path\to\python35\Lib into a single file called stdlib.zip and put it in the project root directory.¹
  4. Copy all files inside of path\to\python35\DLLs to the lib\python35 directory in the project root directory. The _tkinter.pyd library file should be inside.²
  5. Copy the libpython35.a import library from path\to\python35\libs to the lib directory in the project root directory.
  6. Create a main.py file inside of the src directory in the project root directory with the following contents:

    import tkinter as tkdef run():    root = tk.Tk()    root.mainloop()
  7. Zip main.py into a single file called source.zip and put it in the project root directory.
  8. Create a main.c file inside of 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;}
  9. Create a CMakeLists.txt file in the project root directory 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)
  10. Build and run. If you did everything correctly up to 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

    Tcl and Tk directories are nowhere to be found. We need to bring those in and update the TCL_LIBRARY enviroment variable.

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

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

Everything should work now.

¹ This is not strictly necessary. You could just as well keep your .py files in a directory and append its path to sys.path.

² The reason why python was raising an ImportError before was because _tkinter.pyd was inside a zip file and thus could not be loaded.


Just updating after Jovito answered everything to specify how to make it a light installation (steps 3 to 5 this saves space):

Copy the _tkinter.pyd file mine is in \Pythonpath\DLLs\ into the directory you have the python35.dll located for your embedded installation. You also need 2 DLLs for tkinter to work in the same location tcl86t.dll and tk86t.dll

You need these directories: \Pythonpath\Lib\tkinterand \Pythonpath\tcl\tcl8.6 and \Pythonpath\tcl\tk8.6 and have to set in your main.py script as shown below:

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

That makes Jovito's answer as lightweight as possible. Use the rest of his answer. Works for me.