From 66937e2cfcde72c22f5bcbe884c8dee3926418d0 Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Mon, 13 Oct 2025 16:30:44 +0200 Subject: [PATCH] Implementation using python invocation using qprocess --- CMakeLists.txt | 6 +++++ mainwindow.cpp | 21 ++++++++++++++++- mainwindow.h | 4 ++++ pythonembed.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ pythonembed.h | 31 ++++++++++++++++++++++++ pythonrunner.cpp | 26 +++++++++++++++++++++ pythonrunner.h | 26 +++++++++++++++++++++ 7 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 pythonembed.cpp create mode 100644 pythonembed.h create mode 100644 pythonrunner.cpp create mode 100644 pythonrunner.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a5c3161..32b9cc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ find_package(PkgConfig REQUIRED) pkg_check_modules(EISMULIPLEXER REQUIRED eismuliplexer) find_package(Qt6 REQUIRED COMPONENTS Widgets) find_package(Qt6 REQUIRED COMPONENTS Core) +# find_package(Python3 REQUIRED) - Removed since we're using QProcess instead of embedding Python set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) @@ -45,11 +46,16 @@ add_executable(${PROJECT_NAME} multiplexer.cpp triggerwidget.cpp triggerwidget.h + pythonrunner.cpp + pythonrunner.h + pythonembed.cpp ) set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE ON) target_compile_options(${PROJECT_NAME} PUBLIC "-Wall") 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_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core ${EISMULIPLEXER_LIBRARIES} QCodeEditor) +# ${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) qt_add_resources(${PROJECT_NAME} "resources" PREFIX "/" FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/eismultiplexerqt.png ) install(TARGETS ${PROJECT_NAME} DESTINATION bin) diff --git a/mainwindow.cpp b/mainwindow.cpp index efef4b8..14a525f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -15,7 +15,8 @@ MainWindow::MainWindow(QWidget *parent): codeEditor(this), pythonOutput(this), currentFilePath(""), - isFileModified(false) + isFileModified(false), + pythonRunner(nullptr) { ui->setupUi(this); enumerateDevices(); @@ -49,6 +50,11 @@ MainWindow::MainWindow(QWidget *parent): isFileModified = true; updateStatus(); }); + + // Connect Run and Stop buttons + pythonRunner = new PythonRunner(&pythonOutput, this); + connect(ui->pushButtonRun, &QPushButton::clicked, this, &MainWindow::onPushButtonRunClicked); + connect(ui->pushButtonStop, &QPushButton::clicked, this, &MainWindow::onPushButtonStopClicked); } MainWindow::~MainWindow() @@ -226,6 +232,19 @@ void MainWindow::enumerateDevices() generateExample(); } +void MainWindow::onPushButtonRunClicked() { + QString scriptContent = codeEditor.toPlainText(); + pythonRunner->runScript(scriptContent); + ui->pushButtonRun->setEnabled(false); + ui->pushButtonStop->setEnabled(true); +} + +void MainWindow::onPushButtonStopClicked() { + pythonRunner->stopScript(); + ui->pushButtonRun->setEnabled(true); + ui->pushButtonStop->setEnabled(false); +} + void MainWindow::generateExample() { QString example = diff --git a/mainwindow.h b/mainwindow.h index ec96f0a..df74472 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -11,6 +11,7 @@ #include "channelwidget.h" #include "triggerwidget.h" +#include "pythonrunner.h" namespace Ui { @@ -27,6 +28,7 @@ class MainWindow : public QMainWindow QPythonHighlighter highligter; QPythonCompleter completer; QTextEdit pythonOutput; + PythonRunner* pythonRunner; signals: void channelStateChanged(uint16_t device, uint16_t channel); @@ -39,6 +41,8 @@ private slots: void onActionOpenTriggered(); void onActionSaveTriggered(); void onActionSaveAsTriggered(); + void onPushButtonRunClicked(); + void onPushButtonStopClicked(); private: void enumerateDevices(); diff --git a/pythonembed.cpp b/pythonembed.cpp new file mode 100644 index 0000000..f6df212 --- /dev/null +++ b/pythonembed.cpp @@ -0,0 +1,61 @@ + + +#include "pythonembed.h" +#include + +PythonEmbed::PythonEmbed(QTextEdit* outputWidget, QObject* parent) + : QObject(parent), m_outputWidget(outputWidget), m_process(nullptr) { + m_process = new QProcess(this); + connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonEmbed::onOutputAvailable); + connect(m_process, &QProcess::readyReadStandardError, this, &PythonEmbed::onErrorAvailable); + connect(m_process, QOverload::of(&QProcess::finished), this, &PythonEmbed::onProcessFinished); +} + +PythonEmbed::~PythonEmbed() { + if (m_process) { + m_process->terminate(); + m_process->waitForFinished(1000); + } +} + +void PythonEmbed::runScript(const QString& scriptContent) { + if (m_process->state() == QProcess::Running) { + m_process->terminate(); + m_process->waitForFinished(1000); + } + + 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_process->start("python3", QStringList() << "-c" << scriptContent); +} + +void PythonEmbed::stopScript() { + if (m_process && m_process->state() == QProcess::Running) { + m_process->terminate(); + } +} + +void PythonEmbed::onOutputAvailable() { + QByteArray output = m_process->readAllStandardOutput(); + m_outputWidget->append(output); + m_outputWidget->moveCursor(QTextCursor::End); +} + +void PythonEmbed::onErrorAvailable() { + QByteArray error = m_process->readAllStandardError(); + m_outputWidget->append(error); + m_outputWidget->moveCursor(QTextCursor::End); +} + +void PythonEmbed::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) { + if (exitStatus == QProcess::NormalExit && exitCode != 0) { + m_outputWidget->append(QString("Process exited with code %1\n").arg(exitCode)); + } else if (exitStatus == QProcess::CrashExit) { + m_outputWidget->append("Python process crashed\n"); + } +} + +#include "pythonembed.moc" + diff --git a/pythonembed.h b/pythonembed.h new file mode 100644 index 0000000..b48bded --- /dev/null +++ b/pythonembed.h @@ -0,0 +1,31 @@ + + +#ifndef PYTHONEMBED_H +#define PYTHONEMBED_H + +#include +#include +#include + +class PythonEmbed : public QObject { + Q_OBJECT + +public: + PythonEmbed(QTextEdit* outputWidget, QObject* parent = nullptr); + ~PythonEmbed(); + + void runScript(const QString& scriptContent); + void stopScript(); + +private slots: + void onOutputAvailable(); + void onErrorAvailable(); + void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + +private: + QTextEdit* m_outputWidget; + QProcess* m_process; +}; + +#endif // PYTHONEMBED_H + diff --git a/pythonrunner.cpp b/pythonrunner.cpp new file mode 100644 index 0000000..e2f09ca --- /dev/null +++ b/pythonrunner.cpp @@ -0,0 +1,26 @@ + +#include "pythonrunner.h" +#include "pythonembed.h" + +PythonRunner::PythonRunner(QTextEdit* outputWidget, QObject* parent) + : QObject(parent), m_outputWidget(outputWidget), m_pythonEmbed(nullptr) { + m_pythonEmbed = new PythonEmbed(m_outputWidget, this); +} + +PythonRunner::~PythonRunner() { + delete m_pythonEmbed; +} + +void PythonRunner::runScript(const QString& scriptContent) { + if (m_pythonEmbed) { + m_pythonEmbed->runScript(scriptContent); + } +} + +void PythonRunner::stopScript() { + if (m_pythonEmbed) { + m_pythonEmbed->stopScript(); + } +} + +#include "pythonrunner.moc" diff --git a/pythonrunner.h b/pythonrunner.h new file mode 100644 index 0000000..b5067cb --- /dev/null +++ b/pythonrunner.h @@ -0,0 +1,26 @@ + +#ifndef PYTHONRUNNER_H +#define PYTHONRUNNER_H + +#include +#include + +class PythonEmbed; + +class PythonRunner : public QObject { + Q_OBJECT + +public: + explicit PythonRunner(QTextEdit* outputWidget, QObject* parent = nullptr); + ~PythonRunner(); + +public slots: + void runScript(const QString& scriptContent); + void stopScript(); + +private: + QTextEdit* m_outputWidget; + PythonEmbed* m_pythonEmbed; +}; + +#endif // PYTHONRUNNER_H