How to deploy Qt application with external libraries
-
Hello, So ive built my Qt application and it uses the Python C API, which I statically link. In my .pro file. I Include it in my project as so:
LIBS += -L"C:/python/libs" -lpython38 INCLUDEPATH += C:/python/include/ DEPENDPATH += C:/python/include/
linking to a local directory on my computer. I then
#include "Python.h"
in one of my header files as usual - no problems.This works fine, but when it comes to deploying the application, I receive the error from windows:
The code execution cannot proceed because python38.dll was not found. Reinstalling the program may fix this problem.
Now, I dont understand why I got this error because I thought all the required Python libraries would be compiled into the .exe file since it was statically linked.
Anyway, I add the
python38.dll
to the directory where my .exe is located, As required, and the program (.exe file) starts fine. However, when it gets to the part where it actually has to call the python code, the application just crashes.Any suggestion towards how I may fix this? Thank you.
-
Hi,
What do you do in that script ?
-
@SGaist In the python script you mean? It has a couple functions which sends request to a raspberry pi server. Here is how the python code is called within my Qt Application:
Py_Initialize(); PyObject* sys = PyImport_ImportModule( "sys" ); PyObject* sys_path = PyObject_GetAttrString( sys, "path" ); PyObject* folder_path = PyUnicode_FromString( "." ); PyList_Append( sys_path, folder_path ); PyObject *pName = PyUnicode_FromString("client"); PyObject *pModule = PyImport_Import(pName); Py_DECREF(pName); PyObject* pFunc_send = PyObject_GetAttrString(pModule, (char*)"send_string"); PyObject* pFunc_connect = PyObject_GetAttrString(pModule, (char*)"connect_to_server"); if ((pFunc_send && PyCallable_Check(pFunc_send)) && (pFunc_connect && PyCallable_Check(pFunc_send))) { PyObject* pResult_connect = PyObject_CallObject(pFunc_connect, NULL); const int connection_result = PyLong_AsLong(pResult_connect); if(connection_result) { for(auto question : cachedQuestions) { const char* args[2] = {question.first.c_str(), question.second.c_str()}; PyObject* pArgs_send = PyTuple_New(2); for(int i = 0; i < 2; ++i) { PyObject* pValue = PyUnicode_FromString(args[i]); if (!pValue) { Py_DECREF(pArgs_send); Py_DECREF(pModule); fprintf(stderr, "Cannot convert argument\n"); } PyTuple_SetItem(pArgs_send, i, pValue); } //send the question and capture the server response PyObject* pResult = PyObject_CallObject(pFunc_send, pArgs_send); const std::string result = _PyUnicode_AsString(pResult); Py_DECREF(pArgs_send); Py_DECREF(pResult); if(result == "data_error" && (POTWParams["new_playlist"] == "False")) { generateNewPopup("ERROR 1\n\n" "there was an error in trasmitting your data to the server\n" "make sure your spotify playlist is of the form: https://open.spotify.com/playlist/________\n" "please try again"); bool result = disconnect_server(pFunc_send); if(!result) { generateNewPopup("ERROR 2\n\n" "there was an error when attempting to disconnect from the server\n" "trying again ..."); disconnect_server(pFunc_send); Py_DECREF(pFunc_send); Py_DECREF(pFunc_connect); Py_DECREF(pModule); } return; } } } bool disconnect_result = disconnect_server(pFunc_send); if(disconnect_result) generateNewPopup("SUCCESS\n\n" "A new POTW is now underway :)\n" "You may now quit the program"); } else { generateNewPopup("ERROR\n\n" "Could not call send function" "A file may have been moved or destroyed") ; } Py_DECREF(pFunc_send); Py_DECREF(pFunc_connect); Py_DECREF(pModule); // Finish the Python Interpreter Py_Finalize(); } else{ generateNewPopup("ERROR\n\n" "you did not fill in all the necessary fields.\n " "Your request was not transmitted to the server. \n" "Please try again \n"); }
you dont have to read it all, but just so you understand how im using the API I gave it for reference.
-
Why not use QNetworkAccessManager rather than embedding python ?
-
Why not use QNetworkAccessManager rather than embedding python ?
@SGaist I could, but that would probably require me to install Qt on my target machine (im not even sure it has enough storage for that), and, the server needs to interact with a host of web APIs so it would have to switch from c++ to python at some point, since C++ is often not supported in those use cases. I do take your point though, and will probably resort to this if i cant find a solution for my current problems.
-
Why would you need to do that ?
QNetworkAccessManager just does standard http requests. The backend can be whatever you want. -
What should your request look like ?
-
essentially the message being sent is built up as:
[length of opcode (in bytes)] - [length of message (in byes)] - [opcode] - [message]
where each section above is a utf-8 encoded strings.
My original python code was:
HEADER = 256 #header length(bytes) OPCODE = 4 PORT = #####REDACTED_FOR_PRIVACY##### FORMAT = 'utf-8' DISCONNECT_MESSAGE = "!DISCONNECT" SERVER = #####REDACTED_FOR_PRIVACY##### ADDR = (SERVER, PORT) class Opcode (str, Enum): WEEK: str = "1" EMAIL: str = "2" COMPETITION_DURATION: str = "3" SPOTIFY: str = "5" SUBMISSION_DURATION: str = "6" RANKING_DURATION: str = "7" DISCONNECT: str = "8" NEW_PLAYLIST: str = "9" NUM_PARTICIPANTS: str = "10" NAME_QUESTION: str = "11" BONUS_QUESTION: str = "12" def connect_to_server(): global client client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(ADDR) def send(opcode, msg): message= msg.encode(FORMAT) #convert the message to byte format op = opcode.encode(FORMAT) msg_length = len(message) #length of message msg_send_length = str(msg_length).encode(FORMAT) #convert the length of the message to byte format op_length = len(op) op_send_length = str(op_length).encode(FORMAT) # Pad out the length to 64 bytes and the opcode to 4 bytes msg_send_length += b' ' * (HEADER - len(msg_send_length)) op_send_length += b' ' * (OPCODE - len(op_send_length)) client.send(msg_send_length) client.send(op_send_length) client.send(op) client.send(message) return client.recv(2048).decode(FORMAT) send(Opcode.EMAIL, "example@gmail.com") send(Opcode.WEEK, "12") send(Opcode.COMPETITION_DURATION, "7") send(Opcode.SPOTIFY, "https://open.spotify.com/playlist/cyt67ryic657bi")