174 lines
5.5 KiB
C++
174 lines
5.5 KiB
C++
#include "pythonrunner.h"
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <QStandardPaths>
|
|
#include <QThread>
|
|
|
|
PythonRunner::PythonRunner(QTextEdit* outputWidget, QObject* parent)
|
|
: QObject(parent), m_outputWidget(outputWidget), m_process(nullptr), m_venvProcess(nullptr), venvReady(false) {
|
|
m_process = new QProcess(this);
|
|
connect(m_process, &QProcess::readyReadStandardOutput, this, &PythonRunner::onOutputAvailable);
|
|
connect(m_process, &QProcess::readyReadStandardError, this, &PythonRunner::onErrorAvailable);
|
|
connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonRunner::onProcessFinished);
|
|
|
|
// Start venv setup in background
|
|
QThread::create([this]() { setupVenv(); });
|
|
}
|
|
|
|
PythonRunner::~PythonRunner() {
|
|
if (m_process) {
|
|
m_process->terminate();
|
|
m_process->waitForFinished(1000);
|
|
}
|
|
if (m_venvProcess) {
|
|
m_venvProcess->terminate();
|
|
m_venvProcess->waitForFinished(1000);
|
|
}
|
|
}
|
|
|
|
QString PythonRunner::venvPath() {
|
|
return dir.path() + QDir::separator() + "venv";
|
|
}
|
|
|
|
QString PythonRunner::pythonExePath() {
|
|
QString venv = venvPath();
|
|
#ifdef Q_OS_WIN
|
|
return venv + "\\Scripts\\python.exe";
|
|
#else
|
|
return venv + "/bin/python";
|
|
#endif
|
|
}
|
|
|
|
void PythonRunner::ensureVenvReady() {
|
|
QMutexLocker locker(&venvMutex);
|
|
if (!venvReady) {
|
|
venvCondition.wait(&venvMutex);
|
|
}
|
|
}
|
|
|
|
void PythonRunner::setupVenv() {
|
|
QString venv = venvPath();
|
|
QString pythonCmd = "python3";
|
|
#ifdef Q_OS_WIN
|
|
pythonCmd = "python";
|
|
#endif
|
|
|
|
// Check if venv already exists
|
|
QDir venvDir(venv);
|
|
if (venvDir.exists()) {
|
|
venvReady = true;
|
|
emit venvSetupProgress("Virtual environment already exists");
|
|
return;
|
|
}
|
|
|
|
// Create virtual environment
|
|
emit venvSetupProgress("Creating virtual environment...");
|
|
|
|
m_venvProcess = new QProcess();
|
|
connect(m_venvProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &PythonRunner::onVenvProcessFinished);
|
|
|
|
QStringList args;
|
|
#ifdef Q_OS_WIN
|
|
args << "-m" << "venv" << "venv";
|
|
#else
|
|
args << "-m" << "venv" << venv;
|
|
#endif
|
|
|
|
m_venvProcess->setWorkingDirectory(dir.path());
|
|
m_venvProcess->start(pythonCmd, args);
|
|
if (!m_venvProcess->waitForStarted()) {
|
|
emit venvSetupProgress("Failed to start venv creation process");
|
|
venvReady = false;
|
|
m_venvProcess->deleteLater();
|
|
m_venvProcess = nullptr;
|
|
}
|
|
}
|
|
|
|
void PythonRunner::onVenvProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
|
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
|
// Install eismultiplexer package
|
|
emit venvSetupProgress("Installing eismultiplexer package...");
|
|
|
|
QProcess installProcess;
|
|
installProcess.setWorkingDirectory(dir.path());
|
|
QStringList installArgs;
|
|
installArgs << "-m" << "pip" << "install" << "eismultiplexer";
|
|
|
|
installProcess.start(pythonExePath(), installArgs);
|
|
if (!installProcess.waitForFinished(30000)) { // 30 second timeout
|
|
emit venvSetupProgress("Package installation timed out");
|
|
venvReady = false;
|
|
} else {
|
|
QByteArray output = installProcess.readAllStandardOutput();
|
|
QByteArray error = installProcess.readAllStandardError();
|
|
if (!error.isEmpty()) {
|
|
emit venvSetupProgress("Installation error: " + QString::fromLocal8Bit(error));
|
|
venvReady = false;
|
|
} else {
|
|
emit venvSetupProgress("Package installed successfully");
|
|
venvReady = true;
|
|
}
|
|
}
|
|
} else {
|
|
emit venvSetupProgress(QString("Virtual environment creation failed with code %1").arg(exitCode));
|
|
venvReady = false;
|
|
}
|
|
|
|
// Clean up
|
|
m_venvProcess->deleteLater();
|
|
m_venvProcess = nullptr;
|
|
}
|
|
|
|
bool PythonRunner::runScript(const QString& scriptContent) {
|
|
if (m_process->state() == QProcess::Running) {
|
|
m_process->terminate();
|
|
m_process->waitForFinished(1000);
|
|
}
|
|
|
|
m_outputWidget->clear();
|
|
|
|
// Check if venv is ready
|
|
if (!venvReady) {
|
|
m_outputWidget->append("Error: Virtual environment not ready. Please wait for the setup to complete.\n");
|
|
emit scriptFinished(-1);
|
|
return false;
|
|
}
|
|
|
|
m_outputWidget->append("Running script in virtual environment...\n");
|
|
|
|
QString pythonPath = pythonExePath();
|
|
m_process->start(pythonPath, QStringList() << "-u" << "-c" << scriptContent);
|
|
|
|
return true;
|
|
}
|
|
|
|
void PythonRunner::stopScript() {
|
|
if (m_process && m_process->state() == QProcess::Running) {
|
|
m_process->terminate();
|
|
}
|
|
}
|
|
|
|
void PythonRunner::onOutputAvailable() {
|
|
QByteArray output = m_process->readAllStandardOutput();
|
|
m_outputWidget->append(output);
|
|
m_outputWidget->moveCursor(QTextCursor::End);
|
|
}
|
|
|
|
void PythonRunner::onErrorAvailable() {
|
|
QByteArray error = m_process->readAllStandardError();
|
|
m_outputWidget->append(error);
|
|
m_outputWidget->moveCursor(QTextCursor::End);
|
|
}
|
|
|
|
void PythonRunner::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 was stopped\n");
|
|
}
|
|
emit scriptFinished(exitCode);
|
|
}
|
|
|
|
void PythonRunner::onVenvSetupFinished() {
|
|
// This slot can be used to handle venv setup completion if needed
|
|
}
|