mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-25 02:36:10 +03:00
Allow users to specify Python executable path
Closes #19195. PR #19644.
This commit is contained in:
parent
529e49aea7
commit
b3fda76027
10 changed files with 77 additions and 16 deletions
|
@ -1487,6 +1487,19 @@ void Preferences::setTrackerPortForwardingEnabled(const bool enabled)
|
||||||
setValue(u"Preferences/Advanced/trackerPortForwarding"_s, enabled);
|
setValue(u"Preferences/Advanced/trackerPortForwarding"_s, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path Preferences::getPythonExecutablePath() const
|
||||||
|
{
|
||||||
|
return value(u"Preferences/Search/pythonExecutablePath"_s, Path());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setPythonExecutablePath(const Path &path)
|
||||||
|
{
|
||||||
|
if (path == getPythonExecutablePath())
|
||||||
|
return;
|
||||||
|
|
||||||
|
setValue(u"Preferences/Search/pythonExecutablePath"_s, path);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||||
bool Preferences::isUpdateCheckEnabled() const
|
bool Preferences::isUpdateCheckEnabled() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -305,6 +305,8 @@ public:
|
||||||
void setTrackerPort(int port);
|
void setTrackerPort(int port);
|
||||||
bool isTrackerPortForwardingEnabled() const;
|
bool isTrackerPortForwardingEnabled() const;
|
||||||
void setTrackerPortForwardingEnabled(bool enabled);
|
void setTrackerPortForwardingEnabled(bool enabled);
|
||||||
|
Path getPythonExecutablePath() const;
|
||||||
|
void setPythonExecutablePath(const Path &path);
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||||
bool isUpdateCheckEnabled() const;
|
bool isUpdateCheckEnabled() const;
|
||||||
void setUpdateCheckEnabled(bool enabled);
|
void setUpdateCheckEnabled(bool enabled);
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/path.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/utils/bytearray.h"
|
#include "base/utils/bytearray.h"
|
||||||
|
|
||||||
using namespace Utils::ForeignApps;
|
using namespace Utils::ForeignApps;
|
||||||
|
@ -52,6 +54,8 @@ namespace
|
||||||
{
|
{
|
||||||
bool testPythonInstallation(const QString &exeName, PythonInfo &info)
|
bool testPythonInstallation(const QString &exeName, PythonInfo &info)
|
||||||
{
|
{
|
||||||
|
info = {};
|
||||||
|
|
||||||
QProcess proc;
|
QProcess proc;
|
||||||
proc.start(exeName, {u"--version"_s}, QIODevice::ReadOnly);
|
proc.start(exeName, {u"--version"_s}, QIODevice::ReadOnly);
|
||||||
if (proc.waitForFinished() && (proc.exitCode() == QProcess::NormalExit))
|
if (proc.waitForFinished() && (proc.exitCode() == QProcess::NormalExit))
|
||||||
|
@ -77,7 +81,7 @@ namespace
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
info = {exeName, version};
|
info = {exeName, version};
|
||||||
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python detected, executable name: '%1', version: %2")
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Found Python executable. Name: \"%1\". Version: \"%2\"")
|
||||||
.arg(info.executableName, info.version.toString()), Log::INFO);
|
.arg(info.executableName, info.version.toString()), Log::INFO);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -254,20 +258,43 @@ bool Utils::ForeignApps::PythonInfo::isSupportedVersion() const
|
||||||
PythonInfo Utils::ForeignApps::pythonInfo()
|
PythonInfo Utils::ForeignApps::pythonInfo()
|
||||||
{
|
{
|
||||||
static PythonInfo pyInfo;
|
static PythonInfo pyInfo;
|
||||||
|
|
||||||
|
const QString preferredPythonPath = Preferences::instance()->getPythonExecutablePath().toString();
|
||||||
|
if (pyInfo.isValid() && (preferredPythonPath == pyInfo.executableName))
|
||||||
|
return pyInfo;
|
||||||
|
|
||||||
|
if (!preferredPythonPath.isEmpty())
|
||||||
|
{
|
||||||
|
if (testPythonInstallation(preferredPythonPath, pyInfo))
|
||||||
|
return pyInfo;
|
||||||
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find Python executable. Path: \"%1\".")
|
||||||
|
.arg(preferredPythonPath), Log::WARNING);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// auto detect only when there are no preferred python path
|
||||||
|
|
||||||
if (!pyInfo.isValid())
|
if (!pyInfo.isValid())
|
||||||
{
|
{
|
||||||
if (testPythonInstallation(u"python3"_s, pyInfo))
|
if (testPythonInstallation(u"python3"_s, pyInfo))
|
||||||
return pyInfo;
|
return pyInfo;
|
||||||
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python3` executable in PATH environment variable. PATH: \"%1\"")
|
||||||
|
.arg(qEnvironmentVariable("PATH")), Log::INFO);
|
||||||
|
|
||||||
if (testPythonInstallation(u"python"_s, pyInfo))
|
if (testPythonInstallation(u"python"_s, pyInfo))
|
||||||
return pyInfo;
|
return pyInfo;
|
||||||
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python` executable in PATH environment variable. PATH: \"%1\"")
|
||||||
|
.arg(qEnvironmentVariable("PATH")), Log::INFO);
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
if (testPythonInstallation(findPythonPath(), pyInfo))
|
if (testPythonInstallation(findPythonPath(), pyInfo))
|
||||||
return pyInfo;
|
return pyInfo;
|
||||||
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python` executable in Windows Registry."), Log::INFO);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python not detected"), Log::INFO);
|
LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find Python executable"), Log::WARNING);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pyInfo;
|
return pyInfo;
|
||||||
|
|
|
@ -99,6 +99,7 @@ namespace
|
||||||
TRACKER_STATUS,
|
TRACKER_STATUS,
|
||||||
TRACKER_PORT,
|
TRACKER_PORT,
|
||||||
TRACKER_PORT_FORWARDING,
|
TRACKER_PORT_FORWARDING,
|
||||||
|
PYTHON_EXECUTABLE_PATH,
|
||||||
// libtorrent section
|
// libtorrent section
|
||||||
LIBTORRENT_HEADER,
|
LIBTORRENT_HEADER,
|
||||||
BDECODE_DEPTH_LIMIT,
|
BDECODE_DEPTH_LIMIT,
|
||||||
|
@ -316,7 +317,8 @@ void AdvancedSettings::saveAdvancedSettings() const
|
||||||
pref->setTrackerPort(m_spinBoxTrackerPort.value());
|
pref->setTrackerPort(m_spinBoxTrackerPort.value());
|
||||||
pref->setTrackerPortForwardingEnabled(m_checkBoxTrackerPortForwarding.isChecked());
|
pref->setTrackerPortForwardingEnabled(m_checkBoxTrackerPortForwarding.isChecked());
|
||||||
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
|
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
|
||||||
|
// Python executable path
|
||||||
|
pref->setPythonExecutablePath(Path(m_pythonExecutablePath.text().trimmed()));
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value<BitTorrent::ChokingAlgorithm>());
|
session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value<BitTorrent::ChokingAlgorithm>());
|
||||||
// Seed choking algorithm
|
// Seed choking algorithm
|
||||||
|
@ -810,6 +812,10 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||||
// Tracker port forwarding
|
// Tracker port forwarding
|
||||||
m_checkBoxTrackerPortForwarding.setChecked(pref->isTrackerPortForwardingEnabled());
|
m_checkBoxTrackerPortForwarding.setChecked(pref->isTrackerPortForwardingEnabled());
|
||||||
addRow(TRACKER_PORT_FORWARDING, tr("Enable port forwarding for embedded tracker"), &m_checkBoxTrackerPortForwarding);
|
addRow(TRACKER_PORT_FORWARDING, tr("Enable port forwarding for embedded tracker"), &m_checkBoxTrackerPortForwarding);
|
||||||
|
// Python executable path
|
||||||
|
m_pythonExecutablePath.setPlaceholderText(tr("(Auto detect if empty)"));
|
||||||
|
m_pythonExecutablePath.setText(pref->getPythonExecutablePath().toString());
|
||||||
|
addRow(PYTHON_EXECUTABLE_PATH, tr("Python executable path (may require restart)"), &m_pythonExecutablePath);
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots));
|
m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots));
|
||||||
m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased));
|
m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased));
|
||||||
|
|
|
@ -81,7 +81,7 @@ private:
|
||||||
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport;
|
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport;
|
||||||
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
|
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
|
||||||
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage;
|
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage;
|
||||||
QLineEdit m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
|
QLineEdit m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
|
||||||
|
|
||||||
#ifndef QBT_USES_LIBTORRENT2
|
#ifndef QBT_USES_LIBTORRENT2
|
||||||
QSpinBox m_spinBoxCache, m_spinBoxCacheTTL;
|
QSpinBox m_spinBoxCache, m_spinBoxCacheTTL;
|
||||||
|
|
|
@ -1635,11 +1635,11 @@ void MainWindow::on_actionRSSReader_triggered()
|
||||||
|
|
||||||
void MainWindow::on_actionSearchWidget_triggered()
|
void MainWindow::on_actionSearchWidget_triggered()
|
||||||
{
|
{
|
||||||
if (!m_hasPython && m_ui->actionSearchWidget->isChecked())
|
if (m_ui->actionSearchWidget->isChecked())
|
||||||
{
|
{
|
||||||
const Utils::ForeignApps::PythonInfo pyInfo = Utils::ForeignApps::pythonInfo();
|
const Utils::ForeignApps::PythonInfo pyInfo = Utils::ForeignApps::pythonInfo();
|
||||||
|
|
||||||
// Not installed
|
// Not found
|
||||||
if (!pyInfo.isValid())
|
if (!pyInfo.isValid())
|
||||||
{
|
{
|
||||||
m_ui->actionSearchWidget->setChecked(false);
|
m_ui->actionSearchWidget->setChecked(false);
|
||||||
|
@ -1679,7 +1679,6 @@ void MainWindow::on_actionSearchWidget_triggered()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hasPython = true;
|
|
||||||
m_ui->actionSearchWidget->setChecked(true);
|
m_ui->actionSearchWidget->setChecked(true);
|
||||||
Preferences::instance()->setSearchEnabled(true);
|
Preferences::instance()->setSearchEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,6 @@ private:
|
||||||
// Power Management
|
// Power Management
|
||||||
PowerManagement *m_pwr = nullptr;
|
PowerManagement *m_pwr = nullptr;
|
||||||
QTimer *m_preventTimer = nullptr;
|
QTimer *m_preventTimer = nullptr;
|
||||||
bool m_hasPython = false;
|
|
||||||
QMenu *m_toolbarMenu = nullptr;
|
QMenu *m_toolbarMenu = nullptr;
|
||||||
|
|
||||||
SettingValue<bool> m_storeExecutionLogEnabled;
|
SettingValue<bool> m_storeExecutionLogEnabled;
|
||||||
|
|
|
@ -404,6 +404,8 @@ void AppController::preferencesAction()
|
||||||
data[u"enable_embedded_tracker"_s] = session->isTrackerEnabled();
|
data[u"enable_embedded_tracker"_s] = session->isTrackerEnabled();
|
||||||
data[u"embedded_tracker_port"_s] = pref->getTrackerPort();
|
data[u"embedded_tracker_port"_s] = pref->getTrackerPort();
|
||||||
data[u"embedded_tracker_port_forwarding"_s] = pref->isTrackerPortForwardingEnabled();
|
data[u"embedded_tracker_port_forwarding"_s] = pref->isTrackerPortForwardingEnabled();
|
||||||
|
// Python executable path
|
||||||
|
data[u"python_executable_path"_s] = pref->getPythonExecutablePath().toString();
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
data[u"upload_slots_behavior"_s] = static_cast<int>(session->chokingAlgorithm());
|
data[u"upload_slots_behavior"_s] = static_cast<int>(session->chokingAlgorithm());
|
||||||
// Seed choking algorithm
|
// Seed choking algorithm
|
||||||
|
@ -990,6 +992,9 @@ void AppController::setPreferencesAction()
|
||||||
pref->setTrackerPortForwardingEnabled(it.value().toBool());
|
pref->setTrackerPortForwardingEnabled(it.value().toBool());
|
||||||
if (hasKey(u"enable_embedded_tracker"_s))
|
if (hasKey(u"enable_embedded_tracker"_s))
|
||||||
session->setTrackerEnabled(it.value().toBool());
|
session->setTrackerEnabled(it.value().toBool());
|
||||||
|
// Python executable path
|
||||||
|
if (hasKey(u"python_executable_path"_s))
|
||||||
|
pref->setPythonExecutablePath(Path(it.value().toString()));
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
if (hasKey(u"upload_slots_behavior"_s))
|
if (hasKey(u"upload_slots_behavior"_s))
|
||||||
session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(it.value().toInt()));
|
session->setChokingAlgorithm(static_cast<BitTorrent::ChokingAlgorithm>(it.value().toInt()));
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
#include "base/utils/version.h"
|
#include "base/utils/version.h"
|
||||||
#include "api/isessionmanager.h"
|
#include "api/isessionmanager.h"
|
||||||
|
|
||||||
inline const Utils::Version<3, 2> API_VERSION {2, 9, 4};
|
inline const Utils::Version<3, 2> API_VERSION {2, 9, 5};
|
||||||
|
|
||||||
class APIController;
|
class APIController;
|
||||||
class AuthController;
|
class AuthController;
|
||||||
|
|
|
@ -1050,6 +1050,14 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
<input type="checkbox" id="embeddedTrackerPortForwarding" />
|
<input type="checkbox" id="embeddedTrackerPortForwarding" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="pythonExecutablePath">QBT_TR(Python executable path (may require restart):)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="pythonExecutablePath" placeholder="QBT_TR((Auto detect if empty))QBT_TR[CONTEXT=OptionsDialog]" style="width: 15em;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="settings">
|
<fieldset class="settings">
|
||||||
|
@ -2238,6 +2246,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
$('enableEmbeddedTracker').setProperty('checked', pref.enable_embedded_tracker);
|
$('enableEmbeddedTracker').setProperty('checked', pref.enable_embedded_tracker);
|
||||||
$('embeddedTrackerPort').setProperty('value', pref.embedded_tracker_port);
|
$('embeddedTrackerPort').setProperty('value', pref.embedded_tracker_port);
|
||||||
$('embeddedTrackerPortForwarding').setProperty('checked', pref.embedded_tracker_port_forwarding);
|
$('embeddedTrackerPortForwarding').setProperty('checked', pref.embedded_tracker_port_forwarding);
|
||||||
|
$('pythonExecutablePath').setProperty('value', pref.python_executable_path);
|
||||||
$('uploadSlotsBehavior').setProperty('value', pref.upload_slots_behavior);
|
$('uploadSlotsBehavior').setProperty('value', pref.upload_slots_behavior);
|
||||||
$('uploadChokingAlgorithm').setProperty('value', pref.upload_choking_algorithm);
|
$('uploadChokingAlgorithm').setProperty('value', pref.upload_choking_algorithm);
|
||||||
$('announceAllTrackers').setProperty('checked', pref.announce_to_all_trackers);
|
$('announceAllTrackers').setProperty('checked', pref.announce_to_all_trackers);
|
||||||
|
@ -2671,6 +2680,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
settings.set('enable_embedded_tracker', $('enableEmbeddedTracker').getProperty('checked'));
|
settings.set('enable_embedded_tracker', $('enableEmbeddedTracker').getProperty('checked'));
|
||||||
settings.set('embedded_tracker_port', $('embeddedTrackerPort').getProperty('value'));
|
settings.set('embedded_tracker_port', $('embeddedTrackerPort').getProperty('value'));
|
||||||
settings.set('embedded_tracker_port_forwarding', $('embeddedTrackerPortForwarding').getProperty('checked'));
|
settings.set('embedded_tracker_port_forwarding', $('embeddedTrackerPortForwarding').getProperty('checked'));
|
||||||
|
settings.set('python_executable_path', $('pythonExecutablePath').getProperty('value'));
|
||||||
settings.set('upload_slots_behavior', $('uploadSlotsBehavior').getProperty('value'));
|
settings.set('upload_slots_behavior', $('uploadSlotsBehavior').getProperty('value'));
|
||||||
settings.set('upload_choking_algorithm', $('uploadChokingAlgorithm').getProperty('value'));
|
settings.set('upload_choking_algorithm', $('uploadChokingAlgorithm').getProperty('value'));
|
||||||
settings.set('announce_to_all_trackers', $('announceAllTrackers').getProperty('checked'));
|
settings.set('announce_to_all_trackers', $('announceAllTrackers').getProperty('checked'));
|
||||||
|
|
Loading…
Reference in a new issue