File indexing completed on 2024-04-28 08:44:23
0001 /* 0002 SPDX-FileCopyrightText: 2022 Julius Künzel <jk.kdedev@smartlab.uber.space> 0003 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "abstractpythoninterface.h" 0008 #include "core.h" 0009 #include "mainwindow.h" 0010 #include "utils/KMessageBox_KdenliveCompat.h" 0011 0012 #include <KIO/DirectorySizeJob> 0013 #include <KLocalizedString> 0014 #include <KMessageBox> 0015 #include <QAction> 0016 #include <QDebug> 0017 #include <QGuiApplication> 0018 #include <QMutex> 0019 #include <QProcess> 0020 #include <QStandardPaths> 0021 #include <QtConcurrent> 0022 0023 static QMutex mutex; 0024 static bool installInProgress; 0025 0026 PythonDependencyMessage::PythonDependencyMessage(QWidget *parent, AbstractPythonInterface *interface, bool setupErrorOnly) 0027 : KMessageWidget(parent) 0028 , m_interface(interface) 0029 { 0030 setWordWrap(true); 0031 setCloseButtonVisible(false); 0032 hide(); 0033 m_installAction = new QAction(i18n("Install missing dependencies"), this); 0034 m_abortAction = new QAction(i18n("Abort installation"), this); 0035 connect(m_abortAction, &QAction::triggered, m_interface, &AbstractPythonInterface::abortScript); 0036 connect(m_interface, &AbstractPythonInterface::setupError, this, [&](const QString &message) { 0037 removeAction(m_installAction); 0038 removeAction(m_abortAction); 0039 doShowMessage(message, KMessageWidget::Warning); 0040 }); 0041 connect(m_interface, &AbstractPythonInterface::setupMessage, this, 0042 [&](const QString &message, int messageType) { doShowMessage(message, KMessageWidget::MessageType(messageType)); }); 0043 0044 if (!setupErrorOnly) { 0045 connect(m_interface, &AbstractPythonInterface::checkVersionsResult, this, [&](const QStringList &list) { 0046 removeAction(m_abortAction); 0047 if (list.isEmpty()) { 0048 if (m_interface->featureName().isEmpty()) { 0049 doShowMessage(i18n("Everything is properly configured."), KMessageWidget::Positive); 0050 } else { 0051 doShowMessage(i18n("%1 is properly configured.", m_interface->featureName()), KMessageWidget::Positive); 0052 } 0053 } else { 0054 if (m_interface->featureName().isEmpty()) { 0055 doShowMessage(i18n("Everything is configured: %1", list.join(QStringLiteral(", "))), KMessageWidget::Positive); 0056 } else { 0057 doShowMessage(i18n("%1 is configured: %2", m_interface->featureName(), list.join(QStringLiteral(", "))), KMessageWidget::Positive); 0058 } 0059 } 0060 }); 0061 0062 connect(m_interface, &AbstractPythonInterface::dependenciesMissing, this, [&](const QStringList &messages) { 0063 if (!m_interface->installDisabled()) { 0064 m_installAction->setEnabled(true); 0065 removeAction(m_abortAction); 0066 m_installAction->setText(i18n("Install missing dependencies")); 0067 addAction(m_installAction); 0068 } 0069 0070 doShowMessage(messages.join(QStringLiteral("\n")), KMessageWidget::Warning); 0071 }); 0072 0073 if (!m_interface->installDisabled()) { 0074 connect(m_interface, &AbstractPythonInterface::proposeUpdate, this, [&](const QString &message) { 0075 // only allow upgrading python modules once 0076 m_installAction->setText(i18n("Check for update")); 0077 m_installAction->setEnabled(true); 0078 removeAction(m_abortAction); 0079 addAction(m_installAction); 0080 doShowMessage(message, KMessageWidget::Warning); 0081 }); 0082 } 0083 0084 connect(m_interface, &AbstractPythonInterface::dependenciesAvailable, this, [&]() { 0085 if (!m_updated && !m_interface->installDisabled()) { 0086 // only allow upgrading python modules once 0087 m_installAction->setText(i18n("Check for update")); 0088 m_installAction->setEnabled(true); 0089 removeAction(m_abortAction); 0090 addAction(m_installAction); 0091 } 0092 if (text().isEmpty()) { 0093 hide(); 0094 } 0095 }); 0096 0097 connect(m_installAction, &QAction::triggered, this, [&]() { 0098 if (!m_interface->missingDependencies().isEmpty()) { 0099 m_installAction->setEnabled(false); 0100 doShowMessage(i18n("Installing modules… this can take a while"), KMessageWidget::Information); 0101 addAction(m_abortAction); 0102 qApp->processEvents(); 0103 m_interface->installMissingDependencies(); 0104 removeAction(m_installAction); 0105 } else { 0106 // upgrade 0107 m_updated = true; 0108 m_installAction->setEnabled(false); 0109 addAction(m_abortAction); 0110 doShowMessage(i18n("Updating modules…"), KMessageWidget::Information); 0111 qApp->processEvents(); 0112 m_interface->updateDependencies(); 0113 removeAction(m_installAction); 0114 } 0115 }); 0116 } 0117 } 0118 0119 void PythonDependencyMessage::doShowMessage(const QString &message, KMessageWidget::MessageType messageType) 0120 { 0121 if (message.isEmpty()) { 0122 hide(); 0123 } else { 0124 setMessageType(messageType); 0125 setText(message); 0126 show(); 0127 } 0128 } 0129 0130 void PythonDependencyMessage::checkAfterInstall() 0131 { 0132 doShowMessage(i18n("Checking configuration…"), KMessageWidget::Information); 0133 m_interface->checkDependencies(true, false); 0134 } 0135 0136 AbstractPythonInterface::AbstractPythonInterface(QObject *parent) 0137 : QObject{parent} 0138 , m_dependencies() 0139 , m_versions(new QMap<QString, QString>()) 0140 , m_disableInstall(pCore->packageType() == QStringLiteral("flatpak")) 0141 , m_dependenciesChecked(false) 0142 , m_scripts(new QMap<QString, QString>()) 0143 { 0144 addScript(QStringLiteral("checkpackages.py")); 0145 addScript(QStringLiteral("checkgpu.py")); 0146 } 0147 0148 AbstractPythonInterface::~AbstractPythonInterface() 0149 { 0150 delete m_versions; 0151 delete m_scripts; 0152 } 0153 0154 bool AbstractPythonInterface::checkPython(bool useVenv, bool calculateSize, bool forceInstall) 0155 { 0156 if (installInProgress) { 0157 return false; 0158 } 0159 if (!calculateSize && useVenv == KdenliveSettings::usePythonVenv() && !KdenliveSettings::pythonPath().isEmpty() && !KdenliveSettings::pipPath().isEmpty() && 0160 QFile::exists(KdenliveSettings::pythonPath())) { 0161 // Already setup 0162 if (KdenliveSettings::pythonPath().contains(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation))) { 0163 if (useVenv) { 0164 // Everything ok, using venv python 0165 qDebug() << "::::: CHECKING PYTHON WITH VENV EVEYTHING OK..."; 0166 return true; 0167 } 0168 } else if (!useVenv) { 0169 // Everything ok, using system python 0170 qDebug() << "::::: CHECKING PYTHON NO VENV EVEYTHING OK..."; 0171 return true; 0172 } 0173 } 0174 qDebug() << "::::: CHECKING PYTHON, RQST VENV: " << useVenv; 0175 Q_EMIT setupMessage(i18nc("@label:textbox", "Checking setup…"), int(KMessageWidget::Information)); 0176 QMutexLocker bk(&mutex); 0177 QStringList pythonPaths; 0178 if (useVenv) { 0179 #ifdef Q_OS_WIN 0180 const QString pythonPath = QStringLiteral("venv/Scripts/"); 0181 #else 0182 const QString pythonPath = QStringLiteral("venv/bin/"); 0183 #endif 0184 QDir pluginDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); 0185 if (!pluginDir.exists(pythonPath)) { 0186 // Setup venv 0187 if (forceInstall && !setupVenv()) { 0188 return false; 0189 } else { 0190 // Venv folder not found, disable 0191 useVenv = false; 0192 } 0193 } 0194 if (pluginDir.exists(pythonPath)) { 0195 pythonPaths << pluginDir.absoluteFilePath(pythonPath); 0196 } 0197 } 0198 #ifdef Q_OS_WIN 0199 KdenliveSettings::setPythonPath(QStandardPaths::findExecutable(QStringLiteral("python"), pythonPaths)); 0200 KdenliveSettings::setPipPath(QStandardPaths::findExecutable(QStringLiteral("pip"), pythonPaths)); 0201 #else 0202 KdenliveSettings::setPythonPath(QStandardPaths::findExecutable(QStringLiteral("python3"), pythonPaths)); 0203 KdenliveSettings::setPipPath(QStandardPaths::findExecutable(QStringLiteral("pip3"), pythonPaths)); 0204 #endif 0205 if (KdenliveSettings::pythonPath().isEmpty()) { 0206 Q_EMIT setupError(i18n("Cannot find python3, please install it on your system.\n" 0207 "If already installed, check it is installed in a directory " 0208 "listed in PATH environment variable")); 0209 return false; 0210 } 0211 if (KdenliveSettings::pipPath().isEmpty() && !m_disableInstall) { 0212 Q_EMIT setupError(i18n("Cannot find pip3, please install it on your system.\n" 0213 "If already installed, check it is installed in a directory " 0214 "listed in PATH environment variable")); 0215 return false; 0216 } 0217 if (useVenv != KdenliveSettings::usePythonVenv()) { 0218 KdenliveSettings::setUsePythonVenv(useVenv); 0219 Q_EMIT venvSetupChanged(); 0220 } 0221 Q_EMIT setupMessage(i18n("Using python from %1", QFileInfo(KdenliveSettings::pythonPath()).absolutePath()), int(KMessageWidget::Information)); 0222 if (calculateSize) { 0223 // Calculate venv size 0224 QDir pluginDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); 0225 if (pluginDir.cd(QStringLiteral("venv"))) { 0226 KIO::DirectorySizeJob *job = KIO::directorySize(QUrl::fromLocalFile(pluginDir.absolutePath())); 0227 connect(job, &KIO::DirectorySizeJob::result, this, &AbstractPythonInterface::gotFolderSize); 0228 } else { 0229 Q_EMIT gotPythonSize(i18n("No python venv found")); 0230 } 0231 } 0232 return true; 0233 } 0234 0235 void AbstractPythonInterface::gotFolderSize(KJob *job) 0236 { 0237 auto *sourceJob = static_cast<KIO::DirectorySizeJob *>(job); 0238 KIO::filesize_t total = sourceJob->totalSize(); 0239 if (sourceJob->totalFiles() == 0) { 0240 total = 0; 0241 } 0242 Q_EMIT gotPythonSize(i18n("Python venv size: %1", KIO::convertSize(total))); 0243 } 0244 0245 bool AbstractPythonInterface::checkSetup() 0246 { 0247 if (!(KdenliveSettings::pythonPath().isEmpty() || KdenliveSettings::pipPath().isEmpty() || m_scripts->values().contains(QStringLiteral("")))) { 0248 return true; 0249 } 0250 if (!checkPython(KdenliveSettings::usePythonVenv())) { 0251 return false; 0252 } 0253 0254 for (int i = 0; i < m_scripts->count(); i++) { 0255 QString key = m_scripts->keys()[i]; 0256 (*m_scripts)[key] = locateScript(key); 0257 if ((*m_scripts)[key].isEmpty()) { 0258 return false; 0259 } 0260 } 0261 return true; 0262 } 0263 0264 bool AbstractPythonInterface::setupVenv() 0265 { 0266 // First check if python and venv are available 0267 QString pyExec; 0268 #ifdef Q_OS_WIN 0269 pyExec = QStandardPaths::findExecutable(QStringLiteral("python")); 0270 #else 0271 pyExec = QStandardPaths::findExecutable(QStringLiteral("python3")); 0272 #endif 0273 // Check that the system python is found 0274 if (pyExec.isEmpty()) { 0275 Q_EMIT setupError(i18n("Cannot find python3, please install it on your system.\n" 0276 "If already installed, check it is installed in a directory " 0277 "listed in PATH environment variable")); 0278 return false; 0279 } 0280 // Use system python to check for venv 0281 installInProgress = true; 0282 KdenliveSettings::setPythonPath(pyExec); 0283 const QString missingDeps = runScript(QStringLiteral("checkpackages.py"), {"virtualenv"}, QStringLiteral("--check"), false); 0284 if (!missingDeps.isEmpty()) { 0285 Q_EMIT setupError(i18n("Cannot find python virtualenv, please install it on your system. Defaulting to system python.")); 0286 installInProgress = false; 0287 return false; 0288 } 0289 QDir pluginDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); 0290 pluginDir.mkpath(QStringLiteral(".")); 0291 0292 QProcess envProcess; 0293 // For some reason, this fails in AppImage, but when extracting the Appimage it works... 0294 // No workaround found yet for AppImage 0295 QStringList args = {QStringLiteral("-m"), QStringLiteral("venv"), pluginDir.absoluteFilePath(QStringLiteral("venv"))}; 0296 envProcess.start(pyExec, args); 0297 envProcess.waitForStarted(); 0298 envProcess.waitForFinished(-1); 0299 if (envProcess.exitStatus() == QProcess::NormalExit) { 0300 #ifdef Q_OS_WIN 0301 const QString pythonPath = QStringLiteral("venv/Scripts/"); 0302 QStringList pythonPaths = {pluginDir.absoluteFilePath(pluginDir.absoluteFilePath(pythonPath))}; 0303 KdenliveSettings::setPythonPath(QStandardPaths::findExecutable(QStringLiteral("python"), pythonPaths)); 0304 KdenliveSettings::setPipPath(QStandardPaths::findExecutable(QStringLiteral("pip"), pythonPaths)); 0305 #else 0306 const QString pythonPath = QStringLiteral("venv/bin/"); 0307 QStringList pythonPaths = {pluginDir.absoluteFilePath(pluginDir.absoluteFilePath(pythonPath))}; 0308 KdenliveSettings::setPythonPath(QStandardPaths::findExecutable(QStringLiteral("python3"), pythonPaths)); 0309 KdenliveSettings::setPipPath(QStandardPaths::findExecutable(QStringLiteral("pip3"), pythonPaths)); 0310 #endif 0311 if (!KdenliveSettings::pipPath().isEmpty()) { 0312 installPackage({QStringLiteral("importlib")}); 0313 installInProgress = false; 0314 return true; 0315 } 0316 } 0317 installInProgress = false; 0318 return false; 0319 } 0320 0321 QString AbstractPythonInterface::locateScript(const QString &script) 0322 { 0323 QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("scripts/%1").arg(script)); 0324 if (path.isEmpty()) { 0325 Q_EMIT setupError(i18n("The %1 script was not found, check your install.", script)); 0326 } 0327 return path; 0328 } 0329 0330 void AbstractPythonInterface::addDependency(const QString &pipname, const QString &purpose) 0331 { 0332 m_dependencies.insert(pipname, purpose); 0333 } 0334 0335 void AbstractPythonInterface::addScript(const QString &script) 0336 { 0337 m_scripts->insert(script, QStringLiteral("")); 0338 } 0339 0340 void AbstractPythonInterface::checkDependenciesConcurrently() 0341 { 0342 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0343 QtConcurrent::run(this, &AbstractPythonInterface::checkDependencies, false, false); 0344 #else 0345 QtConcurrent::run(&AbstractPythonInterface::checkDependencies, this, false, false); 0346 #endif 0347 } 0348 0349 void AbstractPythonInterface::checkVersionsConcurrently() 0350 { 0351 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0352 QtConcurrent::run(this, &AbstractPythonInterface::checkVersions, true); 0353 #else 0354 QtConcurrent::run(&AbstractPythonInterface::checkVersions, this, true); 0355 #endif 0356 } 0357 0358 void AbstractPythonInterface::checkDependencies(bool force, bool async) 0359 { 0360 if (!force && m_dependenciesChecked) { 0361 // Don't check twice if dependecies are satisfied 0362 return; 0363 } 0364 // Force check, reset flag 0365 m_missing.clear(); 0366 const QString output = runPackageScript(QStringLiteral("--check")); 0367 QStringList messages; 0368 if (!output.isEmpty()) { 0369 // We have missing dependencies 0370 for (auto i : m_dependencies.keys()) { 0371 if (output.contains(i)) { 0372 m_missing.append(i); 0373 if (m_dependencies.value(i).isEmpty()) { 0374 messages.append(xi18n("The <application>%1</application> python module is required.", i)); 0375 } else { 0376 messages.append(xi18n("The <application>%1</application> python module is required for %2.", i, m_dependencies.value(i))); 0377 } 0378 } 0379 } 0380 } 0381 m_dependenciesChecked = true; 0382 if (messages.isEmpty()) { 0383 Q_EMIT dependenciesAvailable(); 0384 if (async) { 0385 checkVersionsConcurrently(); 0386 } else { 0387 checkVersions(true); 0388 } 0389 } else { 0390 Q_EMIT dependenciesMissing(messages); 0391 } 0392 } 0393 0394 QStringList AbstractPythonInterface::missingDependencies(const QStringList &filter) 0395 { 0396 if (filter.isEmpty()) { 0397 return m_missing; 0398 } 0399 QStringList filtered; 0400 for (auto item : filter) { 0401 if (m_missing.contains(item)) { 0402 filtered.append(item); 0403 } 0404 } 0405 return filtered; 0406 }; 0407 0408 void AbstractPythonInterface::installMissingDependencies() 0409 { 0410 if (!KdenliveSettings::usePythonVenv()) { 0411 QDir pluginDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); 0412 if (KMessageBox::questionTwoActions( 0413 pCore->window(), 0414 i18n("Kdenlive can install the missing python modules in a virtual environment under %1.\nThis way, it won't touch your system libraries.", 0415 pluginDir.absoluteFilePath(QStringLiteral("venv"))), 0416 i18n("Python environment"), KGuiItem(i18n("Use virtual environment (recommended)")), 0417 KGuiItem(i18n("Use system install"))) == KMessageBox::PrimaryAction) { 0418 if (!checkPython(true, true, true)) { 0419 return; 0420 } 0421 } 0422 } 0423 runPackageScript(QStringLiteral("--install"), true); 0424 } 0425 0426 void AbstractPythonInterface::updateDependencies() 0427 { 0428 runPackageScript(QStringLiteral("--upgrade"), true); 0429 } 0430 0431 void AbstractPythonInterface::runConcurrentScript(const QString &script, QStringList args) 0432 { 0433 if (m_dependencies.keys().isEmpty()) { 0434 qWarning() << "No dependencies specified"; 0435 Q_EMIT setupError(i18n("Internal Error: Cannot find dependency list")); 0436 return; 0437 } 0438 if (!checkSetup()) { 0439 return; 0440 } 0441 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0442 QtConcurrent::run(this, &AbstractPythonInterface::runScript, script, args, QString(), true, false); 0443 #else 0444 QtConcurrent::run(&AbstractPythonInterface::runScript, this, script, args, QString(), true, false); 0445 #endif 0446 } 0447 0448 void AbstractPythonInterface::proposeMaybeUpdate(const QString &dependency, const QString &minVersion) 0449 { 0450 checkVersions(false); 0451 QString currentVersion = m_versions->value(dependency); 0452 if (currentVersion.isEmpty()) { 0453 Q_EMIT setupError(i18n("Error while checking version of module %1", dependency)); 0454 return; 0455 } 0456 if (versionToInt(currentVersion) < versionToInt(minVersion)) { 0457 Q_EMIT proposeUpdate(i18n("At least version %1 of module %2 is required, " 0458 "but your current version is %3", 0459 minVersion, dependency, currentVersion)); 0460 } else { 0461 Q_EMIT proposeUpdate(i18n("Please consider to update your setup.")); 0462 } 0463 } 0464 0465 int AbstractPythonInterface::versionToInt(const QString &version) 0466 { 0467 QStringList v = version.split(QStringLiteral(".")); 0468 return QT_VERSION_CHECK(v.length() > 0 ? v.at(0).toInt() : 0, v.length() > 1 ? v.at(1).toInt() : 0, v.length() > 2 ? v.at(2).toInt() : 0); 0469 } 0470 0471 void AbstractPythonInterface::checkVersions(bool signalOnResult) 0472 { 0473 if (installDisabled()) { 0474 return; 0475 } 0476 QString output = runPackageScript(QStringLiteral("--details")); 0477 if (output.isEmpty() || !output.contains(QStringLiteral("Version: "))) { 0478 Q_EMIT setupMessage(i18nc("@label:textbox", "No version information available."), int(KMessageWidget::Warning)); 0479 qDebug() << "::: CHECKING DEPENDENCIES... NO VERSION INFO AVAILABLE"; 0480 return; 0481 } 0482 QStringList raw = output.split(QStringLiteral("Version: ")); 0483 QStringList versions; 0484 for (int i = 0; i < raw.count(); i++) { 0485 QString name = raw.at(i); 0486 int pos = name.indexOf(QStringLiteral("Name:")); 0487 if (pos > -1) { 0488 if (pos != 0) { 0489 name.remove(0, pos); 0490 } 0491 name = name.simplified().section(QLatin1Char(' '), 1, 1); 0492 QString version = raw.at(i + 1); 0493 version = version.simplified().section(QLatin1Char(' '), 0, 0); 0494 versions.append(QString("%1 %2").arg(name, version)); 0495 if (m_versions->contains(name)) { 0496 (*m_versions)[name.toLower()] = version; 0497 } else { 0498 m_versions->insert(name.toLower(), version); 0499 } 0500 } 0501 } 0502 qDebug() << "::: CHECKING DEPENDENCIES... VERSION FOUND: " << versions; 0503 if (signalOnResult) { 0504 Q_EMIT checkVersionsResult(versions); 0505 } 0506 } 0507 0508 QString AbstractPythonInterface::runPackageScript(const QString &mode, bool concurrent) 0509 { 0510 if (m_dependencies.keys().isEmpty()) { 0511 qWarning() << "No dependencies specified"; 0512 Q_EMIT setupError(i18n("Internal Error: Cannot find dependency list")); 0513 return {}; 0514 } 0515 if (!checkSetup()) { 0516 return {}; 0517 } 0518 if (concurrent) { 0519 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0520 QtConcurrent::run(this, &AbstractPythonInterface::runScript, QStringLiteral("checkpackages.py"), m_dependencies.keys(), mode, concurrent, true); 0521 #else 0522 QtConcurrent::run(&AbstractPythonInterface::runScript, this, QStringLiteral("checkpackages.py"), m_dependencies.keys(), mode, concurrent, true); 0523 #endif 0524 return {}; 0525 } else { 0526 return runScript(QStringLiteral("checkpackages.py"), m_dependencies.keys(), mode, concurrent, true); 0527 } 0528 } 0529 0530 QString AbstractPythonInterface::installPackage(const QStringList packageNames) 0531 { 0532 if (!checkSetup()) { 0533 return {}; 0534 } 0535 return runScript(QStringLiteral("checkpackages.py"), packageNames, "--install", false, true); 0536 } 0537 0538 QString AbstractPythonInterface::runScript(const QString &script, QStringList args, const QString &firstarg, bool concurrent, bool packageFeedback) 0539 { 0540 QString scriptpath = m_scripts->value(script); 0541 if (KdenliveSettings::pythonPath().isEmpty() || scriptpath.isEmpty()) { 0542 if (KdenliveSettings::pythonPath().isEmpty()) { 0543 Q_EMIT setupError(i18n("Python exec not found")); 0544 } else { 0545 Q_EMIT setupError(i18n("Failed to find script file %1", script)); 0546 } 0547 return {}; 0548 } 0549 if (concurrent && (firstarg == QLatin1String("--install") || firstarg == QLatin1String("--upgrade"))) { 0550 Q_EMIT scriptStarted(); 0551 } 0552 if (!firstarg.isEmpty()) { 0553 args.prepend(firstarg); 0554 } 0555 args.prepend(scriptpath); 0556 QProcess scriptJob; 0557 if (concurrent) { 0558 if (packageFeedback) { 0559 connect(&scriptJob, &QProcess::readyReadStandardOutput, [this, &scriptJob]() { 0560 const QString processData = QString::fromUtf8(scriptJob.readAll()); 0561 if (!processData.isEmpty()) { 0562 Q_EMIT installFeedback(processData.simplified()); 0563 } 0564 }); 0565 } else { 0566 connect(&scriptJob, &QProcess::readyReadStandardOutput, [this, &scriptJob]() { 0567 const QString processData = QString::fromUtf8(scriptJob.readAll()); 0568 Q_EMIT scriptFeedback(processData.split(QLatin1Char('\n'), Qt::SkipEmptyParts)); 0569 }); 0570 } 0571 } 0572 connect(this, &AbstractPythonInterface::abortScript, &scriptJob, &QProcess::kill, Qt::DirectConnection); 0573 scriptJob.start(KdenliveSettings::pythonPath(), args); 0574 // Don't timeout 0575 qDebug() << "::: RUNNONG SCRIPT: " << KdenliveSettings::pythonPath() << " = " << args; 0576 scriptJob.waitForFinished(-1); 0577 if (!concurrent && (scriptJob.exitStatus() != QProcess::NormalExit || scriptJob.exitCode() != 0)) { 0578 qDebug() << "::::: WARNING ERRROR EXIT STATUS: " << scriptJob.exitCode(); 0579 const QString errorMessage = scriptJob.readAllStandardError(); 0580 Q_EMIT setupError(i18n("Error while running python3 script:\n %1\n%2", scriptpath, errorMessage)); 0581 qWarning() << " SCRIPT ERROR: " << errorMessage; 0582 return {}; 0583 } 0584 if (script == QLatin1String("checkgpu.py")) { 0585 Q_EMIT scriptGpuCheckFinished(); 0586 } else if (concurrent && (firstarg == QLatin1String("--install") || firstarg == QLatin1String("--upgrade"))) { 0587 Q_EMIT scriptFinished(); 0588 } 0589 return scriptJob.readAllStandardOutput(); 0590 } 0591 0592 bool AbstractPythonInterface::removePythonVenv() 0593 { 0594 QDir pluginDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); 0595 if (!pluginDir.exists(QStringLiteral("venv")) || !pluginDir.absolutePath().contains(QStringLiteral("kdenlive"))) { 0596 return false; 0597 } 0598 if (!pluginDir.cd(QStringLiteral("venv"))) { 0599 return false; 0600 } 0601 if (KMessageBox::warningContinueCancel(pCore->window(), 0602 i18n("This will delete the python virtual environment from:<br/><b>%1</b><br/>The environment will be recreated " 0603 "and modules downloaded whenever you reenable the python virtual environment.", 0604 pluginDir.absolutePath())) != KMessageBox::Continue) { 0605 return false; 0606 } 0607 return pluginDir.removeRecursively(); 0608 } 0609 0610 bool AbstractPythonInterface::installInProcess() const 0611 { 0612 return installInProgress; 0613 }