cpython attempt

This commit is contained in:
Carl Philipp Klemm 2025-10-13 16:45:50 +02:00
parent 66937e2cfc
commit a3fbd2877b
3 changed files with 66 additions and 44 deletions

View file

@ -28,7 +28,20 @@ find_package(PkgConfig REQUIRED)
pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer) pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer)
find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS Widgets)
find_package(Qt6 REQUIRED COMPONENTS Core) find_package(Qt6 REQUIRED COMPONENTS Core)
# find_package(Python3 REQUIRED) - Removed since we're using QProcess instead of embedding Python find_package(Python3 REQUIRED)
# Manually set Python include directory if not found
if(NOT Python3_INCLUDE_DIRS)
execute_process(COMMAND python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])"
OUTPUT_VARIABLE PYTHON_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(Python3_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}")
endif()
# Manually set Python libraries if not found
if(NOT Python3_LIBRARIES)
set(Python3_LIBRARIES python3.12)
endif()
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
@ -53,9 +66,8 @@ add_executable(${PROJECT_NAME}
set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE ON) set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE ON)
target_compile_options(${PROJECT_NAME} PUBLIC "-Wall") target_compile_options(${PROJECT_NAME} PUBLIC "-Wall")
target_include_directories(${PROJECT_NAME} PUBLIC QCodeEditor) target_include_directories(${PROJECT_NAME} PUBLIC QCodeEditor)
# target_include_directories(${PROJECT_NAME} PRIVATE ${Python3_INCLUDE_DIRS}) - Removed since we're using QProcess instead of embedding Python target_include_directories(${PROJECT_NAME} PRIVATE ${Python3_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES} QCodeEditor) target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES} QCodeEditor ${Python3_LIBRARIES})
# ${Python3_LIBRARIES} - Removed since we're using QProcess instead of embedding Python
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png PROPERTIES QT_RESOURCE_ALIAS eismultiplexerqt.png) set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png PROPERTIES QT_RESOURCE_ALIAS eismultiplexerqt.png)
qt_add_resources(${PROJECT_NAME} "resources" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png ) qt_add_resources(${PROJECT_NAME} "resources" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png )
install(TARGETS ${PROJECT_NAME} DESTINATION bin) install(TARGETS ${PROJECT_NAME} DESTINATION bin)

View file

@ -1,59 +1,73 @@
#include "pythonembed.h" #include "pythonembed.h"
#include <Python.h>
#include <QDebug> #include <QDebug>
PythonEmbed::PythonEmbed(QTextEdit* outputWidget, QObject* parent) PythonEmbed::PythonEmbed(QTextEdit* outputWidget, QObject* parent)
: QObject(parent), m_outputWidget(outputWidget), m_process(nullptr) { : QObject(parent), m_outputWidget(outputWidget) {
m_process = new QProcess(this); // Initialize Python
connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonEmbed::onOutputAvailable); Py_Initialize();
connect(m_process, &QProcess::readyReadStandardError, this, &PythonEmbed::onErrorAvailable);
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonEmbed::onProcessFinished); m_initialized = true;
// Initialize the output widget with Python prompt
m_outputWidget->clear();
m_outputWidget->append("Python 3.11.2 (main, Oct 5 2023, 17:20:59) [GCC 11.4.0] on linux\n");
m_outputWidget->append("Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\n");
m_outputWidget->append(">>> ");
} }
PythonEmbed::~PythonEmbed() { PythonEmbed::~PythonEmbed() {
if (m_process) { if (m_initialized) {
m_process->terminate(); Py_Finalize();
m_process->waitForFinished(1000);
} }
} }
void PythonEmbed::runScript(const QString& scriptContent) { void PythonEmbed::runScript(const QString& scriptContent) {
if (m_process->state() == QProcess::Running) { if (!m_initialized) {
m_process->terminate(); m_outputWidget->append("Python not initialized\n");
m_process->waitForFinished(1000); return;
} }
m_outputWidget->clear(); // Convert QString to const char*
m_outputWidget->append("Python 3.11.2 (main, Oct 5 2023, 17:20:59) [GCC 11.4.0] on linux\n"); QByteArray scriptBytes = scriptContent.toUtf8();
m_outputWidget->append("Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\n"); const char* script = scriptBytes.constData();
m_process->start("python3", QStringList() << "-c" << scriptContent); // Run the script
PyRun_SimpleString(script);
// Check for any errors
if (PyErr_Occurred()) {
handlePythonError("Script execution error");
} else {
m_outputWidget->append("\n>>> ");
}
} }
void PythonEmbed::stopScript() { void PythonEmbed::stopScript() {
if (m_process && m_process->state() == QProcess::Running) { // In this simple implementation, we don't have a running Python thread
m_process->terminate(); // to stop, but we could add that functionality later
}
} }
void PythonEmbed::onOutputAvailable() { void PythonEmbed::handlePythonError(const char* context) {
QByteArray output = m_process->readAllStandardOutput(); PyObject *type, *value, *traceback;
m_outputWidget->append(output); PyErr_Fetch(&type, &value, &traceback);
m_outputWidget->moveCursor(QTextCursor::End); if (type) {
} PyObject *str = PyObject_Str(value);
PyObject *bytes = PyUnicode_AsEncodedString(str, "utf-8", "strict");
const char *error_msg = PyBytes_AS_STRING(bytes);
void PythonEmbed::onErrorAvailable() { QString errorString = QString::fromUtf8(context) + ": " + QString::fromUtf8(error_msg);
QByteArray error = m_process->readAllStandardError(); m_outputWidget->append(errorString);
m_outputWidget->append(error);
m_outputWidget->moveCursor(QTextCursor::End);
}
void PythonEmbed::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { Py_DecRef(str);
if (exitStatus == QProcess::NormalExit && exitCode != 0) { Py_DecRef(bytes);
m_outputWidget->append(QString("Process exited with code %1\n").arg(exitCode)); Py_DecRef(type);
} else if (exitStatus == QProcess::CrashExit) { Py_DecRef(value);
m_outputWidget->append("Python process crashed\n"); Py_DecRef(traceback);
} else {
m_outputWidget->append(QString::fromUtf8(context) + ": No error information available");
} }
} }

View file

@ -4,7 +4,6 @@
#define PYTHONEMBED_H #define PYTHONEMBED_H
#include <QTextEdit> #include <QTextEdit>
#include <QProcess>
#include <QObject> #include <QObject>
class PythonEmbed : public QObject { class PythonEmbed : public QObject {
@ -17,14 +16,11 @@ public:
void runScript(const QString& scriptContent); void runScript(const QString& scriptContent);
void stopScript(); void stopScript();
private slots:
void onOutputAvailable();
void onErrorAvailable();
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
private: private:
void handlePythonError(const char* context);
QTextEdit* m_outputWidget; QTextEdit* m_outputWidget;
QProcess* m_process; bool m_initialized = false;
}; };
#endif // PYTHONEMBED_H #endif // PYTHONEMBED_H