How to compile python 3.4.3 script with the module tkinter and ttk to an self-executable exe (standalone)? (py2exe, pyinstaller, freeze doesn't work.) any suggestions? Thank You
What I do is
download Portable Python
create an file in an other language that can be compiled to exe
make that executable call portable Python with my Python file.
Structure:
application_folder # the folder where everything is in
+--my_python_folder # the folder where your python files are in
| +--my_program.py # the python file that you want to start
+--Portable Python 3 # the Python version that you use
+--program.exe # the compiled program
The C++ source code:
// based on https://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain( int argc, TCHAR *argv[] )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
// choose between pythonw.exe and python.exe
TCHAR command[] = "\"Portable Python 3\\App\\pythonw.exe\" \"my_program.py\"";
// the directory where you start the Python program in
TCHAR directory[] = "my_python_folder";
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
if( !CreateProcess( NULL, // No module name (use command line)
command, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
directory, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d).\n", GetLastError() );
return 1;
}
/*
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
*/
return 0;
}
You can compile the file with devc++.
Evaluation
Pros:
An other way to do it.
Cons:
Needs whole Portable Python.
No command line arguments are passed.
You can do it with a .bat file and it works, too.
The current working directory is different than the caller's one.
Related
I have a c++ (VS2019) app
#include <stdio.h>
#include <Python.h>
void main(int argc, char *argv[])
{
printf( "- before\n" );
FILE* file;
Py_SetPath( L"c:\\Python37\\Lib" );
Py_Initialize();
file = fopen( "abc.py", "r" );
PyRun_SimpleFile( file, "abc.py" );
Py_Finalize();
printf( "- after\n" );
return;
}
that calls a python (3.10) script abc.py
print( "hi" );
When run (on Win11) from cmd (main.exe), the output is:
- before
hi
- after
However, when diverted into a file (main.exe > out.txt, or used in a pipe to tee), I get:
hi
- before
- after
In my real app, where I interleave python with c++, the python output is printed after the script terminates (again, only when diverting the app's output).
Adding
setbuf( stdout, NULL );
, like #retired-ninja suggested, resolves the simple example above. However, in my more complicated app, it doesn't. I need to find a new simple example.
I only needed to add auto-flush to my python script to resolve the issue:
sys.stdout.reconfigure(line_buffering=True)
This question already has answers here:
how can i include python.h in QMake
(1 answer)
Embedding python 3.4 into C++ Qt Application?
(4 answers)
Closed 2 years ago.
When I was trying to embed a Python script into my Qt C++ program, I run into multiple problems when trying to include Python.h.
The following features, I would like to provide:
Include python.h
Execute Python Strings
Execute Python Scripts
Execute Python Scripts with Arguments
It should also work when Python is not installed on the deployed machine
Therefore I searched around the Internet to try to find a solution. And found a lot of Questions and Blogs, but non have them covered all my Problems and it still took me multiple hours and a lot of frustration.
That's why I have to write down a StackOverflow entry with my full solution so it might help and might accelerate all your work :)
(This answer and all its code examples work also in a non-Qt environment. Only 2. and 4. are Qt specific)
Download and install Python https://www.python.org/downloads/release
Alter the .pro file of your project and add the following lines (edit for your correct python path):
INCLUDEPATH = "C:\Users\Public\AppData\Local\Programs\Python\Python39\include"
LIBS += -L"C:\Users\Public\AppData\Local\Programs\Python\Python39\libs" -l"python39"
Example main.cpp code:
#include <QCoreApplication>
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
/*!
* \brief runPy can execut a Python string
* \param string (Python code)
*/
static void runPy(const char* string){
Py_Initialize();
PyRun_SimpleString(string);
Py_Finalize();
}
/*!
* \brief runPyScript executs a Python script
* \param file (the path of the script)
*/
static void runPyScript(const char* file){
FILE* fp;
Py_Initialize();
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
runPy("from time import time,ctime\n"
"print('Today is', ctime(time()))\n");
//uncomment the following line to run a script
//runPyScript("test/decode.py");
return a.exec();
}
Whenever you #include <Python.h> use the following code instead. (The Slots from Python will otherwise conflict with the Qt Slots
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#pragma pop_macro("slots")
After compiling, add the python3.dll, python39.dll, as well as the DLLs and Lib Python folders to your compilation folder. You can find them in the root directory of your Python installation. This will allow you to run the embedded c++ code even when python is not installed.
With these steps, I was able to get python running in Qt with the 64 bit MinGW and MSVC compiler. Only the MSVC in debug mode got still a problem.
FURTHER:
If you want to pass arguments to the python script, you need the following function (It can be easy copy-pasted into your code):
/*!
* \brief runPyScriptArgs executs a Python script and passes arguments
* \param file (the path of the script)
* \param argc amount of arguments
* \param argv array of arguments with size of argc
*/
static void runPyScriptArgs(const char* file, int argc, char *argv[]){
FILE* fp;
wchar_t** wargv = new wchar_t*[argc];
for(int i = 0; i < argc; i++)
{
wargv[i] = Py_DecodeLocale(argv[i], nullptr);
if(wargv[i] == nullptr)
{
return;
}
}
Py_SetProgramName(wargv[0]);
Py_Initialize();
PySys_SetArgv(argc, wargv);
fp = _Py_fopen(file, "r");
PyRun_SimpleFile(fp, file);
Py_Finalize();
for(int i = 0; i < argc; i++)
{
PyMem_RawFree(wargv[i]);
wargv[i] = nullptr;
}
delete[] wargv;
wargv = nullptr;
}
To use this function, call it like this (For example in your main):
int py_argc = 2;
char* py_argv[py_argc];
py_argv[0] = "Progamm";
py_argv[1] = "Hello";
runPyScriptArgs("test/test.py", py_argc, py_argv);
Together with the test.py script in the test folder:
import sys
if len(sys.argv) != 2:
sys.exit("Not enough args")
ca_one = str(sys.argv[0])
ca_two = str(sys.argv[1])
print ("My command line args are " + ca_one + " and " + ca_two)
you get the following output:
My command line args are Progamm and Hello
I have a C++ project that I would like to add an embedded python interpreter to. I've actually succeeded in this with simple scripts, but when I try doing something with Tkinter, it opens a blank window and never draws any frame or contents. I'm pretty sure it has to do with the GIL, but have been unsuccessful in finding some combination of calls that work. I've made a simple example that illustrates this with a C++ file and a python script that the compiled program runs. If you uncomment the MyPythonThread line and comment out the two that run it in a thread, then it works as expected.
It seems python knows whether it is in the "main" thread or not, regardless of which thread I Py_Initialize from.
Other info.: I'm testing this on Mac OS X 10.13.6 with homebrew installed python 2.7.15.
//
// Compile with: g++ `python-config --cflags --libs` --std=c++11 test.cc
//
#include <cstdio>
#include <Python.h>
#include <thread>
void MyPythonThread(void)
{
PyEval_InitThreads();
Py_Initialize();
const char *fname = "test.py";
PySys_SetArgv( 1, (char**)&fname );
auto fil = std::fopen(fname, "r");
PyRun_AnyFileEx( fil, NULL, 1 );
}
int main(int narg, char * argv[])
{
// This works
// MyPythonThread();
// This does not
std::thread thr(MyPythonThread);
thr.join();
return 0;
}
Here is the python script that it runs:
#!/usr/bin/env python
import Tkinter as tk
window = tk.Tk()
top_frame = tk.Frame(window).pack()
b1 = tk.Button(top_frame, text = "Button").pack()
window.mainloop()
I am working on packaging and distributing some python applications in Windows by wrapping together a python runtime, the python packages for the applications, and some executables to run the python applications. The approach is just to modify the source for python.exe to launch the applications but accept command line arguments for things like data file names.
Below is an example C++ source for one of the executables:
// source for my_python_application1
#include "stdafx.h"
#include "Windows.h"
#include "Python.h"
wchar_t SWITCH[] = L"-m";
wchar_t APP[] = L"my_python_application1.main";
int wmain(int argc, wchar_t **argv) {
int newargc;
newargc = argc + 2;
// can use this to modify the PythonPath for specific distributions
// _putenv("PYTHONPATH=\"\"");
wchar_t **newargv = new wchar_t*[newargc];
newargv[0] = argv[0];
newargv[1] = SWITCH;
newargv[2] = APP;
for (int i = 1; i < argc; i++) {
newargv[i + 2] = argv[i];
}
return Py_Main(newargc, newargv);
// return Py_Main(argc, argv);
}
Functionally this achieves everything I need it to achieve, but I suffer from a certain OCD nature which leads me to want things organized in a certain way. I'd like to have a structure like the following
/application_suite
/python_runtime
python.exe
python36.dll
(and everything else in a python dir)
/python_applications
my_python_application1.exe
my_python_application2.exe
However, since mypythonapplication1/2.exe are basically modified python.exe files, in order for them to work properly (load the python dll, import modules, access all of the landmarking features necessary for modules to be interconnected) they need to be located in the /python_runtime directory.
I'm wondering is there a way to compile these executables so that they can be arranged in the directory structure that I presented, but know that they python_runtime directory and all of its structure are located in a relative path of './python_runtime' or whatever so that this all behaves well no matter where the distribution of applications is installed by the end user.
Pre-Answer Warning I am not a C/C++ programmer. It is possible there are bad C++ practices in here, so please use what you find in this answer with a grain of salt.
The requirements to achieve this behavior are the following:
We must get the directory of the custom executable
We must set the PYTHONHOME environment variable to %executable_dir%\runtime
We must set the PYTHONPATH environment variable to %executable_dir%\apps so that python knows where our python packages are living. This also clears out any system wide settings so that the distribution doesn't use other python environment settings
I don't know if it's necessary, but I am adding the runtime directory at the front of the path
We have to dynamically load the Py_Main function from the desired dll. Since we are not expecting the runtime to be on the path before execution, we must find the dll dynamically from %executable_dir%\runtime\python36.dll.
The following source code works when I compiled in Visual Studio 2017, with no Python Header files and no dll specified in the Linker
// source code for custom my_python_application1
// requires _CRT_SECURE_NO_WARNINGS flag to compile deprecated path operations
#include "stdafx.h"
#include <string>
#include <sstream>
#include <iostream>
#include "Windows.h"
#include "Shlwapi.h"
// #include "Python.h" // don't need this as we are dynamically loading the library of choice
#pragma comment(lib, "Shlwapi.lib")
__pragma(warning(disable:4996)) // # _CRT_SECURE_NO_DEPRECIATE
wchar_t SWITCH[] = L"-m";
wchar_t APP[] = L"my_python_application1.main";
typedef int(__stdcall *py_main_function)(int, wchar_t**);
int wmain(int argc, wchar_t **argv) {
int newargc;
newargc = argc + 2;
// determine the path of the executable so we know the absolute path
// of the python runtime and application directories
wchar_t executable_dir[MAX_PATH];
if (GetModuleFileName(NULL, executable_dir, MAX_PATH) == 0)
return -1;
PathRemoveFileSpec(executable_dir);
std::wstring executable_dir_string(executable_dir);
// now set the relevant environment variables so that the environment works as it is supposed to
std::wstring python_home(L"PYTHONHOME=" + executable_dir_string + L"\\runtime");
_wputenv(python_home.c_str());
std::wstring python_path(L"PYTHONPATH=" + executable_dir_string + L"\\apps");
_wputenv(python_path.c_str());
// put the python runtime at the front of the path
std::wstringstream ss;
ss << "PATH=" << executable_dir << "\\runtime;" << getenv("PATH");
std::wstring path_string (ss.str());
_wputenv(path_string.c_str());
wchar_t **newargv = new wchar_t*[newargc];
newargv[0] = argv[0];
newargv[1] = SWITCH;
newargv[2] = APP;
for (int i = 1; i < argc; i++) {
newargv[i + 2] = argv[i];
}
// dynamically load the python dll
std::wstring python_dll(executable_dir_string + L"\\runtime\\python36.dll");
HINSTANCE hGetProcIDDLL = LoadLibrary(python_dll.c_str());
py_main_function Py_Main = (py_main_function)GetProcAddress(hGetProcIDDLL, "Py_Main");
//now call Py_Main with our arguments
return Py_Main(newargc, newargv);
// return Py_Main(argc, argv);
}
I try to redirect in windows the cmd.exe stdout&stdin (with CreateProcess()). It works fine as long as I run simple commands or open GUI apps, but if I try running a software like python, it does not give me it's output anymore (nor getting the input through stdin).
Code example:
#include <windows.h>
#include <iostream>
#include <string>
#include <thread>
using namespace std;
HANDLE child_input_read;
HANDLE child_input_write;
HANDLE child_output_read;
HANDLE child_output_write;
void writeToPipe()
{
while (true)
{
DWORD bytes_written;
string msg;
getline(cin, msg);
msg += '\n';
BOOL success = WriteFile(child_input_write, msg.c_str(), msg.size(), &bytes_written, NULL);
if (!success)
{
break;
}
}
}
void readFromPipe()
{
while (true)
{
DWORD bytes_read;
char buffer[512];
BOOL success = ReadFile(child_output_read, buffer, sizeof(buffer)-1, &bytes_read, NULL);
buffer[bytes_read] = 0;
if (!success)
{
break;
}
cout << buffer;
}
}
void createCmdProcess()
{
PROCESS_INFORMATION process_info;
STARTUPINFO startup_info;
SECURITY_ATTRIBUTES security_attributes;
// Set the security attributes for the pipe handles created
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = NULL;
CreatePipe(&child_output_read, &child_output_write, &security_attributes, 0);
CreatePipe(&child_input_read, &child_input_write, &security_attributes, 0);
// Create the child process
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.hStdInput = child_input_read;
startup_info.hStdOutput = child_output_write;
startup_info.hStdError = child_output_write;
startup_info.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info);
}
int main()
{
createCmdProcess();
thread t(writeToPipe);
thread t2(readFromPipe);
t.join();
t2.join();
system("pause");
}
It's not a bug, it's a feature. :-)
From Python Setup & Usage, section 1.1.1, Interface options (emphasis added):
The interpreter interface resembles that of the UNIX shell, but provides some additional methods of invocation:
When called with standard input connected to a tty device, it prompts for commands and executes them until an EOF (an end-of-file character, you can produce that with Ctrl-D on UNIX or Ctrl-Z, Enter on Windows) is read.
When called with a file name argument or with a file as standard input, it reads and executes a script from that file.
A pipe is neither a file nor a tty device, but it looks like a file as far as the C standard library (and hence Python) is concerned. So the second behaviour kicks in, and Python attempts to read to the end of file. Since we never close our end of the pipe, that never happens.
This behaviour isn't particularly sensible in my opinion (or at least not in Windows) and you could file a bug if you wanted. I would guess that such a proposal would be rejected, since any change would break backwards compatibility, but I could be wrong.
You can work around the problem by providing the -i option on the command line:
python -i
That makes Python enter interactive mode, despite the fact that stdin isn't a terminal.
Unfortunately, there is no known way to make a pipe that looks like a terminal on Windows.