diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui index 6497cf24fb..8299ff1d6a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingGUIControls.ui @@ -1,347 +1,231 @@ QmitkSegmentAnythingGUIControls 0 0 699 490 0 0 100 0 100000 100000 QmitkSegmentAnythingToolWidget 0 0 0 0 - + 0 0 <html><head/><body><p>Welcome to Segment Anything Model (SAM) tool in MITK. [Experimental]</p><p>Please note that this is only an interface to SAM. MITK does not ship with SAM. Make sure to have a working internet connection to install SAM via MITK. </p><p>Refer to <a href="https://segment-anything.com/"><span style=" text-decoration: underline; color:#0000ff;">https://segment-anything.com</span></a> to learn everything about the SAM.</p><p><br/></p></body></html> Qt::RichText true - - - - - - 0 - 0 - - - - - 100000 - 16777215 - - - - Install SAM - - - - - - - - - 0 - 0 - - - - Install Options - - - true - - - 5 - - - true - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - - - - - 0 - 0 - - - - System Python: - - - - - - - - 0 - 0 - - - - - 100000 - 16777215 - - - - Clear Install - - - - - - - - - - + 0 0 Press SHIFT+Left-click for positive seeds. Press SHIFT+Right-click for negative seeds. Press DEL to remove last seed. 0 0 Preferences true 5 true 0 0 0 0 6 0 0 GPU Id: - - - - - 0 - 0 - - - - Model Type: - - - - - - 0 0 100000 16777215 Reset Picks 0 0 100000 16777215 Activate SAM 0 0 true 1 0 false ctkComboBox QComboBox
ctkComboBox.h
1
ctkCollapsibleGroupBox QWidget
ctkCollapsibleGroupBox.h
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp index 982a9fc549..9c416893da 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.cpp @@ -1,530 +1,341 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSegmentAnythingToolGUI.h" #include "mitkSegmentAnythingTool.h" #include "mitkProcessExecutor.h" #include #include #include #include #include #include #include +#include +#include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkSegmentAnythingToolGUI, "") +namespace +{ + mitk::IPreferences *GetPreferences() + { + auto *preferencesService = mitk::CoreServices::GetPreferencesService(); + return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); + } +} + QmitkSegmentAnythingToolGUI::QmitkSegmentAnythingToolGUI() : QmitkSegWithPreviewToolGUIBase(true) { // Nvidia-smi command returning zero doesn't always imply lack of GPUs. // Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given. if (m_GpuLoader.GetGPUCount() == 0) { - std::string warning = "WARNING: No GPUs were detected on your machine. The TotalSegmentator tool can be very slow."; + std::string warning = "WARNING: No GPUs were detected on your machine. The SegmentAnything tool can be very slow."; this->ShowErrorMessage(warning); } m_EnableConfirmSegBtnFnc = [this](bool enabled) { bool result = false; auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { result = enabled && tool->HasPicks(); } return result; }; + m_Prefences = GetPreferences(); } void QmitkSegmentAnythingToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); -#ifndef _WIN32 - m_Controls.sysPythonComboBox->addItem("/usr/bin"); -#endif - this->AutoParsePythonPaths(); - m_Controls.sysPythonComboBox->addItem("Select..."); - m_Controls.sysPythonComboBox->setCurrentIndex(0); m_Controls.statusLabel->setTextFormat(Qt::RichText); - m_Controls.modelTypeComboBox->addItems(VALID_MODELS_URL_MAP.keys()); QString welcomeText; this->SetGPUInfo(); if (m_GpuLoader.GetGPUCount() != 0) { welcomeText = "STATUS: Welcome to Segment Anything tool. You're in luck: " + QString::number(m_GpuLoader.GetGPUCount()) + " GPU(s) were detected."; } else { welcomeText = "STATUS: Welcome to Segment Anything tool. Sorry, " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected."; } connect(m_Controls.activateButton, SIGNAL(clicked()), this, SLOT(OnActivateBtnClicked())); connect(m_Controls.resetButton, SIGNAL(clicked()), this, SLOT(OnResetPicksClicked())); - connect(m_Controls.installButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked())); - connect(m_Controls.clearButton, SIGNAL(clicked()), this, SLOT(OnClearInstall())); - connect(m_Controls.sysPythonComboBox, - QOverload::of(&QComboBox::activated), - [=](int index) { OnSystemPythonChanged(m_Controls.sysPythonComboBox->itemText(index)); }); - connect(m_Controls.gpuComboBox, SIGNAL(currentTextChanged(const QString &)), this, - SLOT(OnParametersChanged(const QString &))); - connect(m_Controls.modelTypeComboBox, SIGNAL(currentTextChanged(const QString &)), this, - SLOT(OnParametersChanged(const QString &))); - QIcon deleteIcon = - QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/edit-delete.svg")); QIcon arrowIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/go-next.svg")); - m_Controls.clearButton->setIcon(deleteIcon); m_Controls.activateButton->setIcon(arrowIcon); - const QString storageDir = m_Installer.GetVirtualEnvPath(); - m_IsInstalled = this->IsSAMInstalled(storageDir); - if (m_IsInstalled) + bool isInstalled = this->ValidatePrefences(); + if (isInstalled) { - m_PythonPath = GetExactPythonPath(storageDir); - m_Installer.SetVirtualEnvPath(m_PythonPath); + m_PythonPath = QString::fromStdString(m_Prefences->Get("sam python path", "")); welcomeText += " SAM is already found installed."; } else { - welcomeText += " SAM is not installed. Please click on \"Install SAM\" above."; + welcomeText += " SAM is not configured correctly. Please go to Prefences (Cntl+P) to configure and/or install SAM."; } - this->EnableAll(m_IsInstalled); + this->EnableAll(isInstalled); this->WriteStatusMessage(welcomeText); this->ShowProgressBar(false); m_Controls.samProgressBar->setMaximum(0); mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); } -QString QmitkSegmentAnythingToolGUI::GetExactPythonPath(const QString &pyEnv) const +bool QmitkSegmentAnythingToolGUI::ValidatePrefences() { - QString fullPath = pyEnv; - bool isPythonExists = false; -#ifdef _WIN32 - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - if (!isPythonExists && - !(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("Scripts"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - } -#else - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - if (!isPythonExists && - !(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("bin"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - } -#endif - if (!isPythonExists) - { - fullPath.clear(); - } - return fullPath; + const QString storageDir = QString::fromStdString(m_Prefences->Get("sam python path", "")); + bool isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir); + std::string modelType = m_Prefences->Get("sam modeltype", ""); + std::string path = m_Prefences->Get("sam parent path", ""); + return (isInstalled && !modelType.empty() && !path.empty()); } void QmitkSegmentAnythingToolGUI::EnableAll(bool isEnable) { m_Controls.activateButton->setEnabled(isEnable); - m_Controls.installButton->setEnabled((!isEnable)); } void QmitkSegmentAnythingToolGUI::SetGPUInfo() { std::vector specs = m_GpuLoader.GetAllGPUSpecs(); for (const QmitkGPUSpec &gpuSpec : specs) { m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")"); } if (specs.empty()) { m_Controls.gpuComboBox->setEditable(true); m_Controls.gpuComboBox->addItem(QString::number(0)); m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this)); } } void QmitkSegmentAnythingToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); qApp->processEvents(); } void QmitkSegmentAnythingToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); qApp->processEvents(); } void QmitkSegmentAnythingToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } -void QmitkSegmentAnythingToolGUI::AutoParsePythonPaths() -{ - QString homeDir = QDir::homePath(); - std::vector searchDirs; -#ifdef _WIN32 - searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() + - QString("anaconda3")); -#else - // Add search locations for possible standard python paths here - searchDirs.push_back(homeDir + QDir::separator() + "environments"); - searchDirs.push_back(homeDir + QDir::separator() + "anaconda3"); - searchDirs.push_back(homeDir + QDir::separator() + "miniconda3"); - searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3"); - searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3"); -#endif - for (QString searchDir : searchDirs) - { - if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive)) - { - if (QDir(searchDir).exists()) - { - m_Controls.sysPythonComboBox->addItem("(base): " + searchDir); - searchDir.append((QDir::separator() + QString("envs"))); - } - } - for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();) - { - subIt.next(); - QString envName = subIt.fileName(); - if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. - { - m_Controls.sysPythonComboBox->addItem("(" + envName + "): " + subIt.filePath()); - } - } - } -} - unsigned int QmitkSegmentAnythingToolGUI::FetchSelectedGPUFromUI() const { QString gpuInfo = m_Controls.gpuComboBox->currentText(); if (m_GpuLoader.GetGPUCount() == 0) { return static_cast(gpuInfo.toInt()); } else { QString gpuId = gpuInfo.split(":", QString::SplitBehavior::SkipEmptyParts).first(); return static_cast(gpuId.toInt()); } } void QmitkSegmentAnythingToolGUI::OnActivateBtnClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } try { m_Controls.activateButton->setEnabled(false); qApp->processEvents(); - if (!this->IsSAMInstalled(m_PythonPath)) + if (!QmitkSegmentAnythingToolGUI::IsSAMInstalled(m_PythonPath)) { throw std::runtime_error(WARNING_SAM_NOT_FOUND); } tool->SetIsAuto(false); tool->SetPythonPath(m_PythonPath.toStdString()); tool->SetGpuId(FetchSelectedGPUFromUI()); - QString modelType = m_Controls.modelTypeComboBox->currentText(); + const QString modelType = QString::fromStdString(m_Prefences->Get("sam modeltype", "")); tool->SetModelType(modelType.toStdString()); this->WriteStatusMessage( QString("STATUS: Checking if model is already downloaded... This might take a while.")); if (this->DownloadModel(modelType)) { this->ActivateSAMDaemon(); this->WriteStatusMessage(QString("STATUS: Model found. SAM Activated.")); } else { tool->IsReadyOff(); this->WriteStatusMessage(QString("STATUS: Model not found. Starting download...")); } } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for SAM segmentation. Reason: " << e.what(); this->ShowErrorMessage(errorMsg.str()); this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.activateButton->setEnabled(true); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation SAM segmentation."; this->ShowErrorMessage(errorMsg); m_Controls.activateButton->setEnabled(true); return; } } void QmitkSegmentAnythingToolGUI::ActivateSAMDaemon() { auto tool = this->GetConnectedToolAs(); if (nullptr == tool) { return; } this->ShowProgressBar(true); tool->InitSAMPythonProcess(); while (!tool->IsPythonReady()) { qApp->processEvents(); } tool->IsReadyOn(); m_Controls.activateButton->setEnabled(true); this->ShowProgressBar(false); } -QString QmitkSegmentAnythingToolGUI::GetPythonPathFromUI(const QString &pyUI) const -{ - QString fullPath = pyUI; - if (-1 != fullPath.indexOf(")")) - { - fullPath = fullPath.mid(fullPath.indexOf(")") + 2); - } - return fullPath.simplified(); -} - bool QmitkSegmentAnythingToolGUI::DownloadModel(const QString &modelType) { - QUrl url = VALID_MODELS_URL_MAP[modelType]; + QUrl url = QmitkSegmentAnythingToolGUI::VALID_MODELS_URL_MAP[modelType]; QString modelFileName = url.fileName(); - const QString& storageDir = m_Installer.STORAGE_DIR; + const QString storageDir = QString::fromStdString(m_Prefences->Get("sam parent path", "")); QString checkPointPath = storageDir + QDir::separator() + modelFileName; if (QFile::exists(checkPointPath)) { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->SetCheckpointPath(checkPointPath.toStdString()); } return true; } connect(&m_Manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(FileDownloaded(QNetworkReply*))); QNetworkRequest request(url); m_Manager.get(request); this->ShowProgressBar(true); return false; } void QmitkSegmentAnythingToolGUI::FileDownloaded(QNetworkReply *reply) { - const QString &storageDir = m_Installer.STORAGE_DIR; + const QString storageDir = QString::fromStdString(m_Prefences->Get("sam parent path", "")); const QString &modelFileName = reply->url().fileName(); QFile file(storageDir + QDir::separator() + modelFileName); if (file.open(QIODevice::WriteOnly)) { file.write(reply->readAll()); file.close(); disconnect(&m_Manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(FileDownloaded(QNetworkReply *))); + this->WriteStatusMessage(QString("STATUS: Model successfully downloaded. Activating SAM....")); auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->SetCheckpointPath(file.fileName().toStdString()); this->ActivateSAMDaemon(); this->WriteStatusMessage(QString("STATUS: Model successfully downloaded. SAM Activated.")); } } else { this->WriteErrorMessage("STATUS: Model couldn't be downloaded. SAM not activated."); } this->EnableAll(true); this->ShowProgressBar(false); } -QString QmitkSegmentAnythingToolGUI::OnSystemPythonChanged(const QString &pyEnv) -{ - QString pyPath; - if (pyEnv == QString("Select...")) - { - m_Controls.activateButton->setDisabled(true); - QString path = - QFileDialog::getExistingDirectory(m_Controls.sysPythonComboBox->parentWidget(), "Python Path", "dir"); - if (!path.isEmpty()) - { - this->OnSystemPythonChanged(path); // recall same function for new path validation - bool oldState = m_Controls.sysPythonComboBox->blockSignals(true); // block signal firing while inserting item - m_Controls.sysPythonComboBox->insertItem(0, path); - m_Controls.sysPythonComboBox->setCurrentIndex(0); - m_Controls.sysPythonComboBox->blockSignals( - oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration - } - } - else - { - QString uiPyPath = this->GetPythonPathFromUI(pyEnv); - pyPath = this->GetExactPythonPath(uiPyPath); - } - return pyPath; -} - void QmitkSegmentAnythingToolGUI::ShowProgressBar(bool enabled) { m_Controls.samProgressBar->setEnabled(enabled); m_Controls.samProgressBar->setVisible(enabled); } bool QmitkSegmentAnythingToolGUI::IsSAMInstalled(const QString &pythonPath) { QString fullPath = pythonPath; bool isPythonExists = false; + bool samExists = false; #ifdef _WIN32 isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("Scripts"); isPythonExists = (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists; } #else isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("bin"); isPythonExists = (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists; } #endif - bool isExists = /*QFile::exists(fullPath + QDir::separator() + QString("MITK_SAM"))*/ true && isPythonExists; + QDir folderPath(fullPath); + folderPath.cdUp(); + if (folderPath.cd("Lib/site-packages/segment_anything")) + { + samExists = true; + } + bool isExists = samExists && isPythonExists; return isExists; } void QmitkSegmentAnythingToolGUI::OnParametersChanged(const QString &) { if (m_Controls.activateButton->isEnabled()) { this->WriteStatusMessage("STATUS: Please Reactivate SAM."); } } void QmitkSegmentAnythingToolGUI::OnResetPicksClicked() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { tool->ClearPicks(); } } - -void QmitkSegmentAnythingToolGUI::OnInstallBtnClicked() -{ - bool isInstalled = false; - QString systemPython = OnSystemPythonChanged(m_Controls.sysPythonComboBox->currentText()); - if (systemPython.isEmpty()) - { - this->WriteErrorMessage("ERROR: Couldn't find Python."); - } - else - { - this->WriteStatusMessage("STATUS: Installing SAM..."); - m_Installer.SetSystemPythonPath(systemPython); - isInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME); - if (isInstalled) - { - m_PythonPath = this->GetExactPythonPath(m_Installer.GetVirtualEnvPath()); - this->WriteStatusMessage("STATUS: Successfully installed SAM."); - } - else - { - this->WriteErrorMessage("ERROR: Couldn't install SAM."); - } - } - this->EnableAll(isInstalled); -} - -void QmitkSegmentAnythingToolGUI::OnClearInstall() -{ - QDir folderPath(m_Installer.GetVirtualEnvPath()); - if (folderPath.removeRecursively()) - { - m_Controls.installButton->setEnabled(true); - m_IsInstalled = false; - } - else - { - MITK_ERROR << "The virtual environment couldn't be removed. Please check if you have the required access " - "privileges or, some other process is accessing the folders."; - } - this->EnableAll(m_IsInstalled); -} - -bool QmitkSegmentAnythingToolInstaller::SetupVirtualEnv(const QString &venvName) -{ - if (GetSystemPythonPath().isEmpty()) - { - return false; - } - QDir folderPath(GetBaseDir()); - folderPath.mkdir(venvName); - if (!folderPath.cd(venvName)) - { - return false; // Check if directory creation was successful. - } - mitk::ProcessExecutor::ArgumentListType args; - auto spExec = mitk::ProcessExecutor::New(); - auto spCommand = itk::CStyleCommand::New(); - spCommand->SetCallback(&PrintProcessEvent); - spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); - - args.push_back("-m"); - args.push_back("venv"); - args.push_back(venvName.toStdString()); -#ifdef _WIN32 - QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python.exe"; - QString pythonExeFolder = "Scripts"; -#else - QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python3"; - QString pythonExeFolder = "bin"; -#endif - spExec->Execute(GetBaseDir().toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment - if (folderPath.cd(pythonExeFolder)) - { - this->SetPythonPath(folderPath.absolutePath()); - this->SetPipPath(folderPath.absolutePath()); - this->InstallPytorch(); - for (auto &package : PACKAGES) - { - this->PipInstall(package.toStdString(), &PrintProcessEvent); - } - std::string pythonCode; // python syntax to check if torch is installed with CUDA. - pythonCode.append("import torch;"); - pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was " - "installed WITHOUT CUDA');"); - this->ExecutePython(pythonCode, &PrintProcessEvent); - return true; - } - return false; -} - -QString QmitkSegmentAnythingToolInstaller::GetVirtualEnvPath() -{ - return STORAGE_DIR + VENV_NAME; -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.h index 2b9e140672..0ecad9672f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSegmentAnythingToolGUI.h @@ -1,197 +1,138 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkSegmentAnythingToolGUI_h #define QmitkSegmentAnythingToolGUI_h #include "QmitkSegWithPreviewToolGUIBase.h" #include #include "ui_QmitkSegmentAnythingGUIControls.h" #include "QmitknnUNetGPU.h" #include "QmitkSetupVirtualEnvUtil.h" #include #include #include #include - - -/** - * @brief Installer class for SegmentAnythingModel Tool. - * Class specifies the virtual environment name, install version, packages required to pip install - * and implements SetupVirtualEnv method. - * - */ -class QmitkSegmentAnythingToolInstaller : public QmitkSetupVirtualEnvUtil -{ -public: - const QString VENV_NAME = ".sam"; - const QString SAM_VERSION = "1.0"; //currently, unused - const std::vector PACKAGES = {QString("numpy"), - QString("opencv-python"), - QString("git+https://github.com/facebookresearch/segment-anything.git"), - QString("SimpleITK")}; - const QString STORAGE_DIR; - inline QmitkSegmentAnythingToolInstaller( - const QString baseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + - qApp->organizationName() + QDir::separator()) - : QmitkSetupVirtualEnvUtil(baseDir), STORAGE_DIR(baseDir){}; - bool SetupVirtualEnv(const QString &) override; - QString GetVirtualEnvPath() override; -}; - +#include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal -\brief GUI for mitk::PickingTool. +\brief GUI for mitk::SegmentAnythingTool. \sa mitk::PickingTool */ class MITKSEGMENTATIONUI_EXPORT QmitkSegmentAnythingToolGUI : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkSegmentAnythingToolGUI, QmitkSegWithPreviewToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); -protected slots: - /** - * @brief Qt Slot - */ - void OnResetPicksClicked(); - - /** - * @brief Qt Slot - */ - void OnActivateBtnClicked(); + inline static const QMap VALID_MODELS_URL_MAP = { + {"vit_b", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth")}, + //{"vit_b", QUrl("http://www.google.ru/images/srpr/logo3w.png")}, + {"vit_l", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_l_0b3195.pth")}, + {"vit_h", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth")}}; /** - * @brief Qt Slot + * @brief Checks if SegmentAnything is found inside the selected python virtual environment. + * @return bool */ - void OnInstallBtnClicked(); + static bool IsSAMInstalled(const QString &); +protected slots: /** * @brief Qt Slot */ - QString OnSystemPythonChanged(const QString&); + void OnResetPicksClicked(); /** * @brief Qt Slot */ - void OnClearInstall(); + void OnActivateBtnClicked(); /** * @brief Qt Slot */ void FileDownloaded(QNetworkReply *); /** * @brief Qt Slot */ - void OnParametersChanged(const QString &); + void OnParametersChanged(const QString&); protected: QmitkSegmentAnythingToolGUI(); ~QmitkSegmentAnythingToolGUI() = default; void InitializeUI(QBoxLayout *mainLayout) override; /** * @brief Writes any message in white on the tool pane. */ void WriteStatusMessage(const QString &); /** * @brief Writes any message in red on the tool pane. */ void WriteErrorMessage(const QString &); /** * @brief Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); /** * @brief Adds GPU information to the gpu combo box. * In case, there aren't any GPUs avaialble, the combo box will be * rendered editable. */ void SetGPUInfo(); - /** - * @brief Searches and parses paths of python virtual enviroments - * from predefined lookout locations - */ - void AutoParsePythonPaths(); - /** * @brief Returns GPU id of the selected GPU from the Combo box. * @return unsigned int */ unsigned int FetchSelectedGPUFromUI() const; - /** - * @brief Checks if TotalSegmentator command is valid in the selected python virtual environment. - * @return bool - */ - bool IsSAMInstalled(const QString &); - - /** - * @brief Get the Exact Python Path for any OS - * from the virtual environment path. - * @return QString - */ - QString GetExactPythonPath(const QString &) const; - /** * @brief Enable (or Disable) GUI elements. */ void EnableAll(bool); - - /** - * @brief Get the virtual env path from UI combobox removing any - * extra special characters. - * - * @return QString - */ - QString GetPythonPathFromUI(const QString &) const; /** * */ bool DownloadModel(const QString &); /** * */ void ShowProgressBar(bool); void ActivateSAMDaemon(); + bool ValidatePrefences(); + private: - QmitkSegmentAnythingToolInstaller m_Installer; + mitk::IPreferences *m_Prefences; QNetworkAccessManager m_Manager; Ui_QmitkSegmentAnythingGUIControls m_Controls; QString m_PythonPath; QmitkGPULoader m_GpuLoader; bool m_FirstPreviewComputation = true; - bool m_IsInstalled = false; const std::string WARNING_SAM_NOT_FOUND = - "SAM is not detected in the selected python environment.Please reinstall SAM."; - const QMap VALID_MODELS_URL_MAP = { - {"vit_b", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth")}, - //{"vit_b", QUrl("http://www.google.ru/images/srpr/logo3w.png")}, - {"vit_l", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_l_0b3195.pth")}, - {"vit_h", QUrl("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth")}}; + "SAM is not detected in the selected python environment. Please reinstall SAM."; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp index e1d8403795..9e7f6a193e 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp @@ -1,179 +1,419 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSegmentationPreferencePage.h" #include #include #include #include +#include +#include +#include #include +#include +#include +#include #include namespace { mitk::IPreferences* GetPreferences() { auto* preferencesService = mitk::CoreServices::GetPreferencesService(); return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); } } QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage() : m_Ui(new Ui::QmitkSegmentationPreferencePageControls), m_Control(nullptr), m_Initializing(false) { } QmitkSegmentationPreferencePage::~QmitkSegmentationPreferencePage() { } void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer) { } void QmitkSegmentationPreferencePage::CreateQtControl(QWidget* parent) { m_Initializing = true; m_Control = new QWidget(parent); m_Ui->setupUi(m_Control); - +#ifndef _WIN32 + m_Ui->sysPythonComboBox->addItem("/usr/bin"); +#endif + this->AutoParsePythonPaths(); + m_Ui->sysPythonComboBox->addItem("Select..."); + m_Ui->sysPythonComboBox->setCurrentIndex(0); connect(m_Ui->labelSetPresetToolButton, SIGNAL(clicked()), this, SLOT(OnLabelSetPresetButtonClicked())); connect(m_Ui->suggestionsToolButton, SIGNAL(clicked()), this, SLOT(OnSuggestionsButtonClicked())); connect(m_Ui->installSAMButton, SIGNAL(clicked()), this, SLOT(OnInstallBtnClicked())); connect(m_Ui->clearSAMButton, SIGNAL(clicked()), this, SLOT(OnClearInstall())); + connect(m_Ui->sysPythonComboBox, QOverload::of(&QComboBox::activated), + [=](int index) { OnSystemPythonChanged(m_Ui->sysPythonComboBox->itemText(index)); }); + QIcon deleteIcon = + QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/edit-delete.svg")); + m_Ui->clearSAMButton->setIcon(deleteIcon); + + const QString storageDir = m_Installer.GetVirtualEnvPath(); + m_IsInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(storageDir); + QString welcomeText; + if (m_IsInstalled) + { + m_PythonPath = GetExactPythonPath(storageDir); + m_Installer.SetVirtualEnvPath(m_PythonPath); + welcomeText += " SAM is already found installed."; + } + else + { + welcomeText += " SAM is not installed. Please click on \"Install SAM\" above."; + } + this->WriteStatusMessage(welcomeText); + + m_Ui->samModelTypeComboBox->addItems(QmitkSegmentAnythingToolGUI::VALID_MODELS_URL_MAP.keys()); this->Update(); m_Initializing = false; } QWidget* QmitkSegmentationPreferencePage::GetQtControl() const { return m_Control; } bool QmitkSegmentationPreferencePage::PerformOk() { auto* prefs = GetPreferences(); prefs->PutBool("compact view", m_Ui->compactViewCheckBox->isChecked()); prefs->PutBool("draw outline", m_Ui->outlineRadioButton->isChecked()); prefs->PutBool("selection mode", m_Ui->selectionModeCheckBox->isChecked()); prefs->Put("label set preset", m_Ui->labelSetPresetLineEdit->text().toStdString()); prefs->PutBool("default label naming", m_Ui->defaultNameRadioButton->isChecked()); prefs->Put("label suggestions", m_Ui->suggestionsLineEdit->text().toStdString()); prefs->PutBool("replace standard suggestions", m_Ui->replaceStandardSuggestionsCheckBox->isChecked()); prefs->PutBool("suggest once", m_Ui->suggestOnceCheckBox->isChecked()); + + prefs->Put("sam parent path", m_Installer.STORAGE_DIR.toStdString()); + prefs->Put("sam python path", m_PythonPath.toStdString()); + prefs->Put("sam modeltype", m_Ui->samModelTypeComboBox->currentText().toStdString()); return true; } void QmitkSegmentationPreferencePage::PerformCancel() { } void QmitkSegmentationPreferencePage::Update() { auto* prefs = GetPreferences(); m_Ui->compactViewCheckBox->setChecked(prefs->GetBool("compact view", false)); if (prefs->GetBool("draw outline", true)) { m_Ui->outlineRadioButton->setChecked(true); } else { m_Ui->overlayRadioButton->setChecked(true); } m_Ui->selectionModeCheckBox->setChecked(prefs->GetBool("selection mode", false)); auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), ""); bool isOverriddenByCmdLineArg = !labelSetPreset.empty(); if (!isOverriddenByCmdLineArg) labelSetPreset = prefs->Get("label set preset", ""); m_Ui->labelSetPresetLineEdit->setDisabled(isOverriddenByCmdLineArg); m_Ui->labelSetPresetToolButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->labelSetPresetCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg); m_Ui->labelSetPresetLineEdit->setText(QString::fromStdString(labelSetPreset)); if (prefs->GetBool("default label naming", true)) { m_Ui->defaultNameRadioButton->setChecked(true); } else { m_Ui->askForNameRadioButton->setChecked(true); } auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), ""); isOverriddenByCmdLineArg = !labelSuggestions.empty(); if (!isOverriddenByCmdLineArg) labelSuggestions = prefs->Get("label suggestions", ""); m_Ui->defaultNameRadioButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->askForNameRadioButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsLineEdit->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsToolButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg); m_Ui->suggestionsLineEdit->setText(QString::fromStdString(labelSuggestions)); m_Ui->replaceStandardSuggestionsCheckBox->setChecked(prefs->GetBool("replace standard suggestions", true)); m_Ui->suggestOnceCheckBox->setChecked(prefs->GetBool("suggest once", true)); } void QmitkSegmentationPreferencePage::OnLabelSetPresetButtonClicked() { const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Set Preset"), QString(), QStringLiteral("Label set preset (*.lsetp)")); if (!filename.isEmpty()) m_Ui->labelSetPresetLineEdit->setText(filename); } void QmitkSegmentationPreferencePage::OnSuggestionsButtonClicked() { const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Suggestions"), QString(), QStringLiteral("Label suggestions (*.json)")); if (!filename.isEmpty()) m_Ui->suggestionsLineEdit->setText(filename); } +QString QmitkSegmentationPreferencePage::OnSystemPythonChanged(const QString &pyEnv) +{ + QString pyPath; + if (pyEnv == QString("Select...")) + { + QString path = QFileDialog::getExistingDirectory(m_Ui->sysPythonComboBox->parentWidget(), "Python Path", "dir"); + if (!path.isEmpty()) + { + this->OnSystemPythonChanged(path); // recall same function for new path validation + bool oldState = m_Ui->sysPythonComboBox->blockSignals(true); // block signal firing while inserting item + m_Ui->sysPythonComboBox->insertItem(0, path); + m_Ui->sysPythonComboBox->setCurrentIndex(0); + m_Ui->sysPythonComboBox->blockSignals(oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration + } + } + else + { + QString uiPyPath = this->GetPythonPathFromUI(pyEnv); + pyPath = this->GetExactPythonPath(uiPyPath); + } + return pyPath; +} + +QString QmitkSegmentationPreferencePage::GetPythonPathFromUI(const QString &pyUI) const +{ + QString fullPath = pyUI; + if (-1 != fullPath.indexOf(")")) + { + fullPath = fullPath.mid(fullPath.indexOf(")") + 2); + } + return fullPath.simplified(); +} + +QString QmitkSegmentationPreferencePage::GetExactPythonPath(const QString &pyEnv) const +{ + QString fullPath = pyEnv; + bool isPythonExists = false; +#ifdef _WIN32 + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); + if (!isPythonExists && + !(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("Scripts"); + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); + } +#else + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); + if (!isPythonExists && + !(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("bin"); + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); + } +#endif + if (!isPythonExists) + { + fullPath.clear(); + } + return fullPath; +} + +void QmitkSegmentationPreferencePage::AutoParsePythonPaths() +{ + QString homeDir = QDir::homePath(); + std::vector searchDirs; +#ifdef _WIN32 + searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() + + QString("anaconda3")); +#else + // Add search locations for possible standard python paths here + searchDirs.push_back(homeDir + QDir::separator() + "anaconda3"); + searchDirs.push_back(homeDir + QDir::separator() + "miniconda3"); + searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3"); + searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3"); +#endif + for (QString searchDir : searchDirs) + { + if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive)) + { + if (QDir(searchDir).exists()) + { + m_Ui->sysPythonComboBox->addItem("(base): " + searchDir); + searchDir.append((QDir::separator() + QString("envs"))); + } + } + for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();) + { + subIt.next(); + QString envName = subIt.fileName(); + if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. + { + m_Ui->sysPythonComboBox->addItem("(" + envName + "): " + subIt.filePath()); + } + } + } +} + +/* bool QmitkSegmentationPreferencePage::IsSAMInstalled(const QString &pythonPath) +{ + QString fullPath = pythonPath; + bool isPythonExists = false; +#ifdef _WIN32 + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); + if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("Scripts"); + isPythonExists = + (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python.exe")) : isPythonExists; + } +#else + isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); + if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("bin"); + isPythonExists = + (!isPythonExists) ? QFile::exists(fullPath + QDir::separator() + QString("python3")) : isPythonExists; + } +#endif*/ +// bool isExists = /*QFile::exists(fullPath + QDir::separator() + QString("MITK_SAM"))*/ true && isPythonExists; +// return isExists; +//} void QmitkSegmentationPreferencePage::OnInstallBtnClicked() { MITK_INFO << "Install clicked"; + QString systemPython = OnSystemPythonChanged(m_Ui->sysPythonComboBox->currentText()); + if (!systemPython.isEmpty()) + { + this->WriteStatusMessage("STATUS: Installing SAM..."); + m_Installer.SetSystemPythonPath(systemPython); + m_IsInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME); + if (m_IsInstalled) + { + m_PythonPath = this->GetExactPythonPath(m_Installer.GetVirtualEnvPath()); + this->WriteStatusMessage("STATUS: Successfully installed SAM."); + auto *prefs = GetPreferences(); + prefs->Put("sam python path", m_PythonPath.toStdString()); + } + else + { + this->WriteErrorMessage("ERROR: Couldn't install SAM."); + } + } } -void QmitkSegmentationPreferencePage::OnClearInstall() +void QmitkSegmentationPreferencePage::OnClearInstall() { MITK_INFO << "OnClearInstall clicked"; + QDir folderPath(m_Installer.GetVirtualEnvPath()); + m_IsInstalled = folderPath.removeRecursively(); + if (!m_IsInstalled) + { + MITK_ERROR << "The virtual environment couldn't be removed. Please check if you have the required access " + "privileges or, some other process is accessing the folders."; + } +} + +void QmitkSegmentationPreferencePage::WriteStatusMessage(const QString &message) +{ + m_Ui->samInstallStatusLabel->setText(message); + m_Ui->samInstallStatusLabel->setStyleSheet("font-weight: bold; color: white"); + qApp->processEvents(); +} + +void QmitkSegmentationPreferencePage::WriteErrorMessage(const QString &message) +{ + m_Ui->samInstallStatusLabel->setText(message); + m_Ui->samInstallStatusLabel->setStyleSheet("font-weight: bold; color: red"); + qApp->processEvents(); } QString QmitkSAMInstaller::GetVirtualEnvPath() { - return QString("help"); + return STORAGE_DIR + VENV_NAME; } -bool QmitkSAMInstaller::SetupVirtualEnv(const QString &) +bool QmitkSAMInstaller::SetupVirtualEnv(const QString &venvName) { - return true; + if (GetSystemPythonPath().isEmpty()) + { + return false; + } + QDir folderPath(GetBaseDir()); + folderPath.mkdir(venvName); + if (!folderPath.cd(venvName)) + { + return false; // Check if directory creation was successful. + } + mitk::ProcessExecutor::ArgumentListType args; + auto spExec = mitk::ProcessExecutor::New(); + auto spCommand = itk::CStyleCommand::New(); + spCommand->SetCallback(&PrintProcessEvent); + spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); + + args.push_back("-m"); + args.push_back("venv"); + args.push_back(venvName.toStdString()); +#ifdef _WIN32 + QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python.exe"; + QString pythonExeFolder = "Scripts"; +#else + QString pythonFile = GetSystemPythonPath() + QDir::separator() + "python3"; + QString pythonExeFolder = "bin"; +#endif + spExec->Execute(GetBaseDir().toStdString(), pythonFile.toStdString(), args); // Setup local virtual environment + if (folderPath.cd(pythonExeFolder)) + { + this->SetPythonPath(folderPath.absolutePath()); + this->SetPipPath(folderPath.absolutePath()); + this->InstallPytorch(); + for (auto &package : PACKAGES) + { + this->PipInstall(package.toStdString(), &PrintProcessEvent); + } + std::string pythonCode; // python syntax to check if torch is installed with CUDA. + pythonCode.append("import torch;"); + pythonCode.append("print('Pytorch was installed with CUDA') if torch.cuda.is_available() else print('PyTorch was " + "installed WITHOUT CUDA');"); + this->ExecutePython(pythonCode, &PrintProcessEvent); + return true; + } + return false; } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h index 099d455bbc..a4af736897 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h @@ -1,87 +1,114 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkSegmentationPreferencePage_h #define QmitkSegmentationPreferencePage_h #include "org_mitk_gui_qt_segmentation_Export.h" #include #include #include +#include +#include class QWidget; namespace Ui { class QmitkSegmentationPreferencePageControls; } class QmitkSAMInstaller : public QmitkSetupVirtualEnvUtil { public: const QString VENV_NAME = ".sam"; const QString SAM_VERSION = "1.0"; // currently, unused const std::vector PACKAGES = {QString("numpy"), QString("opencv-python"), QString("git+https://github.com/facebookresearch/segment-anything.git"), QString("SimpleITK")}; const QString STORAGE_DIR; inline QmitkSAMInstaller( const QString baseDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + qApp->organizationName() + QDir::separator()) : QmitkSetupVirtualEnvUtil(baseDir), STORAGE_DIR(baseDir){}; bool SetupVirtualEnv(const QString &) override; QString GetVirtualEnvPath() override; }; class MITK_QT_SEGMENTATION QmitkSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage { Q_OBJECT Q_INTERFACES(berry::IPreferencePage) public: QmitkSegmentationPreferencePage(); ~QmitkSegmentationPreferencePage() override; void Init(berry::IWorkbench::Pointer workbench) override; void CreateQtControl(QWidget* widget) override; QWidget* GetQtControl() const override; bool PerformOk() override; void PerformCancel() override; void Update() override; protected Q_SLOTS: void OnLabelSetPresetButtonClicked(); void OnSuggestionsButtonClicked(); void OnInstallBtnClicked(); void OnClearInstall(); + QString OnSystemPythonChanged(const QString&); protected: - + /** + * @brief Searches and parses paths of python virtual enviroments + * from predefined lookout locations + */ + void AutoParsePythonPaths(); + + /** + * @brief Get the virtual env path from UI combobox removing any + * extra special characters. + * + * @return QString + */ + QString GetPythonPathFromUI(const QString&) const; + + /** + * @brief Get the Exact Python Path for any OS + * from the virtual environment path. + * @return QString + */ + QString GetExactPythonPath(const QString&) const; + void WriteStatusMessage(const QString&); + void WriteErrorMessage(const QString&); + + QNetworkAccessManager m_Manager; Ui::QmitkSegmentationPreferencePageControls* m_Ui; QmitkSAMInstaller m_Installer; QWidget* m_Control; - + QString m_PythonPath; + bool m_IsInstalled = false; bool m_Initializing; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui index febf113317..4e94cc1b70 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui @@ -1,282 +1,302 @@ QmitkSegmentationPreferencePageControls 0 0 656 779 Form Compact view Hide tool button texts and increase icon size 2D display Draw as outline true displayButtonGroup Draw as transparent overlay displayButtonGroup Data node selection mode If checked the segmentation plugin ensures that only the selected segmentation and the reference image are visible at one time. Show only selected nodes Default label set preset true ... <html><head/><body><p><span style=" color:#ff0000;">The default label set preset is currently overriden by the </span><span style=" font-family:'Courier New'; color:#ff0000;">Segmentation.labelSetPreset</span><span style=" color:#ff0000;"> command-line argument.</span></p></body></html> Qt::RichText true Label creation Assign default name and color true labelCreationButtonGroup Ask for name and color labelCreationButtonGroup Label suggestions true ... <html><head/><body><p><span style=" color:#ff0000;">Suggestions are currently enforced by the </span><span style=" font-family:'Courier New'; color:#ff0000;">Segmentation.labelSuggestions</span><span style=" color:#ff0000;"> command-line argument.</span></p></body></html> Qt::RichText true Replace standard organ suggestions true Suggest once per segmentation true SAM Tool 0 0 Install SAM 0 0 Clear Install System Python + + + + Model Type: + + + + + + + + + + Qt::RichText + + + true + + + Qt::Vertical 20 40